数据库系统体系结构:第2章进程模型

JosephM.Hellerstein,MichaelStonebrakerandJamesHamilton

第2章进程模型

翻译:林子雨

厦门大学数据库实验室

前言

本文一共包括6章,分别是:第1章概述,第2章进程模型,第3章并行体系结构:进程和内存协调,第4章关系查询处理器,第5章存储管理,第6章事务:并发控制和恢复,第7章共享组件,第8章结束语。

本文翻译由厦门大学数据库实验室林子雨老师团队合力完成,其中,林子雨老师负责统稿校对,刘颖杰同学负责翻译第1章和第2章,罗道文同学负责翻译第3章和第4章,谢荣东同学负责翻译第5章、第6章、第7章和第8章。

如果对本文翻译内容有任何疑问,欢迎联系林子雨老师。

林子雨于厦门大学海韵园

2013年9月

摘要

备注:

(1)可以到页面底部,下载本章节PDF文件;

在设计多用户服务器时,必须要尽早决定并发用户请求的执行以及它们是如何被映射到操作系统的进程和线程的。这个决定将对之后的系统软件架构以及运行效率、稳定性、轻便性产生深远影响。在这一章,我们测试一些DBMS的运行数据,它们也可以作为其他高并发系统的参考模版。我们考虑一个简化的框架,假设系统支持线程并且仅考虑单片机的情况。然后我们进一步阐述现代DBMS在简化的框架下是如何实现它们的进程模型的。在第三章,我们将介绍机群技术以及多进程系统和多核系统。

我们接下来的讨论基于以下定义:

在这一部分,我们介绍一个简单的DBMS进程模型的分类方法。尽管先进的DBMS很少完全按照我们将介绍的内容来构建,但是在这个基础上,我们可以更详细地讨论新一代产品。现今的每一款数据库系统的核心都是下列所述模型的扩展或者增强。

我们首先做两个简单的假设(在后文的讨论中我们将放宽条件):

通过上下文我们了解到,DBMS拥有三个天然的进程模型性质。从最简单到最复杂依次为:(1)每个DBMS工作者拥有一个进程;(2)每个DBMS工作者拥有一个线程;(3)进程池。尽管该模型被简化了,但是,今天的商业DBMS系统都遵循这三条性质。

图2-1每个DBMS工作者拥有一个进程的模型

如果把“扩展到大量并发连接”作为衡量标准,那么,每个DBMS工作者拥有一个进程的模型并不十分有效。这是因为,进程相对于线程而言,拥有更多的环境变量并且消耗更多的存储空间。进程切换需要切换安全的上下文环境、存储空间变量、文件和网络句柄列表以及其他一些进程上下文。这在线程切换时是不需要的。尽管如此,每个DBMS工作者拥有一个进程的模型,还是比较受欢迎的,并得到IBMDB2、PostgreSQL和Oracle的支持。

在每个DBMS工作者拥有一个线程的模型(图2.2)中,一个多线程进程负责所有的DBMS工作者的工作。一个调度线程监听新的DBMS客户端连接。每个连接都被分配一个新的线程。当每个客户端提交SQL请求时,该请求都由对应的DBMS工作者线程来执行。这个线程在DBMS进程中运行,一旦运行结束,结果将返回给客户端,然后该线程等待来着这个客户端的下一个请求。

多线程编程在这种架构下有以下困难:操作系统对线程不提供溢出和指针的保护;调试困难,尤其是在运行情况下更是如此;由于不同操作系统在线程接口和多线程扩展性方面的不同,使得软件可移植性较差。“每个DBMS工作者拥有一个线程模型”中的很多编程问题,同样存在于每个DBMS工作者拥有一个进程模型中,因为它们都需要共享内存的使用。

尽管不同操作系统在线程API的多样性方面,近年来不断缩小,但不同平台之间微小的差别还是不断带来调试上的麻烦。如果不考虑实现上的困难,那么,每个DBMS工作者拥有一个线程的模型,还是可以很好地扩展到高并发系统中的,并且这种模型在一些现代DBMS产品中也被使用了,如IBMDB2、微软SQLServer、MySql、Informix和Sybase。

图2-2每个DBMS工作者拥有一个线程的模型

这个模型是“每个DBMS工作者一个进程”这种模型的变体。我们知道,每个DBMS工作者拥有一个进程的模型的优点是编程实现较为简单,但是,每一个连接都需要一个进程却是一个缺点。使用进程池(如图2-3所示)后,不必每个DBMS工作者都分配一个进程,而是由进程池来管理所有DBMS工作者。一个中央进程控制所有的DBMS客户端连接,每个从客户端到来的SQL请求将被分配一个进程池中的进程。SQL请求处理完之后,结果返回客户端,进程回到缓冲池中准备分配给下一个请求。缓冲池的大小是一定的,且通常不可变。如果一个请求到来而没有进程空闲,那么新的请求必须等待进程。

图2-3进程池

进程池拥有每个DBMS工作者一个进程模型的所有优点,而且,由于只需要少量的进程,所以,其内存使用效率也很高。进程池一般被设计成大小可动态变化的,以此应对大量的高并发请求。当请求负载较低时,进程池可以缩减为较少的等待进程。跟每个DBMS工作者一个进程一样,很多现代DBMS产品也支持进程池。

上述的每一个模型都试图尽可能地独立处理并发客户端请求。但是,DBMS工作者完全地独立工作是不可能的,因为它们处理的是共享的数据库。对于每个DBMS工作者拥有一个线程的模型,数据共享较为简单,因为,所有线程共享内存空间。在其它模型中,共享内存被用来存放共享数据结构。在所有三个模型中,数据必须从DBMS传送给客户端。这使得所有的SQL请求都必须传给服务器来处理,然后得到的结果需要返回给客户端。要实现这个功能,简单地说,就是使用不同的缓冲区。两种主要的缓冲区是磁盘I/O缓冲区和客户端通信缓冲区。我们简要地介绍一下它们以及它们的管理方式。

磁盘I/O缓冲区:最常见的工作者之间的数据依赖是读写共享的数据。所以,工作者之间的I/O中断是必要的。有两种不同的I/O中断需要分别考虑:(1)数据库请求;(2)日志请求。

对于每个DBMS工作者拥有一个线程的模型,日志尾部只是一个堆数据结构。在另外两种模型中,它们的设计方式比较类似。一种方法是一个独立的进程负责管理日志。日志记录通过共享内存或者其他有效的通信协议与日志管理器进行交互。另一种方式是,日志尾部像上文中提到的缓冲池那样分配给共享内存。关键的一点是,所有的线程或者进程在处理客户端请求时,需要写日志记录并将日志尾部刷新至磁盘。

客户端通信缓冲区:SQL通常被用于“拉”(pull)模型,也就是说,客户端通过重复发送SQLFETCH请求,不断地获取结果元组,SQLFETCH每个请求会获取一个或多个元组。大多数DBMS尽可能地在FETCH流到来之前做一些数据预提取工作,以此确保在客户端请求之前将结果存入队列。

为支持预提取功能,DBMS工作者使用客户端通信套接字作为元组队列。将来更复杂的方法是实现客户端游标缓存功能,并使用DBMS客户端存储近期即将被访问的结果,而不是依赖于操作系统通信缓冲区。

锁表:锁表由所有的DBMS工作者共享,由锁管理器实现数据库锁机制。锁共享技术类似于缓冲池共享,并可以用类似的方法实现DBMS开发需要的其他数据结构共享。

前文中我们简单描述了DBMS进程模型。我们假设系统线程有良好的的性能,DBMS着眼于单处理机系统。在本节剩余的篇幅中,我们放宽第一个假设,然后来看看它对DBMS实现的影响。接下来讨论多进程和并行化操作。

当前的大多数DBMS技术,都离不开19世纪70年代开始的系统研究和19世纪80年代开始的商业化发展。在数据库开发的早期阶段,标准操作系统的技术无法用到数据库开发中。高效的操作系统线程支持就是其中一种技术。直到19世纪90年代,操作系统线程的出现和广泛应用,才使得数据库开发发生了很大的变化。即便是今天,操作系统线程开发都没有很好地支持DBMS的工作负载[31,48,93,94]。

受历史发展的影响以及其他一些原因,很多广泛应用的DBMS在开发上并不依赖于操作系统线程。一些完全不涉及线程,而是使用每个DBMS工作者拥有一个进程或者进程池模型。其他的DBMS,比如一个DBMS工作者拥有一个线程的模型,需要一个方案来应对那些没有好的内核线程的系统。一些技术领先的DBMS解决该问题的方式是,开发了自己的高效轻量级的线程包。这些轻量级线程或者说DBMS线程,取代了上文提到的系统线程。每个DBMS线程都管理自己的变量,通过非中断的异步接口来编写中断操作,并通过调度器来调度任务。

在现今主要的DBMS中,我们可以看到2.1节中介绍的所有三种架构以及它们的一些有趣的变化。IBMDB2是支持四种进程模型的很有趣的例子。对于线程支持良好的操作系统,DB2默认的模型是每个DBMS工作者拥有一个线程,它也支持DBMS工作者多路复用一个线程池。当运行在没有线程支持的系统上时,DB2默认的模型是每个DBMS工作者拥有一个进程,同时也支持DBMS工作者多路复用一个进程池。

我们对IBMDB2、MySql、Oracle、PostgreSQL和MicrosoftSQLServer所支持的进程模型做一个总结:

每个DBMS工作者拥有一个进程:

这是最直接的进程模型,同时也得到了广泛的应用。DB2在不支持线程的系统上,默认使用每个DBMS工作者拥有一个进程的模型,在支持线程的系统上,默认使用每个DBMS工作者拥有一个线程的模型。这也是Oracle进程模型的默认设计。Oracle也支持上文提到的进程池。PostgreSQL在所有的操作系统上都只运行每个DBMS工作者拥有一个进程模型。

每个DBMS工作者拥有一个线程:

该模型有两个种类,在目前来看十分高效:

1通过系统进程来调度DBMS线程:轻量级线程的调度是由一个或多个系统进程来完成的。Sybase和Informix支持该模型。现今很多系统采用这个模型来开发实现DBMS线程的调度,以此调度DBMS工作者发挥多处理器的作用。然而,并不是所有采用这个模型的系统都实现了线程迁移:将DBMS线程再分配给不同的系统进程(如考虑到负载均衡)。

2通过系统线程来调度系统进程:微软SQLSever以可选择的方式支持这种模型(默认的模型为DBMS工作者多路复用线程池)。SQLSever中这个选项叫做Fibers,被用做应对高频事务处理,但是很少被使用。

进程/线程池:

在这个模型中,DBMS工作者共用一个进程池。随着系统线程支持性能的不断提高,依赖线程池而不是进程池的新的变种开始出现。在后面这种变种模型中,DBMS工作者复用系统的一个系统线程池:

我们将在下一章中谈到,现今大多数商业数据库支持内部查询并行化:并行多线程执行一条查询语句或者该语句的一部分。这一节中我们之所以提到这一点,是因为并行化内部查询,使得多个DBMS工作者暂时分配给一条SQL请求。除了一个客户端连接将拥有多个DBMS工作者来处理其请求之外,之前提到的进程模型与该情况并无冲突。

随着多用户系统负载不断升高,吞吐量将达到上限。在这一点上,当系统挂起时应当减少吞吐量。对于操作系统来说,“挂起”经常是由内存问题造成的:DBMS无法保证在缓冲池中存放数据库页数据的工作空间,导致出现不断的页替换情况。在数据库系统中,一些特殊的操作比如排序和哈希连接往往会造成大量的内存消耗。有时候,系统挂起也会发生在锁竞争上:事务处理发生死锁需要回滚并重启。因此,任何好的多用户系统都有准入控制机制,在系统没有充足资源的情况下,新的任务不被接受。如果拥有一个好的准入控制器,系统将在过载情况下发生比较优雅的性能衰退:事务延迟将随着到达率的增加而适当增加,但吞吐量一直保持在峰值。

DBMS的准入控制可以在两个层面上来实现:

第一个层面是,一个简单的准入控制可以通过进程来确保客户端连接数处于一个临界值之下。这就避免了网络连接数这类基础资源的过度消耗。在一些DBMS中,这种控制并没有被提供,我们可以假设该功能被系统的其他部分实现了,如应用层、事务处理层或者网络服务层。

第二个层面是,直接在DBMS内核关系查询处理器上实现。准入控制器在查询语句转换和优化完成后执行这一步操作,由该操作来决定,是否要推迟执行一个查询,是否要使用更少的资源来执行查询,以及是否需要额外的限制条件来执行查询。准入控制器依靠查询优化器的信息来执行,如查询所需的资源以及系统并发资源等信息。特殊情况下,优化器的查询计划可以:(1)确定查询所需要的磁盘设备以及对磁盘的I/O请求数;(2)根据查询中的操作以及要求的元组数目判断CPU负载;(3)评估查询数据结构的内存使用情况,包括在连接和其他操作期间的排序和哈希所消耗的内存。正如之前描述的那样,上面的第3点对于准入控制而言是最为关键的,因为,内存压力通常是引起“抖动”的主要原因。因此,许多DBMS把内存使用情况和活跃的DBMS工作者的数量作为主要的准入控制标准。

进程模型的选择对DBMS的表现有很大的影响。因此,商业数据库往往支持多个进程模型。从工程师的角度来看,在所有操作系统以及各种负载级别上使用单进程,很显然会更加简单明了。但是,由于操作系统和应用场合的多样性,三种DBMS都选择了支持多种模型。

展望未来,由于硬件瓶颈的变化以及网络的规模和多样性,近年来很多人致力于为服务器系统研究新的进程模型[31,48,93,94].在这些设计中,有一种想法是把一个服务器系统分解成一系列独立调度的引擎,并且在这些引擎之间进行异步和批量的消息传输。这有一些类似于上文中提到的进程池模型,即在多个请求之间重用工作者单元。这类最新研究的主要创新是,用一种更加小范围的、面向特定任务的方式来执行一些功能。这使得DBMS工作者与请求的关系变为多对多的,即一个查询的工作由多个DBMS工作者来完成,一个DBMS工作者负责多个查询的同样任务。这种结构带来了更为灵活的调度方式,比如,它允许一个工作者完成许多查询的任务(可能提高系统的整体吞吐量),或者允许一个查询由多个工作者共同完成(可能减少查询操作的延时)。在一些例子中,它在处理器局部性方面显得更有优势,在硬件未命中的情况下,能更好地防止CPU空闲。更多关于这一点的研究可以参考StagedDB的研究项目[35],这也是很好的阅读材料。

THE END
1.003Linux内核什么是内核空间,用户空间?注意:用户空间和内核空间之间的切换涉及到一定的开销,因此需要尽量减少切换的次数,提高系统的性能。 Linux中内核空间、用户空间的区别? 内存访问权限不同:内核空间的内存访问权限比用户空间更高,因为内核需要访问整个系统的物理资源,例如设备驱动、中断处理程序等。 https://blog.csdn.net/qq_41709234/article/details/131060489
2.内核空间和用户空间之间的差异腾讯云开发者社区内核空间和用户空间是操作系统中的两个重要概念,用于区分操作系统内核和用户程序的运行环境。它们之间的差异主要体现在以下几个方面: 1. 定义:内核空间是操作系统内核运行的环境,包含操作系统的核心功能和https://cloud.tencent.com/developer/information/%E5%86%85%E6%A0%B8%E7%A9%BA%E9%97%B4%E5%92%8C%E7%94%A8%E6%88%B7%E7%A9%BA%E9%97%B4%E4%B9%8B%E9%97%B4%E7%9A%84%E5%B7%AE%E5%BC%82-article
3.Linux为什么区分内核空间和用户空间?Linux操作系统通过区分内核空间和用户空间的这种设计,将操作系统代码和用户程序代码分开,这样即使在某一个应用程序出错,也不会影响到操作系统,再说,Linux操作系统是多任务系统,其它应用程序不也还能运行。 现代操作系统基本上都是分内核空间和用户空间的做法,来保护操作系统自身的安全性和稳定性,这也是区分内核空间和用户https://www.51cto.com/article/701867.html
4.Linux内核空间与用户空间有什么不同问答Linux内核空间和用户空间是操作系统中的两个不同的运行环境。1. 权限:Linux内核空间具有更高的权限,可以直接访问硬件资源和操作系统的核心功能,如内存管理、进程调度、设备驱动等。用户空间则受到限制,不能直接访问这些核心功能,只能通过系统调用来请求内核执行特定的操作。2. 内存访问:内核空间可以直接访问整个系统的https://www.yisu.com/ask/5945656.html
5.Linux中内核空间与用户空间的区别在哪?Linux运维培训Linux作为近几年来十分热门的操作系统,被广大开发者所使用的,那在Linux后期的学习中,我们会接触到内核空间和用户空间,两者乍看都是空间,那究竟区别在哪?或者说我们什么情况该使用哪种更为合适呢?接下来老男孩教育通过这篇文章为大家介绍一下。https://www.oldboyedu.com/blog/3012.html
6.内核对象命名空间“Session\”前缀保留供系统使用,不应在内核对象的名称中使用它。 通过使用会话实现快速用户切换。 第一个登录用户使用会话 1,下一个登录用户使用会话 2 等。 内核对象名称必须遵循为远程桌面服务列出的准则,以便应用程序可以支持多个用户。 从会话零以外的会话创建全局命名空间中的文件映射对象或符号链接对象(例如使用https://msdn.microsoft.com/en-us/library/aa382954
7.硬核操作系统讲解内核空间:操作系统内核访问的区域,独立于普通的应用程序,是受保护的内存空间。内核态下CPU可执行任何指令,可自由访问任何有效地址。 用户空间:普通应用程序可访问的内存区域。被执行代码会受到CPU众多限制,进程只能访问映射其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址。 那为啥要搞俩空间呢?现在嵌入式环http://baijiahao.baidu.com/s?id=1705058832615063960&wfr=spider&for=pc
8.[转]Linux内核使用浮点问题出现这种问题的原因是:内核由于性能原因,在内核运行的代码,内核在进行上下文切换时,不会主动保存和恢复浮点寄存器。这样可能会导致内核在进行浮点运算时,可能会破坏此时用户空间的浮点寄存器状态,导致用户空间的fpsimd_state状态异常,随后程序的行为将变的不可控。https://www.jianshu.com/p/c1b29f9dea97
9.Linux系统中,为何需要区分内核空间与用户空间?为什么黄章的魅族和雷军的小米走向了两个完全不同的结局? 195 万热度 4 深圳市实施赴港旅游「一签行」政策,珠海市实施赴澳旅游「一周一行」政策,赴港澳旅游新政释放了什么信号? 117 万热度 查看更多 ? ? AI 总结 Linux 系统中,为何需要区分内核空间与用户空间? 已引用 8 位答主的内容 查看AI 回答 赞同https://www.zhihu.com/question/553731336/answer/2675160636?utm_id=0