LSM树简介
概述
LSM树(Log Structure Merge Tree,日志结构合并树)不是树,而是一系列树。日志结构,说的是 SSTable(Sorted String Table,有序字符串表),是一种 Append Only 的日志形式的存储结构。合并树,说的是逐层合并 SSTable Index(有序字符串表索引),SSTable Index 采用 B-tree 实现。
整体来看,LSM 树很适合写入多于读取的应用场景,其通过将随机写转换成顺序写,提升写入效率,但同时因为读取时需要依次访问 MemTable 和 SSTables,降低了读取效率。
读写过程
(来源:LSM Tree-Based存储引擎的compaction策略(feat. RocksDB))
(来源:Flink Forward Berlin 2018: Stefan Richter - "Tuning Flink for Robustness and Performance")
如图,写入时,会将数据先写入 WAL(Write Ahead Log,预写日志)避免数据丢失;然后再写入 active memtable(活跃内存表),active memtable 满了之后再转入 immutable memtable(不可变内存表),之后再刷入磁盘中的 L0 级 SSTable,并随着数据量的增加依次压缩(compact)到 L1、L2 直到设定的最终一级的 SSTable。
而读取时,会依次从 active memtable 到 immutable memtable 再到 block cache 中查找,如果 block cache 中找无,会从磁盘中的 SSTables 查找,然后再加载到 block cache 中,以便下次直接击中缓存,提升读取效率。
SSTable 之结构
SSTable 即有序字符串表,其将键值依次追加到 SSTable file 末尾,并将键在文件中的偏移量(offset)写到索引文件中。因为 SSTable 是不可变的(immutable),更新键值会首先写入 Memtable,然后刷入 SSTables,但不会修改以往的数据。读取的顺序性保证了新的键值会被读取到而旧的数据不会被触及;删除键值时也同理,不过删除时是写入一条“墓碑”(tombstone)记录。
(来源:SSTable and Log Structured Storage: LevelDB)
SSTable 的索引采用 B-tree 实现,
压缩策略
从上面的描述中,我们知道更新和删除键值时,旧的数据并没有删除,随着更新和删除操作的增加,SSTables 中保留了越来越多的冗余数据。于是,LSM 提供了两种压缩策略。此两种策略可以混合使用,以便实现最佳效率。比如 RocksDB 就使用了 LCS 策略,但在 L0 级使用了 STCS 策略。下面粗略介绍下 STCS 和 LCS。
STCS
STCS,即 Size-Tiered Compaction Strategy,暂译为「体积阶梯式压缩策略」。如下图所示,刚开始的 SSTable 体积较小,随后就像大鱼吃小鱼一样,四个较小的 SSTables 合并成一个中型的 SSTable,而四个中型的 SSTables 又被合并成一个较大的 SSTable,配置该策略时需要指定各个体型(small、medium 和 large)的文件体积大小,看起来就像一个阶梯。
(来源:Scylla’s Compaction Strategies Series: Space Amplification in Size-Tiered Compaction)
LCS
LCS,Leveled Compaction Strategy,暂译为「层级式压缩策略」。如下图所示,每个 SSTable 的体积大致相同,但限制每个层级的文件数,当某一层级的文件数超过限定值时,会移动并合并到下一层级。
(来源:Scylla’s Compaction Strategies Series: Write Amplification in Leveled Compaction)