数据库通用基础实现技术---多进程共享信息

Berkeley DB数据库以程序库的方式链接到应用程序中,而应用程序可以以多线程、多进程的方式访问同一个数据库环境中的所有数据库。注意,这里的数据库环境(Database Environment, DB_ENV)是Berkeley DB的一个术语,相当于关系数据库中的一个“数据库”,我们知道这个“数据库”中有很多个“关系表”。而Berkeley DB当中用“数据库”("Database")来指代大家所熟悉的“关系表”---记住这个术语的改变,否则后面就很难理解了。

在通常的关系数据库服务器中,通常也是有很多个进程在向客户连接提供数据,所以本文所描述的技术适用于所有的数据库系统。

由于数据库系统当中的数据最终都是保存在磁盘的文件中的,而且为了高效地读写,这些文件都有比较复杂的结构,首先它们都被分成很多个页,每个页有相同的复杂结构,比如
每个页上有很多字段记录本页有多少条记录、每个记录的各个字段的起始地址、长度,等等,除此之外还有很多用户数据。向数据库文件中读写数据时,都需要访问页中的很多字段;另外,必须保证多个进程对这些页的读写访问符合规定的隔离级别,比如不可以读取未提交数据(可选)、多个进程不可以同时写同一个页等等。

为了实现上述需求,通常使用两种方法:

1. 映射文件

现在的操作系统都支持把一个文件映射到一个进程的内存空间中,当进程A把文件F映射到自己的内存空间后,在这个进程中,就得到一个指针(地址)B,你可以把这个地址当作文件第一个字节的内存地址,同时你可以认为这个文件后续的字节的地址就是在B的基础上加上文件内的偏移值,所以文件中第二个字节在内存中的地址是B + 1, 最后一个字节在内存中的地址是B + 文件长度 - 1. 多个进程可以同时把同一个文件映射到自己的内存空间,并且它们可以同时对这个文件修改,任何一个进程的改动可以立刻被其他进程看到(有些操作系统,比如 windows,对映射文件的共享一致性提供多种选项,除了这种完全一致性外,可以有更松弛的级别)。由于映射文件的这些特性,我们可以使用映射文件把同一个数据库文件映射到各个需要访问这个数据库文件的进程中,从而并发地处理用户的读、写数据请求。

需要注意的有三点:
第一,各个进程把同一个文件映射到自己的地址空间后,所得到的指针值未必相同,而且多次执行同一个程序,得到的指针值也不同。那么,我们就不可以在这样的数据库文件中使用绝对地址,只能保存文件内偏移值(offset),然后在映射到某个进程的地址空间之后,当我们需要访问某个地址值时候,都是要做基地址+偏移值的运算,得到目标内存地址;比如基地址是B, 本页第一条记录起始偏移值是O, 那么映射后第一条记录在内存中的地址就是B + O. 如果在页内偏移值为D的地方要保存某个字段的起始地址,而我们只知道该字段目前在本进程中的地址是P,我们应该这样做: (*(offset_t *)(B + D)) = P - B; 也就是说,要保存地址到数据库文件,也是用目标地址-本页基地址得到目标的偏移值,写到页内的特定位置。

第二,当一个页映射到多个进程后,由于多个进程可以不受任何限制同时访问这个页,我们需要一种互斥机制确保读写一致性,或者叫做数据库事务的隔离级别。通常的数据库系统使用锁定技术,也有其他方法,比如基于时间戳、多版本的机制,以及乐观的并发控制。Berkeley DB中主要使用锁定技术,每个页是一个可以被锁定的对象(lock object),而事务、游标句柄等就是锁定者(locker),锁系统通过操作系统提供的互斥原语确保被锁定lock object只被得到了其lock的locker访问,其他locker只能等到这些lock被释放后才能得到lock来访问这个lock object. 关于锁系统的更多细节,我们以后再做讨论。

第三,这种多进程共享信息的技术,不仅仅限于共享数据库文件,在数据库系统自身运行过程中使用的信息,也可以使用这样的映射技术。这时,每个文件内存储的是数据库自身的信息,比如,锁系统中需要记录有哪些locker在等待哪些lock,以便这些lock被现属的locker释放后,可以通知到等待的locker;同时需要记录哪些locker目前持有哪些lock,锁着的是哪些lock object等等,这些信息不是用户数据,而是数据库自身运行过程中需要的信息,它们必须被本数据库系统的所有进程同步地访问到,所以它们也可以被存储在文件中,在数据库运行期间映射到每个数据库进程中,在一定的互斥机制下,各个进程可以共享这些信息。这时,还需要对这个映射文件的空间进行管理,包括分配、释放、合并空间等等,类似c/c++的内存空间管理函数。在通常的应用程序中,我们调用 malloc/realloc/free或者new/delete 来分配、释放空间,这时c/c++的这些函数来管理我们的堆空间,这个堆空间是操作系统分配给进程的。针对特定数据库的数据结构及访问方式的特征,可以对空间管理算法做各种优化,使得分配、释放速度快、内碎块少等优点。

 

2. 系统内存

除了用映射文件来多进程共享信息外,还可以使用系统内存---它与文件、信号量等一样,也是一种由操作系统管理的内核对象。在具有虚拟内存管理的操作系统中,操作系统自身预留一部分虚拟地址空间以及物理内存供自身使用,所有进程都可以申请创建或者打开一个系统内存,得到一个本进程内使用的地址(指针值),然后,我们就可以像操纵本进程的空间一样,访问这个系统内存空间,同时与其他进程共享访问这个空间。在另外一些没有虚存管理的操作系统中(比如 vxworks, dos等),所有进程在同一个地址空间中运行,这时访问系统内存更快更直接,而且每个进程/任务/线程(各种这类系统中术语有所不同)得到的指针值都是相同的。 除此之外,所有其他方面,系统内存和映射文件在数据库技术方面是一样的。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值