快速理解LSM-Tree存储结构

275 阅读3分钟

LSM-Tree

适用于磁盘存储的索引结构

  • 原地存储(B+Tree):读性能更好,多应用于关系型数据库。
  • 非原地存储(LSM-Tree):写性能更好,多应用于非关系型数据库。

什么是非原地存储

写入是有序的,但数据是无序的,假设一组操作如下。

write("hello")

write("level")

write("db")

数组组织格式如下。

"hello""level""db"

这样的组织格式让写入性能更好,读取性能更差,需要遍历查询。

对于键值存储也有会空间浪费的情况。

write["x"] = 1

write["a"] = 1

write["x"] = 0

数据组织格式如下。

"x" = 1"a" = 1"x" = 0

键值的覆盖需要多份键空间,读取需要自后向前遍历。

如何解决空间冗余的问题

随着数据不断写入,过多的冗余空间会导致存储空间不足,因此需要对存储记录进行压缩。

设置一个分段大小,当前分段大小达到上限后,新创建一个分段为当前分段。数据写入当前分段,压缩之前的分段,压缩后每个记录分段内没有重复的键。

范围查询如何进行

可以对每个分段排序,再对所有分段做归并排序,归并的过程中删除不同分段间重复的键。

但在磁盘上排序并不是个高效的事情,因此记录应当存储于内存之中。当前分段为memtable,相应的之前的分段为immutable memtable。

换成内存存储后的改进

内存的随机写入和顺序写入性能是一样好的,因此没必要顺序写入memtable再进行排序,可以适用skiplist作为memtable的数据结构。

内存是有限的,不能无穷尽的生成immutable memtable,因此每生成一个,就持久化到磁盘上,这样在内存中就只要一个memtable和一个immutable memtable就够了。

预写日志保证数据不丢失

内存是易失存储,memtable中的数据再未持久化到磁盘前有丢失的风险,因此数据在写入memtable前,需要先写到log中,log在磁盘上,所以顺序写。log不用做存储,只用作备份。

log有两个,分别对应memtable和immutable memtable,当immutable memtable持久化到磁盘后,对应log中的记录就会被删除。

持久化数据的组织方式

持久化到磁盘上的数据称为SST(Sorted String Table)文件,从内存直接dump到磁盘的文件,属于Level 0的文件,Level 0的大小是有限的,在若干个Level 0 SST文件生成后,占用空间达到了Level 0的上限,就会把Level 0归并为若干个文件,归并的过程中,保证归并后的文件间不存在重复的键。

由Level 0归并的文件,属于Level 1层的文件,相应的Level 1层空间达到上限后,会继续向下归并产生Level 2、Level 3 ...除了Level 0外,其余所有同Level的SST间不存在重复的键。