本文缺少实际的实践经验。全部来自在网上的“道听途说”和自己的“胡思乱想”。
基于 LSM-Tree 的存储系统越来越常见了,如 RocksDB、LevelDB。LSM-Tree 能将离散的随机写请求都转换成批量的顺序写请求(WAL + Compaction),以此提高写性能。但也带来了一些问题:
RocksDB 和 LevelDB 通过后台的 compaction 来减少读放大(减少 SST 文件数量)和空间放大(清理过期数据),但也因此带来了写放大(Write Amplification)的问题。
在 HDD 作为主流存储的时代,RocksDB 的 compaction 带来的写放大问题并没有非常明显。这是因为:
现在 SSD 逐渐成为主流存储,compaction 带来的写放大问题显得越来越严重:
所以,在 SSD 上,LSM-Tree 的写放大是一个非常值得关注的问题。而写放大、读放大、空间放大,三者就像 CAP 定理一样,需要做好权衡和取舍。
说明:RocksDB 支持多种 Compaction。下面分析的是 Level Style Compaction。
RocksDB 的写放大分析:
所以,总的写放大是 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
用户可以根据自己的场景需求调整上面的各个参数。