Kyoto Cabinet 实现原理

来源:互联网 发布:手机道路测量软件 编辑:程序博客网 时间:2024/04/28 11:12

       由于最近在做分布式存储系统,需要一个小、性能好、靠谱的存储引擎,我们瞄准了Kyoto Cabinet,希望通过分析源码能对它进行改造成我们需要的引擎,这一工作落在我身上,于是我花了一星期时间彻底地分析了Kyoto Cabinet源码,下面将分享下由Kyoto Cabinet源码分析出的实现原理。

       下面主要分析hashdb和treedb这两种存储结构,其他的要么简单,要么类似,就不分析了。

一、hashdb

      hashdb,顾名思义就是哈希表,只是哈希表存放在文件系统而不是内存,复杂度比存在内存大多了,冲突采用链表或二叉树解决。

      大家对哈希表应该都比较熟悉,它由三要素组成:哈希函数,哈希表和冲突解决方法。在无冲突情况下,查找、插入和更新复杂度为O(1);有冲突情况下的复杂度就视冲突情况来定复杂度了。增加哈希表的桶数,可以减少冲突,但会牺牲空间,所以具体应用时,需要权衡冲突和空间。

     下面分别对上面介绍的三要素是如何hashdb实现的。

(1)哈希函数

      hashdb的哈希函数是采用MurMur算法。具体可以看维基百科的介绍:http://zh.wikipedia.org/wiki/Murmur%E5%93%88%E5%B8%8C,实现函数是hashmurmur,这里就不贴代码了。

(2)哈希表

     hashdb的哈希表是文件系统里一个文件,它的结构由下面图所示:

                                         
                                                      图1 哈希表结构
                                        
                                                图2 bucket section内部结构 
 
                                        
                                               图3 record section内部结构
 
                                        
                                                图4  record 内部结构                                    
                                            
         由图1-图4可知哈希表是如何在文件系统里实现了,其中图1的meta data是哈希表的属性,图4的right address只有冲突方法是二叉树时才有,链表方式不需要。
 
(3)冲突解决方法
          hashdb有两种冲突方案:链表和二叉树,是可选的。冲突较少时用链表好,多时用二叉树。
 
        下面分析下hashdb里较难理解或需注意的技术细节。
(1)碎片整理
        a.  首然找到一个free block
        b.   然后将后面的记录的padding缩小,然后将记录紧凑地前移,在有限步下完成后,会空出一大块空间块
        c.   最后将该大块空间加入free block pool

(2)文件恢复

        触发条件:没有调用close而非正常关闭db。

        恢复思想:遍历旧hashdb文件里的record,然后将合法的record插入新hashdb,最后替换旧文件。

(3)非法close数据库,可能产生数据丢失或数据被认为合法但不正确

         a. 写padding size的时,成功写入第一个字节,写入第二字节失败,可能引起不可回收的文件碎片,或认为该记录不合法;
         b. 写key size或value size写一半,可能引起该记录的边界到了第二个记录上,这就为什么恢复DB时,需要顺利正确能读出两个记录的第一个记录才认为是合法的; 

         c.  写data写一半,只会引起数据不正确;
         d. 写Address写一半,可通过自动恢复dB恢复,而且数据没有丢失或不正确。

(4)Free block pool大小超过最大值时,会有free block无法放入Free block pool,导致该free block无法利用,直到碎片整理到它。

(5)事务

        hashdb是采用预写日志方式来实现事务的,它的事务隔离级别是可序列化的,事务的最高隔离级别,粒度最大。

        日志文件格式如下图所示:

                        
                                              图5 日志格式

 

(6)hashdb的锁粒度是record级别,但又不完全是,它是分配一定数量的锁放到内存级哈希表,通过哈希的方式去获取锁。

 

二、treedb

        treedb的数据结构是采用最小B+树,B+持久化到磁盘方式是采用hashdb,我感觉该持久化方式是比较偷懒的,性能没有直接将B+树保持到磁盘好,因为通过hashdb会产生较多的磁盘IO,但作者为弥补这一点,treedb内部实现了一级lru的index page node cache和二级lru的leaf page node cache来减少读写磁盘。

         treedb主要内部数据结构由下图所示: 

         
                                                  图6  Index node结构
 
          
                                                图7 Link node结构
 
        

                                               图8 Leaf node结构

        
                                              图9 recode结构 

        上面说了,treedb持久化方式是采用hashdb,上图的index node和leaf node对应hashdb的value,key是node id。

        另外,如果不设autsyn,treedb不会该更新立即写入磁盘,而是写在cache里,后面某个时间再刷到磁盘上。

        treedb的锁粒度是node,即page。

       treedb里的lru cache是采用一般的实现方式:哈希表+双向链表。

0 0
原创粉丝点击