LSM-Tree:分布式存储中的数据结构

Log-structured merge-tree 日志结构合并树 (LSM-Tree)。它是一种树形的数据结构,由多个层级组成,每层的数据按 key 有序。LSM-Tree 主要设计用来应对写入密集型工作负载,并于 1996 年在同名论文 The Log-Structured Merge-Tree

Log-structured merge-tree

日志结构合并树 (LSM-Tree)。它是一种树形的数据结构,由多个层级组成,每层的数据按 key 有序。LSM-Tree 主要设计用来应对写入密集型工作负载,并于 1996 年在同名论文 The Log-Structured Merge-Tree (LSM-Tree) 被大家所知。

LSM-Tree 的最高层保存在内存中,包含最近写入的数据。其他较低层级的数据存储在磁盘上,层数编号从 0 到 N 。第 0 层 L0 存储从内存移动到磁盘上的数据,第 1 层及以下层级则存储更老的数据。通常某层的下个层级在数据量上会比该层大一个数量级,当某层数据量变得过大时,会合并到下一层。

⓵ MemTable

  MemTable 在内存中缓存客户端写请求发送的数据,通常用平衡二叉树跳跃表实现。MemTable 中存储的所有数据都是按照 key 有序排列的,当 MemTable 中存储的数据量到达设置的阈值时,这些数据会被写入磁盘,生成一个新的 SSTable。与此同时,一个新的空的 MemTable 被创建出来继续响应客户端的写请求。

客户端的读请求最先也是同 MemTable 进行交互

⓶ SSTTable

  当 MemTable 中的数据量到达设置的阈值或 LSM-Tree 在后台对已有 SSTable 进行压缩操作时,新的 SSTable 就会被创建。SSTable 中的数据也是按照 key 有序存储,以提高读操作的性能。

同一个 SSTable 中的 key 是有序的,但不同的 SSTable 之间的 key 不保证有序

  随着时间的推移,写操作会导致磁盘上 SSTable 数量增加,这些 SSTable 会被不断的重新组织并压缩成从 level 0level n 的不同级别。level 0 级的 SSTable 只是简单的将 MemTable 中的数据进行转储,并不进行其他的处理,所以多个 SSTable 中会出现重复的 keylevel 1 及以后的级别的 SSTable 中则不会出现这种情况。

 高 level 中的 SSTable 是通过压缩合并低 level 中的 SSTable 得到的
 已有的 SSTable 在经过压缩合并后会生成新的、数据量更大的 SSTable 存入更高的 level 中,这样既可以减少磁盘中文件的数量,还有助于提升读操作的性能
 往往越高的 level 中的 SSTable 的 size 会越大,SSTable 之间出现重复 key 的情况会越少

  在实际应用中,为了提升 SSTable 读操作的性能,SSTable 中还会存储一些额外的元数据:

  • 稀疏索引(sparse indexes)

  稀疏索引提供 SSTable 中的 keykey 的范围到 key 的具体存储地址或偏移量的映射。SSTable 中的数据按照 key 有序存储,在 SSTable 中查找某个特定的 key 时,可以先通过稀疏索引定位查找的起始位置,然后再在 SSTable 中进行二次查找。

  • 块索引(block-based indexes)

  块索引的原理与稀疏索引类似,只不过块索引将 keykey 的范围映射到了指定的存储块(block)。SSTable 中的数据会被分成多个指定大小的块,每个块都会被分配一个指定的 ID 或偏移量,读操作先通过块索引定位要查询的数据所在的块,然后再在特定的块中进行二次查找。相对于稀疏索引,块索引的粒度相对比较粗。

  • bloom filter

  bloom filter 可以快速确定要查找的 key 是否存在于指定的 SSTable 中。由于存在哈希碰撞,bloom filter 可能会误报,但不会漏报。

以上这些元数据,不一定同时存在于 SSTable 中,具体情况取决于各个应用中的代码实现。

为什么LSM不直接顺序写入磁盘,而是需要在内存中缓冲一下?

单条写的性能没有批量写快,很多中间件比如elasticsearch、kafka、mysql都有类似的内存缓冲设计

在磁盘缓冲的另一个好处是,针对新增的数据,可以直接查询返回,能够避免一定的IO操作

LSM-Tree和B+Tree的比较

LSM-Tree的优点是支持高吞吐的写O1,这个特点在分布式系统上更为看重

针对读取普通的LSM-Tree结构,读取是On的复杂度

在使用索引或者缓存优化后的也可以达到O(logN)的复杂度。

适用于写多读少

B+tree的优点是支持高效的读(稳定的O(logN))

但是在大规模的写请求下(O(LogN)),效率会变得比较低,因为随着insert的操作,为了维护B+树结构,节点会不断的分裂和合并。操作磁盘的随机读写概率会变大,故导致性能降低。

适用于写少读多或写读平衡

Comment