LevelDB——Compaction操作理解

LevelDB

LevelDB的数据是存储在磁盘上的,采用LSM-Tree的结构实现。LSM-Tree将磁盘的随机写转化为顺序写,从而大大提高了写速度。
为了做到这一点LSM-Tree的思路是将索引树结构拆成一大一小两颗树,较小的一个常驻内存,较大的一个持久化到磁盘,他们共同维护一个有序的key空间

写入操作会首先操作内存中的树,随着内存中树的不断变大,会触发与磁盘中树的归并操作,而归并操作本身仅有 顺序写 。随着数据的不断写入,磁盘中的树会不断膨胀,为了避免每次参与归并操作的数据量过大,以及优化读操作的考虑,LevelDB将磁盘中的数据又拆分成多层,每一层的数据达到一定容量后会触发向下一层的归并操作,每一层的数据量比其上一层成倍增长。这也就是LevelDB的名称来源。

LevelDB组件

LevelDB按照存储来分,可以分为三个组件:

  • MemTable
    MemTable是一个内存数据结构,简单说它就是一个SkipList
  • SSTable
    SSTable(Sorted String Table),意思是数据按照键的顺序存储在磁盘上。
  • WAL
    WAL (Write Ahead Log),正如名字一样,就是在写入前先写入日志的意思。InnoDB也提供了类似的Redo Log。

在这里插入图片描述

MANIFEST是用来管理SSTable的,保存了SSTable的元信息。

MemTable
因为MemTable是内存数据结构,不需要磁盘IO,所以读写的速度非常快,所以就达到了场景需求:高效的写性能。然而这会有个问题,当数据库实例崩溃、宕机或者停机维护的时候,存储的数据就会丢失,这时候需要持久化数据。
当Memtable写入的数据占用内存到达指定数量,则自动转换为Immutable Memtable,等待Dump到磁盘中,系统会自动生成新的Memtable供写操作写入新数据。

WAL
因为日志的写入都是Append的,也就是顺序写,所以写磁盘也是顺序写,虽然磁盘的随机写效率比较低,但是顺序写效率还是很高的,所以就算加入了日志,写效率还是很高,依然可以满足写多的场景。

有了MemTable和WAL,就有了一个功能比较完备的数据库了,类似于一个简化版的Redis。然而这又引入了新的问题:
如果日志量很大,每次重启数据库时,恢复的时间非常长;
内存的容量是有限的,所以当数据量太大,MemTable的大小超过内存容量时,就没法写入数据了。
这时候不仅仅需要将日志写入到磁盘,也需要定时将MemTable的镜像写入磁盘,并且清空MemTable。

SSTable
键是有序的,查找键可以采用二分搜索。但是磁盘的二分搜索会读取多个磁盘块。这时候只需要给每个磁盘块一个索引,告诉这个磁盘块里面存储键的范围,那么在查找时,可以先通过对索引进行二分搜索找到键所在的磁盘块,只需要读取这个磁盘块,便可以找到这个键。索引是一个稀疏索引,比较小,可以放在内存中缓存

定时将MemTable的镜像写入磁盘中的一个SSTable,数据就会分为两个部分:一部分是内存中的MemTable里的数据,一部分是磁盘中SSTable的数据。当写入数据时,还是写入MemTable和日志,而后台线程定时将MemTable的镜像写入磁盘的SSTable中,如果很好的控制后台线程的写入频率,或者SSTable和日志不在同一个磁盘,那么写入依然是比较符合顺序写的,可以有很高的写性能。写入SSTable后,之前的日志就可以被淘汰了,因为之前的数据已经持久化到磁盘上了。这就同时解决了上面的两个问题,恢复时间和数据库容量。

LevelDB读/写操作

首先,生成内部查询所用的Key,用生成的Key,依次尝试从 Memtable,Immtable以及SST文件中读取,直到找到(或者查到最高level,查找失败,说明整个系统中不存在这个Key)。

LevelDB的写操作包括设置key-value和删除key两种。需要指出的是这两种情况在LevelDB的处理上是一致的,删除操作其实是向LevelDB插入一条标识为删除的数据。

Memtable并不存在真正的删除操作,删除某个Key的Value在Memtable内是作为插入一条记录实施的,但是会打上一个Key的删除标记,真正的删除操作是Lazy的,会在以后的Compaction过程中去掉这个KV。

所以这里介绍插入操作:

  1. 首先是将这条KV记录以顺序写的方式追加到WAL,因为尽管这是一个磁盘读写操作,但是文件的顺序追加写入效率是很高的,所以并不会导致写入速度的降低;
  2. 如果写入log文件成功,那么将这条KV记录插入内存中的Memtable中,前面介绍过,Memtable只是一层封装,其内部其实是一个Key有序的SkipList列表,插入一条新记录的过程也很简单,即先查找合适的插入位置,然后修改相应的链接指针将新记录插入即可。完成这一步,写入记录就算完成了。

LevelDB压缩操作(Compaction)

数据压缩是LevelDB中重要的部分,即归并。冷数据会随着Compaction不断的下移,同时过期的数据也会在合并过程中被删除。

LevelDB的压缩操作由单独的后台线程负责。这里的Compaction包括两个部分,Memtable向Level 0 SST文件的Compaction,以及SST文件向下层的Compaction

LevelDb包含 Minor和Major两种压缩方式,Minor Compaction 就是把memtable中的数据导出到SSTable文件中;Major Compaction就是合并不同层级的SSTable文件。

Minor Compaction

Minor compaction 的目的是当内存中的memtable大小到了一定值时,将内容保存到磁盘文件中。
当memtable数量到了一定程度会转换为immutable memtable,此时不能往其中写入记录,只能从中读取KV内容。

之前介绍过,immutable memtable其实是一个多层级队列SkipList,其中的记录是根据key有序排列的。所以这个minor compaction实现起来也很简单,就是按照immutable memtable中记录由小到大遍历,并依次写入一个level 0的新建SSTable文件中,写完后建立文件的index数据,这样就完成了一次minor compaction。

Major Compaction

当某个level下的SSTable文件数目超过一定设置值后,levelDb会从这个level的SSTable中选择一个文件(level>0),将其和高一层级的level+1的SSTable文件合并,这就是major compaction。

我们知道在大于0的层级中,每个SSTable文件内的Key都是由小到大有序存储的,而且不同文件之间的key范围(文件内最小key和最大key之间)不会有任何重叠。Level 0的SSTable文件有些特殊,尽管每个文件也是根据Key由小到大排列,但是因为level 0的文件是通过minor compaction直接生成的,所以任意两个level 0下的两个sstable文件可能再key范围上有重叠。

Major Compaction对多个文件采用多路归并排序的方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值