Berkeley DB持久化和高速缓存 - GK.ZHONG

 

Hello World!

 

 

gkzhong at gmail

 



 

Berkeley DB之所以能实现如此高的性能,和它的持久化特点及高速缓存有莫大的关系,本文将偏向这方面进行解释,而不会或者很少提及到事务,复制等方面的问题。

任何的持久化数据库系统最终的数据存储都是文件,那么BDB也是一样。一般的RMDB,都是基于C/S的模型,这意味着你永远无法接触到最底层的数据是如何持久化的。当然,你也不会需要。而BDB做为一个嵌入式数据库,它的运行模式和C/S有着本质的不同,或者你可以认为BDB是提供了一套API去访问一个数据库文件。

那么这里就会问题了,既然对文件进行操作,那么这个文件可否由多个线程?进程?去访问?答案是肯定可以的,那么如何来实现让这些进程之间对数据库文件访问符合规定的事务隔离级别?

BDB以什么样的并发控制或者说互斥机制来实现事务隔离级别?而同时又可以保持如此高的性能?它会是像你熟悉的RMDB一样锁表么?(或者另外的其它什么称谓)

BDB每秒要完成上百万甚至更多的访问,肯定是不可能发生上百万次磁盘IO的,很自然这里肯定会用缓存来实现,那么它是如何实现的?

这篇文章将带着这些问题进行展开。

如果你已经具备文件系统,内存管理,以及一定的数据结构知识,那么你可能更容易理解各部分内容,为方便理解涉及部分我也会做简单的描述。

BDB对进程间的信息共享采用内存映射,系统内存方式加以实现。

我们知道,每一个用户态进程创建时内核会相应地分配一个地址空间,而内存映射正是将文件映射到进程的地址空间的一个线性区域。对文件某位置内容的访问转换成基址加偏移量的访问。

多进程之间要去获取相应的共享信息:如高速缓冲区,互斥量等相关信息,那么这些信息必须置于一个所有进程可见的公开的位置,而它即是BDB的共享区域(regions)。
默认情况下,它将以文件方法被创建于BDB环境目录下(如:__db.001系列文件),最终也将以内存映射方式,调入操作系统高速缓冲区,或换出至页面文件。
同时BDB还可以通过设置DB_SYSTEM_MEM标识将共享区域在系统内存中创建,当然这仍然还需要创建一个文件来供所有进程访问。
最后BDB还可以通过设置DB_PRIVATE标识将将共享区域置于进程堆空间创建,显然这对于多进程访问是不合适的。

这些共享区域主要包含以下信息:
1,针对共享区域本身的描述,包括共享区域环境和指定哪些共享区域。如果以文件系统的方式创建,那么它就是__db.001这个文件,他也是这些区域文件的入口。
结构如下图:
     +------------+
     |   REGENV   |
     +------------+   +-------------+
     |   REGION   |-> |  __db.002   |
     |            |   +-------------+
     +------------+   +-------------+
     |   REGION   |-> |  __db.003   |
     |            |   +-------------+
     +------------+   +-------------+
     |   REGION   |-> |  __db.004   |
     |            |   +----------- -+
     +------------+
2,针对高速缓冲区的描述(MPOOL)
3,针对互斥量的描述(MUTEX)
4,针对锁的描述(LOCK)
5,针对日志的描述(LOG)
6,针对事务的描述(TXN)

至此,任何一个线程已经拥有足够的信息来完成数据的操作了。

BDB的高速缓冲区,这是BDB创建的一块共享内存区域,是一个可选项。如果选中的话,任何进程可以通过前面所说的“BDB共享区域”内找到内存中该区域的所在位置。对任何数据的读写,都是发生在高速缓冲区。BDB的最小存取单元称之为页(你可以想像为操作系统对内存管理的页,但并不指那个,后面还会有详细介绍)。

用户每一次的数据请求,都会向高速缓冲区请求,如果该页不在高速缓冲区,那么从磁盘中读该页,新页将追加到高速缓冲区,直至高速缓冲区满,将采用最近最久未使用算法将最近最久未使用的页调出高速缓冲区。否则这些页将一直保存。
同样,用户每一次数据修改,都先将检查该页是否在高速缓冲区,如果不在将从磁盘调入。然后在高速缓冲区中对该页进行修改,当然BDB并不会立即将数据刷回磁盘,就像操作系统的内核执行延迟写操作。

因此高速缓冲是BDB性能的一个关键性因素,因此合理地选择缓冲区大尤为重要,它甚至可以达到对数据访问完全不需要IO的程度。
对高速缓冲区大小设置的一些建议:
1,高速缓冲区大小不应该超出系统承载能力,即是漫无边际地加大。这样会造成操作系统频繁的页交换,反而降低了性能。
2,在满足上面条件下尽量加大高速缓冲区大小,直至一个最高点:达到这个点以后所有数据的请求都不需要进行磁盘IO,或者随机访问性高到无法预测下一次缓存命中时间。
3,针对我们的生产应用至少应该把元数据放入高速缓冲区,元数据:即相对于用户数据而言,对数据结构本身的描述信息。采用不同的存储结构,它们也不一样,而BDB的B树是最“浪费”的一种数据结构,它的元数据量是最大的(后面还会详细介绍)。但采用B树存储的话再省也得把一次访问途经的所有结点(包括树根页,经过的内部页,叶子页)给装载进高速缓冲区,否则一定会造成频繁的页交换。

高速缓冲区和底层数据库文件交互仍然采用内存映射的方式进行,当然这也是可选的。BDB可配置最大映射空间尺寸,默认情况下,会映射最大10M的文件内容。因此,这个大小的选择也会影响到BDB的访问性能,因此对它的设置针对数据库文件,物理内存,虚拟内存,进程地址空间进行综合考虑。正常情况下,操作系统会映射进物理内存空间,继而是虚拟内存。而当数据库文件非常大的时间,必须注意进程的地址空间(一个32位的操作系统只能分配4G)。

页(page),是BDB对数据操作的最小单元。不同的数据存储结构对页的定义会稍有不同,但都是在页头若干个字节标识该页的相关属性。下面是一个B树的页结构:
    +-----------------------------------+
    |    lsn    |   pgno    | prev pgno |
    +-----------------------------------+
    | next pgno |  entries  | hf offset |
    +-----------------------------------+
    |   level   |   type    |   chksum  |
    +-----------------------------------+
    |    iv     |   index   | free -->  |
    +-----------+-----------------------+
    |     F R E E A R E A               |
    +-----------------------------------+
    |              <-- free |   item    |
    +-----------------------------------+
    |   item    |   item    |   item    |
    +-----------------------------------+

BDB几乎所有的操作都以页为单位进行的,因此对页大小的选择也同样影响着BDB的性能。BDB允许将页限定在512字节到64K字节范围内。默认情况下,BDB会用操作系统磁盘IO块大小代替。在设置页大小时主要从以下三个方面进行考虑:用户数据溢出,锁,磁盘IO。
1,用户数据溢出:如果将页设置过于小,以至一个页无法保存下一个用户数据项(key,value),那么BDB会将这个用户数据项保存至溢出页,而这些溢出页最终以溢出页项(链表)组织起来。对溢出页的查询修改等相关操作都是影响性能的因素。所以对页大小的设置,应以不造成大量的溢出页使用为基础。
2,锁:除Queue存储结构以外(Btree,Hash,Recno),锁的粒度均以页为单位的,Queue可以以记录为单位。因此,页越大线程(进程)阻塞的几率会越大。
3,磁盘IO:操作系统内核采用块来进行磁盘IO调度,如果页设置小于磁盘IO块,那么内核需要对页进行拼接继而完成一次IO。操作系统进行页数据读入时,如果页小于磁盘IO块,那么意味着操作系统会读入更多的数据(以块为单位读入)置于操作系统高速缓冲区,从而造成缓存资源的浪费;操作系统进行页数据写入时,如果页小于磁盘IO块,那么必须先读入块,再将该页数据拷贝至块再写入到磁盘,而如果页大小与块相等的话将直接把页写入磁盘。如果页设置大于磁盘IO块,那么有可能引起操作系统更多的IO,因为连续的磁盘IO块读取将引起操作系统的预读机制,意味着请求一页数据,会造成操作系统连续读入更多的数据,带来更多的IO。
对于页大小的设置,建议数据项不超过磁盘IO块大小的话,将页大小设置成磁盘IO块同样大小。而大于磁盘IO块大小的话,则以够用为原则。

BDB的数据存储结构:有Btree,Hash,Queue,Recno四种。
1,Queue,Recno是相对较简单的结构,和他的名字一样,是一个队列,所以Key是内部维护的。Queue只能存储定长数据项,而Recno是可以定长和变长。同时前面也有提到Queue可以允许记录级别的锁,这在高并发情况下是一个优势。
2,Btree这种数据结构正是为了减少磁盘IO而产生的,详细的算法不在这里解释了。BDB使用B树进行存储时,这棵B树是一个有序的平衡B树。所有的用户数据(前面已经有说明,以页为单位)是存储于叶子节点(这里称为叶子页,相对于存储元数据的内部页来说),同时保持页内有序存储。因此查找时采用二分法进行查找。在已知一个数据项情况下,能够很容易得到相邻的数据项。因此,对于需要范围查找的情况下,B树是一个较好的选择。如果存储的数据量非常大,那么应该要考虑到B树在维护内部数据结构上面也会带来一定的开销。
3,Hash这种数据结构是为了随机查找应用而生的。在BDB里是采用动态Hash方式进行空间分配,所以几乎能按照用户数据进行分配空间。所有的用户数据页最终会以无序状态装入Hash桶。当然这种动态Hash方式在扩展容量时,进行桶分裂也是需要一定代价的。采用Hash存储结构时需要范围查找的话性能会不如B树。

GK.ZHONG
2009.09
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值