【糙叔带你看源码】leveldb源码解析之零开篇

1,973 阅读4分钟

leveldb是谷歌的一个单机kv存储系统,代码量不足1.5w行,加上谷歌的代码风格逼格比较高,逻辑比较清晰,比较适合学习,趁着闲暇时刻,翻出来看看。

leveldb的本质是数据库,而数据库是以格式化存储数据,并且提供增删改查等功能。假设有个leveldb的对象mydb,leveldb可以简化成以下三个接口:

1 mydb.Put(key, value)
2 mydb.Get(key)
3 mydb.Del(key)

功能上有点像常见的map,实际上更复杂,作为数据库需要考虑更多的东西,比如数据格式化、持久性、性能高低等。leveldb的整体架构如下图:

根据上图,可以对前面提及的三个接口的流程做个简单描述。

首先是Put流程,详细步骤如下:

1.KV写操作按一定格式的二进制数据先写到log文件,顺序写入,做持久化;
2.写log文件后再写入memtable;
3.之后write操作就返回操作成功
4.当memtable的大小达到一个上线,那么memtable会变成Immutale memtable。这两个table的数据结构是一样的,都是跳跃表,只不过后者是只读的。
5.这时候会产生一个新的memtable来支持新的写操作
6.Imutable memtable的内容会定期刷到磁盘,然后清楚对应的log文件,生成新的log文件。
7.磁盘中数据的存储格式叫sstable。

当写入一个kv数据时,会经历memtable, immutable memtable, sstable三个table和一个log,其中memtable和immutable memtable都是跳跃表(这块内容下次细聊),sstable就大有来头了,传说是google三大论文之一的Bigtable中的那个sstable,其结构如下图:

level0是由Imuabletable memtable直接dump到磁盘中的,level1是由level0经过compaction获得,level2是由level1经过compaction获得,以此类推。其中每个文件后缀为.sst, 每个level中的文件数都是有限制的,超过了限制则会被compaction到更高level的层次上去,所以这个东西叫leveldb。其中每一个level符合以下规则:

1. level0中的单个文件(sst)是有序的,但是文件与文件之间是无序的并且有可能有重合的key
2. level 1 ~ level n 每一个level中在自己level中都是全局有序的
3. mainifest文件中包含了每一个sst文件的最小key和最大key,方便查找

log的功能是保证数据的完整性,每一次kv操作需要一次磁盘的顺序写和一次内存写,写的效率非常高。如果内存中的数据丢失,可以从log中读取操作命令重新恢复数据。数据到达sstable之后,旧的log文件会被删除并且产生新的log文件。

然后是Get流程,理解写数据之后,对读就更好理解了。读的流程如下:

1.memtable
2.immuable memtable
3.level 0
4.level 1
……

查找先经过memtable查找,找不到依次顺序往下找,一直找不到返回empty。

最后是删除,数据库的数据以一定的格式存储在一起,要删除某部分就要移动剩下部分,工作量巨大,但是leveldb没有实际的删除操作,只是write一个删除标记和key进去。

key插入的时间都是有序的,从memtable到level n,删除的时候先插入待删除的key和删除标记,在查找的时候遇到待删除标记的key便返回empty,此外在level compaction到level n+1的时候如果又删除的key,便会真正删除它。

以上便是leveldb的框架,其主要特点包括以下几点:

  1. leveldb是一个持久化存储的KV系统,和Redis这种内存型的KV系统不同,leveldb不会像Redis一样狂吃内存,而是将大部分数据存储到磁盘上。
  2. leveldb在存储数据时,是根据记录的key值有序存储的,就是说相邻的key值在存储文件中是依次顺序存储的,而应用可以自定义key大小比较函数,leveldb会按照用户定义的比较函数依序存储这些记录。
  3. 像大多数KV系统一样,leveldb的操作接口很简单,基本操作包括写记录,读记录以及删除记录。也支持针对多条操作的原子批量操作。
  4. leveldb支持数据快照(snapshot)功能,使得读取操作不受写操作影响,可以在读操作过程中始终看到一致的数据。
  5. leveldb还支持数据压缩等操作,这对于减小存储空间以及增快IO效率都有直接的帮助。

  leveldb性能非常突出,官方网站报道其随机写性能达到40万条记录每秒,而随机读性能达到6万条记录每秒。总体来说,LevelDb的写操作要大大快于读操作,而顺序读写操作则大大快于随机读写操作。