leveldb阅读源码总结

经过一个多月的源码阅读,总算是把leveldb的源码几乎阅读完了.leveldb不愧是一款非常优秀的数据库存储引擎框架,优秀的架构设计,优雅的代码编写,清晰的类与类之间的逻辑关系以及较高的执行效率等等.不敢想象leveldb是两个人编写出来的,真心膜拜谷歌两位大神.今天就把之前看的总结下~

leveldb整体框架


leveldb总体上是由memtable,immemtable和sst文件组成,如下图所示,图片来自网络: leveldb框架图;

插入数据


  1. 插入数据时,先是将数据插入日志文件,然后插入memtable.因为如果是相反的顺序插入,假设一种情况,当插入memtable后,突然断电,那么就来不及插入日志文件中,这样就会造成数据丢失.
  2. 当memtable数据量达到一定大小时,将mem_赋值给imm_,然后重新new 一个memtable;接着重新新建一个log文件.DBImpl::MakeRoomForWrite函数内执行
  3. 接着imm_会被Compaction到磁盘文件中

读取数据


leveldb读取数据总是先读取最新的数据,因为有可能插入键值一样的键值对,但是我们查询时,是想获取最近插入的数据,所以读取数据时的顺序为

memtable->immemtable->sst文件

leveldb优秀设计

内存池的设计


在接触leveldb之前,我对内存池的认知只停留在存储一大块内存的地方,但是具体实现一无所知,看了leveldb的内存池Arana之后,以及还有后来STL内存池,我对内存池有个较完整的认识.

Arena实现原理是先像系统申请一大块内存,然程序需要内存时,直接从内存池中获取即可,这样可以减少系统分配内存次数,降低系统分配内存带来的消耗.但是当程序需要一大块内存时(>=1024b)时,单独分配一块内存,这样也是为了减少系统分配内存的次数.

缓存设计


在接触leveldb之前,我听说过缓存这个东西,是在看linux内核设计与实现时,当时只知道有这么一个东西,能够缓存数据,但是并不能想象得出是怎么实现的.leveldb的缓存设计弥补了我这方面的知识盲区,我大概就知道内核缓存区是如何实现了.

leveldb的cache是由16个缓存区构成,每一个缓冲区是由哈希表和环形双向链表构成.哈希表主要是用来加快查询速度.环形双向链表在leveldb用的比较多,快照链表,版本链表都是环形双向链表实现的.环形双向链表有一个傀儡节点Dummy,Cache的链表Dummy.prev是最新的数据,Dummy.next是最"老'的数据.当有新数据时,往Dummy.prev插入,当要删除数据时,删除Dummy.next的数据.

锁的设计


在看skiplist时,我发现skiplist没有使用锁,互斥量,信号量等同步机制,我很好奇,因为leveldb是多线程安全的.后来百度得知leveldb用的是内存屏障,内存屏障是更底层的锁,应用在CPU寄存器和Cache里.通过百度,对内存屏障有了初步了解,但是如何实现同步,还未知...

迭代器的设计


我看完源码之后发现,leveldb到处都在用迭代器,也可以说从底层迭代器开始,一层一层往上封装.先是skiplist迭代器->memtable迭代器,然后是block迭代器和文件迭代器->table双层迭代器->table_cache迭代器->version迭代器.最后MergeIterator->DBIterator.迭代器的设计,最大的好处就是方便了底层数据的检索,只需要一个迭代器,即可屏蔽很多不需要的细节.

对不同系统,实现不同的封装


leveldb把对系统调用封装在了env环境类中这样当在不同系统使用leveldb时,只需要实现当前系统的Env即可.leveldb默认实现的是posix_env,即linux操作系统系统调用.

简单的接口设计


leveldb提供接口极其简单,而且数量少,用户简单的学习即可上手.主要原因是上层调用对下层调用实现极好的封装,例如迭代器,一个迭代器,即可迭代mem,imm,sst中所有数据.

优雅的代码


leveldb使用C++开发,充分利用了C++多态的特性,提供的都头文件iterator.h,comparator.h,cache.h等都是为抽象类,真正操作的是这些抽象类的子类.

其次,看完源码之后,没有看到一个#define宏定义,看了Effective C++中有说明,C++尽量不要用宏定义,取而代之的是:

  1. 常量用const和enum代替
  2. 宏操作用内联函数代替

以上都是我目前的认知,可能还有一些很好的设计,我没提出来.

学习优秀的源码对自己编程的提高很有帮助,以及可以学到好多操作系统基础知识,leveldb主要是一些接口,而redis是一款完整的软件,redis的源码学习可以学到一款软件的方方面面,网络啊,命令执行过程,以及IO复用的Mainae模块,非常推荐~