Compaction的作用

数据持久化

leveldb是典型的LSM树实现,因此需要对内存中的数据进行持久化。一次内存数据的持久化过程,在leveldb中称为MinorCompaction

一次minorcompaction的产出是一个0层的sstable文件,其中包含了所有的内存数据。但是若干个0层文件中是可能存在数据overlap的。

提高读写效率

正如前面的文章提到,leveldb是一个写效率十分高的存储引擎,存储的过程非常简单,只需要一次顺序的文件写和一个时间复杂度为O(logn)的内存操作即可。

相比来说,leveldb的读操作就复杂不少。首先一到两次读操作需要进行一个复杂度为O(logn)的查询操作。若没有在内存中命中数据,则需要在按照数据的新旧程度在0层文件中依次进行查找遍历。由于0层文件中可能存在overlap,因此在最差情况下,可能需要遍历所有的文件。

假设leveldb中就是以这样的方式进行数据维护,那么随着运行时间的增长,0层的文件个数会越来越多,在最差的情况下,查询一个数据需要遍历所有的数据文件,这显然是不可接受的。因此leveldb设计了一个MajorCompaction的过程,将0层中的文件合并为若干个没有数据重叠的1层文件。

对于没有数据重叠的文件,一次查找过程就可以进行优化,最多只需要一个文件的遍历即可完成。因此,leveldb设计compaction的目的之一就是为了提高读取的效率

平衡读写差异

有了minor compaction和majorcompaction,所有的数据在后台都会被规定的次序进行整合。但是一次majorcompaction的过程其本质是一个多路归并的过程,既有大量的磁盘读开销,也有大量的磁盘写开销,显然这是一个严重的性能瓶颈。

但是当用户写入的速度始终大于majorcompaction的速度时,就会导致0层的文件数量还是不断上升,用户的读取效率持续下降。所以leveldb中规定:

  • 当0层文件数量超过SlowdownTrigger时,写入的速度主要减慢;
  • 当0层文件数量超过PauseTrigger时,写入暂停,直至MajorCompaction完成;

故compaction也可以起到平衡读写差异的作用。

整理数据

leveldb的每一条数据项都有一个版本信息,标识着这条数据的新旧程度。这也就意味着同样一个key,在leveldb中可能存在着多条数据项,且每个数据项包含了不同版本的内容。

为了尽量减少数据集所占用的磁盘空间大小,leveldb在majorcompaction的过程中,对不同版本的数据项进行合并。