Content Entry

LSM-Tree (RocksDB) 的写放大、读放大、空间放大(数据参考)

Published: 2021-12-22 Categories: NoSQL Tags: LSM rocksdb

写放大、读放大、空间放大

基于 LSM-Tree 的存储系统越来越常见了,如 RocksDB、LevelDB。LSM-Tree 能将离散随机写请求都转换成批量顺序写请求(WAL + Compaction),以此提高写性能。但也带来了一些问题:

  • 读放大(Read Amplification)。LSM-Tree 的读操作需要从新到旧(从上到下)一层一层查找,直到找到想要的数据。这个过程可能需要不止一次 I/O。特别是 range query 的情况,影响很明显。
  • 空间放大(Space Amplification)。因为所有的写入都是顺序写(append-only)的,不是 in-place update ,所以过期数据不会马上被清理掉。

RocksDB 和 LevelDB 通过后台的 compaction 来减少读放大(减少 SST 文件数量)和空间放大(清理过期数据),但也因此带来了写放大(Write Amplification)的问题。

  • 写放大。实际写入 HDD/SSD 的数据大小和程序要求写入数据大小之比。正常情况下,HDD/SSD 观察到的写入数据多于上层程序写入的数据。

在 HDD 作为主流存储的时代,RocksDB 的 compaction 带来的写放大问题并没有非常明显。这是因为:

  1. HDD 顺序读写性能远远优于随机读写性能,足以抵消写放大带来的开销。
  2. HDD 的写入量基本不影响其使用寿命。

现在 SSD 逐渐成为主流存储,compaction 带来的写放大问题显得越来越严重:

  1. SSD 顺序读写性能比随机读写性能好一些,但是差距并没有 HDD 那么大。所以,顺序写相比随机写带来的好处,能不能抵消写放大带来的开销,这是个问题。
  2. SSD 的使用寿命和其写入量有关,写放大太严重会大大缩短 SSD 的使用寿命。因为 SSD 不支持覆盖写,必须先擦除(erase)再写入。而每个 SSD block(block 是 SSD 擦除操作的基本单位) 的平均擦除次数是有限的。

所以,在 SSD 上,LSM-Tree 的写放大是一个非常值得关注的问题。而写放大、读放大、空间放大,三者就像 CAP 定理一样,需要做好权衡和取舍。

RocksDB 写放大简单分析

说明:RocksDB 支持多种 Compaction。下面分析的是 Level Style Compaction。

RocksDB 的写放大分析:

  • +1 - redo log 的写入
  • +1 - Immutable Memtable 写入到 L0 文件
  • +2 - L0 和 L1 compaction(L0 SST 文件的 key 范围是重叠的,出于性能考虑,一般尽量保持 L0 和 L1 的数据大小是一样的,每次拿全量 L0 的数据和全量 L1 的数据进行 compaction)
  • +11 - Ln-1 和 Ln 合并的写入(n >= 2,默认情况下,Ln 的数据大小是 Ln-1 的 10 倍,见 max_bytes_for_level_multiplier )。

所以,总的写放大是 4 + 11 * (n-1) = 11 * n - 7 倍。关键是 n 的取值。

假设 max_bytes_for_level_multiplier 取默认值 10,则 n 的取值受 L1 的大小和 LSM-Tree 的大小影响。 L1 的大小由 max_bytes_for_level_base 决定,默认是 256 MB。

默认情况下 L0 的大小和 L1 一样大,也是 256 MB。不过 L0 比较特殊,当 L0 的 SST 文件数量达到 level0_file_num_compaction_trigger 时,触发 L0 -> L1 的 comapction。所以 L0 的最大大小为 write_buffer_size * min_write_buffer_number_to_merge * level0_file_num_compaction_trigger

因此,RocksDB 每一层的默认大小为 : L0 - 256 MB L1 - 256 MB L2 - 2.5 GB L3 - 25 GB L4 - 250 GB L5 - 2500 GB

用户可以根据自己的场景需求调整上面的各个参数。

参考文档

comments loading