SSTables and LSM Trees
SSTables:Sorted String Table : 每个segment文件中key值是有序(根据key值排序)并且唯一的。sstable有如下优势:
- merge简单高效
- 因为有序,不需要维护所有记录的索引,可以是稀疏索引
- 因为有序,可以按block进行压缩同时维护索引,除了降低磁盘占用以外,还可以降低磁盘io
构建和管理SSTables
因为写入是无序的,所以我们需要在内存中借助于红黑树或者avl树等数据结构来保证:任意写入,有序读取.
构建步骤如下:
- 当写入发生时,将写入的key-value加入内存平衡树(如红黑树)中,称为”memtable”
- 当memtable增大到一定阈值后(a few megabytes),将memtable写入磁盘生成一个SSTable文件。新写入的SSTable成为数据库中最新的segment文件。 写入SSTable到磁盘时,写入可以继续写入新的memtable
- 读请求来时,优先在memtable中查找,然后按顺序从新到老查找磁盘上的segment文件
- 随着时间的推移,可以后台启动合并和压缩segment文件、丢弃或者覆盖掉老的文件
这种机制工作的很好,但是存在一个问题,如果数据库宕机了,内存中的memtable还没有来得及写入磁盘,可能造成数据丢失。可以通过一个单独的log文件来记录所有的操作,这个log的作用只是用于宕机恢复memtable,每当memtable被写入磁盘,相应的log文件就可以被丢弃了
用SSTables构建LSM-tree
LSM-Tree用于 LevelDB 和 RocksDB、嵌入其他应用的key-value存储引擎。参考:Google’s Bigtable paper
SSTables + log = “Log-Structured Merge-Tree ”
LSM-tree两种文件管理策略:
Size-Tiered Compaction(STC)
基本思想
“当某一层(或同一大小区间)的 SSTable 文件数达到阈值时,将它们合并成一个更大的文件。”
也就是说,不是按“层级”,而是按“文件大小”来触发 compaction。结构示意
MemTable → SSTable优点
- 写放大低
每条数据在被 flush 后只会参与少数几次合并;每次合并是“几块 → 一块”,合并次数较少。 - 写入吞吐高
flush 后直接落地;合并是批量异步进行;对写性能非常友好。
- 写放大低
缺点
- 读放大高
同一 key 可能存在于多个 SSTable 中;读时需要查多个文件(除非借助 Bloom Filter)。 - 空间放大高
合并不够积极;多个旧版本的数据同时存在;临时文件和重复 key 较多。
- 读放大高
应用场景
适合写多读少的场景;比如日志系统、时间序列数据库(TSDB)、写入密集的监控系统Cassandra 默认采用 STCS(Size-Tiered Compaction Strategy)
Level Compaction(LC)
这种策略是 LevelDB、RocksDB 采用的,更现代化。又叫 leveled compaction 或 分层压实。基本思想:
数据被组织成多个层级(Level 0, Level 1, Level 2…),每一层都有固定大小的空间限制,同一层内文件的 key 范围 互不重叠。层次结构:
Level 0: 多个小 SSTable,key 范围可能重叠。
Level 1: 较大文件,key 范围不重叠。
Level 2: 更大文件,key 范围不重叠。
…Compaction 逻辑:
当某一层(如 Level 0)容量超标;就选择一个 SSTable(或一组)与下一层(如 Level 1)中 key 范围重叠的文件;合并、去重、重写为更大的 SSTable,放入下一层。优点
- 读性能优异
除 Level 0 外,其余层内文件 key 范围不重叠;读取某个 key 只需查找每层最多一个文件;查找代价从 O(n_files) 降到 O(levels)。 - 空间利用率高
去重及时;每层占用空间接近固定比例;不容易膨胀。
- 读性能优异
缺点
- 写放大高
每条数据会多次参与合并(从 L0 → L1 → L2…);每次都要重写到下一层; 磁盘写入量是原始写入的数倍。 - Compaction 代价大
大文件之间的合并非常消耗 I/O;RocksDB 必须限制后台 compaction 线程数量。
- 写放大高
应用场景
适合读写比较均衡、查询多的系统;比如 RocksDB、LevelDB、TiKV、ClickHouse 的部分引擎。
性能优化
当查找的key在数据库中不存在时,LSM-Tree算法可能会变慢:在你确认key不存在之前,你需要检查所有memtable,所有sstable对应的磁盘文件。可以使用bloom filters:它可以检查key不在数据库中(每个sstable固化时,生成对应的bloom filter文件)