nessDB2.0 学习笔记

nessDB2.0是1.0的改进版,整个sst的结构有了很大的变化,所以阅读源代码的时候虽然整体的思想没有改变,但是也不能一直想着

1.0的结构,其实附带上spec文件夹下的small-splittable-tree.txt文件阅读,会给整体阅读带来极大的方便。

 

首先2.0还是那些util文件:
buufer.c,bloom.c,skiplist.c 基本没变化
degug.c 是生成日志的代码
xalloc.c 是对内存分配的封装
quicklz.c 是对数据的压缩,不看
sorts.c 主要是插入排序和归并排序
log.c 在恢复时使用

 

nessDB2.0的逻辑文件为:

compact.c 是对.db文件中由于value删除造成的数据空洞的记录以及分配。但是参看cpt_new的代码,这些空洞的记录并没有持久化,本次打开时没有上次时的空洞记录。其实这可以参考gdbm的av_block结构,并使用tcmalloc的内存管理方式来管理这些空洞。毕竟为了达到顺序写的目的,应该尽可能将连续的空洞合并,达到足够的大小时才写数据。而这正可以使用内存管理的方式实现。

 

前面已经知道sst文件的结构发生了很大的变化,在1.0的学习笔记中提出1.0其实还“不够lazy”——每次skiplist一满,就对每个

sst开始进行重新写入了,2.0对这种情况进行了改善,使用LSM-Tree的多组件算法分层进行延迟批处理,策略的实现在block.c,

sst.c,meta.c,index.c中。

 

首先介绍一下sst文件的结构:sst文件中的数据有明显的分界,它被分为LEVEL_MAX层,每层可容纳的元素(元素是定长的)数量是按照LEVEL_BASE的数量乘积递增的。其中第0层是AOF的,也就是无序,而其余高层都是有序的,层数越低,它的更改时间就越新,所以在低层查找到后,就不用再去高层查找了。

 

在1.0的学习笔记中也说过必须要对查找进行优化,那么这种优化就在block.c中。sst的每层其实是被分成多个block的,每个block又被分成多个gap,程序默认BLOCK_GAP为256,也就是说每隔256个元素,sst中第level层的元素就被加入到block中,当查找时使用lower_bound,然后再从sst中读取这256个元素后进行顺序查找。


下面具体分析每个文件的函数功能。

 

block.c

我们可以从block_new函数中看出sst文件是分成多层的,每层都是有固定容量的。

再看block_build函数,首先需要读取sst中第level层的元素放入一个vector中,然后每隔256个元素放入到block[level]中。block并

不是在sst文件里,而是在内存里,就相当于是对sst每个层的索引吧。

block_search是顺序查找lower_bound


meta.c 具有1.0中meta的功能,即使用一个数组进行sst中end_key的记录,并进行搜索。2.0中meta还增加了对sst文件的
split操作支持。正是由于这种改变,使得原来独立的sst数据结构被加入到了meta_node中。

我们可以从_build_meta函数中推断出meta中存放meta_node的就是每个sst的end_key的封装,从memmove中可以看出meta的数组是有序的。

对sst文件进行分裂的函数是_scryed和_split_sst。前者被后者调用,它的作用是新建一个meta_node和sst结构,将特定的sst_items

放入到.sst文件(这个文件是被truncate或者新建的)中。然后将meta_node插入到meta的适当位置。_split_sst函数则是将一个已满的

.sst文件分成最多NESSDB_SST_SEGMENT+1个新的.sst文件,分解的策略是将该.sst文件中的sst_item组成一个长的数组,分成

NESSDB_SST_SEGMENT+1(如果不整除)段,然后将每段使用_scryed函数写入到.sst文件中。不过我觉得_scryed就不应该使用sst_add函数了,有点浪费时间,应该直接将数据插入到.sst文件中的0层以后,这样会简单一些。


sst.c .sst文件是nessDB2.0的灵魂,里面涉及了文件的读,写,查询操作文件开始是_pos_calc函数,它可以给出一个level在文件中开始的偏移地址。不过每次都需要进行pow操作,有点浪费时间。可以学习BeansDB中的g_index结构,直接给出g_index[level]的文件偏移。另外,由于.sst文件中存放着sst_item,它是定长的一段数据,所以当我们想要查找第几层的几个gap的第几个sst_item时,是可以直接计算出来的

 

第二个函数是_update_header,恩,相较于1.0的footer,2.0使用了header结构,不过这没什么影响,把文件信息放在头部或者尾部都一样的,因为sst文件其实是定长的。read_one_level函数用来读取.sst文件中一层的sst_item,第0层是无序的,需要使用sort.c中的插入排序_merge_to_next使用sort.c的归并排序将level层的sst_items跟level+1层的sst_items合并后放入到level+1层,这样level层就空了,level+1层会增加一些元素,这里并没有继续判断level+1层是否会满,而是交给_check_merge来做了。

_check_merge来判断某层的容量是否已经满了。如果满了则merge到上一层——merge的数量是最多不超过上一层的容量。这杨即使上一层满了,也会在for循环的下一次中得到merge。

sst_in_one是将整个.sst文件中的items放入到一个有序的数组中有了.sst文件的结构,sst_add和sst_get就很简单了。
sst_add是直接将item放入到level0中,然后修改header。不过每次add都要修改header,这不好吧?试想,在add item的时候磁头指
向.sst文件的一个位置,但是写header的时候又要指向另一个位置,这会造成不必要的寻道。
至于sst_get,过程就有点复杂了,其实这个.sst是meta告诉我们需要查找的,然后在这个.sst中首先读取level0,然后排好序,依次查
找(有没有觉得操作好浪费时间?)如果最底层没有,则向更高层寻找。注意高层是有block优化的,先读取block,然后再读取

BLOCK_GAP个sst_item,依次寻找。有没有觉得sst_get的过程很繁琐?没错,因为LSM-Tree牺牲了查询的时间来快速进行写入。不过,查询其实还是可以优化的,这就要看leveldb了。

 

index.c 跟1.0的没什么太大的差别,只是加入了压缩。

 

而db.c呢? 也跟1.0没啥区别,只是去掉level lru了,不知道为啥。

 

好了,nessDB算是看完了,我看得比较粗,也许有很多精髓的地方不知道,还望知道的人相告。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值