(一)研究目的
总结关于lsm-tree的研究成果。
(二)LSM-tree基础
2.1 LSM-tree历史
通常,索引结构可以从两种策略中选择一种来处理更新,即就地更新和非就地更新。一个就地更新结构,比如B+树,直接覆盖旧记录来存储新的更新。这些结构通常是读优化的,因为只存储每个记录的最新版本。但是,这种设计牺牲了写性能,因为更新会产生随机I/o。此外,索引页可以通过更新和删除进行分片,从而降低空间利用率。
相比之下,非就地更新结构(如lsm-tree)总是将更新存储到新的位置,而不是覆盖旧的条目。这种设计提高了写性能,因为它可以利用顺序I/o来处理写。它还可以通过不覆盖旧数据简化恢复过程。但是,这种设计的主要问题是牺牲了读取性能,因为一条记录可能存储在多个位置中的任何一个。此外,这些结构通常需要一个单独的数据重组过程,以不断提高存储和查询效率。
在使用lsm -树之前,日志结构存储的方法存在几个关键问题。首先,将数据存储到仅追加日志中会导致较低的查询性能,因为相关记录分散在日志中。
另一个问题是空间利用率低,这是因为还没有删除过时的记录。尽管设计了各种数据重组过程,但没有一个有原则的成本模型来分析写成本、读成本和空间利用率之间的权衡,这使得早期的日志结构存储难以调优;数据重组很容易成为性能瓶颈。
1996年提出的LSM-tree通过设计一个合并过程来解决上述问题,提出的滚动合并流程由于其实现的复杂性,目前基于lsm的存储系统不使用它。在稳定的工作负载下,层数保持不变,当所有相邻组件的大小比Ti = |Ci+1|/|Ci|保持一致时,写性能得到优化。这一原则影响了lsm-tree的所有后续实现和改进。
同时Jagadish等人提出了一种类似的结构,并带有分步合并策略,以获得更好的写性能。这个策略成为今天LSM-tree实现中使用的分级合并策略。
2.2 如今的LSM-tree
2.2.1 基本结构
- 追加写
- 内存里的数据结构是跳跃表或B+tree,磁盘上的数据结构是B+tree或sorted-string tables(SSTables)
- 查询从新到旧
- 水平合并策略 or 分级合并策略
2.2.2 两种有名的优化
Bloom Filter 和 Partitioning
Bloom filter 是一种节省空间的概率数据结构,用于辅助查询。
分区将一个较大的组件合并操作分解为多个较小的操作,从而限制每个合并操作的处理时间以及创建新组件所需的临时磁盘空间。通过只合并具有重叠键范围的组件,分区可以优化具有顺序创建键或倾斜更新的工作负载。
只有基于lsm的工业存储系统(如LevelDB和RocksDB)完全实现了分区均衡策略。一些论文提出了各种形式的分区分级合并策略,以获得更好的写性能。
垂直分组方案和水平分组方案:
2.2.3 并发控制与恢复
对于并发控制,lsm-tree 需要处理并发的读和写,并处理并发的刷新和合并操作。根据事务隔离需求的不同,使用 锁模式 or 多版本模式。
(三)LSM-tree改进
提出了一种分类法,