订阅更新

LSM Tree 为什么写快读慢?读性能怎么优化?

面试问 353 约 2 分钟读完

随机一题
本文目录
  1. 面试怎么回答
  2. 写为什么快
  3. 读为什么慢
  4. 读怎么优化
  5. 三种放大的取舍

面试怎么回答

LSM 写入先写 WAL、再写内存中的 MemTable,满了刷成不可变的有序 SSTable,全程顺序写,所以写入快;代价是同一个 key 的多个版本分散在多个文件里,读要从新到旧查多个文件,有读放大。优化读主要靠后台 Compaction 合并文件、清理旧版本,加上每个 SSTable 的布隆过滤器跳过不可能包含该 key 的文件,以及块索引和 Block Cache。本质上 LSM 是在写放大、读放大和空间放大之间做权衡,Compaction 策略决定了偏向哪一边。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

HBase、RocksDB、Cassandra、Paimon 的底层都是 LSM Tree。面试官问:

LSM 为什么写得快?代价是什么?读慢怎么办?

写为什么快

LSM 把随机写变成顺序写:

  1. 写入先追加到 WAL(预写日志),保证宕机可恢复;
  2. 再写进内存里的有序结构 MemTable;
  3. MemTable 写满后,整体刷成磁盘上一个不可变的有序文件 SSTable。

整个过程没有原地更新,磁盘上只有顺序追加,所以写入吞吐很高。更新和删除也只是追加一条新版本或删除标记。

读为什么慢

同一个 key 的不同版本可能分散在 MemTable 和多个 SSTable 里。读一个 key 时,要从新到旧依次查找,最坏情况要查很多个文件,这就是读放大。

读怎么优化

  • Compaction:后台把多个 SSTable 合并成更少、更大的文件,同时清理旧版本和删除标记,减少一次读要查的文件数。
  • 布隆过滤器:每个 SSTable 带一个布隆过滤器,查询前先判断 key 是否「一定不在」这个文件里,大部分文件可以直接跳过。
  • 块索引和缓存:SSTable 内部按块存储并有索引,热点块放进 Block Cache,减少磁盘读取。
  • 范围查询:利用每个文件的最小、最大 key 先过滤掉不相关的文件。

三种放大的取舍

LSM 绕不开三种放大:

  • 写放大:数据被 Compaction 反复重写;
  • 读放大:一次读要查多个文件;
  • 空间放大:旧版本在合并前占着空间。

Compaction 越积极,读和空间越好,但写放大越高。不同的 Compaction 策略就是在这三者之间做取舍。