花拾录
← 返回知识库

从零理解 B 树与 LSM 树的写入放大:为什么写多的场景不该照搬 MySQL 的索引结构

数据库AI2026/09/270 阅读0 评论

引言

在数据库选型时,MySQL 的 InnoDB 存储引擎常被当作默认选择。但你是否想过:为什么写多的场景(如日志、时序数据)往往推荐使用 LSM 树(Log-Structured Merge Tree)而非 B 树?核心原因之一是写入放大。本文从零解释两者的差异,并说明为什么不能照搬 MySQL 的索引结构。

什么是写入放大?

写入放大(Write Amplification)指实际写入存储设备的数据量与应用逻辑写入数据量的比值。例如,应用写入 1MB 数据,但存储引擎实际向磁盘写了 10MB,写入放大就是 10。放大越高,磁盘寿命越短,写吞吐越低。

B 树(以 MySQL InnoDB 为例)的写入行为

B 树是一种平衡多路搜索树,数据按主键有序存储在页(通常 16KB)中。写入时:

  1. 定位到目标页,若页在内存(缓冲池)中则直接修改。
  2. 若页不在内存,需从磁盘读取该页(随机读)。
  3. 修改后,页变为“脏页”,后续由后台线程刷回磁盘。
  4. 若页写满,触发页分裂,导致更多页的修改。

写入放大的来源:

  • 随机写:每次写入可能涉及不同页,导致大量随机 I/O。
  • 页分裂:分裂产生额外的写操作。
  • 双写缓冲(Doublewrite Buffer):为防止页断裂,InnoDB 先将页写入双写缓冲,再写入数据文件,放大至少 2 倍。
  • 刷脏页:即使只修改几个字节,也必须写回整个 16KB 页。

因此,B 树在写多场景下写入放大较高,且随机写对机械硬盘不友好,对 SSD 也有磨损。

LSM 树的写入行为

LSM 树采用“追加写”思想:

  1. 写入先记录到 WAL(预写日志)保证持久性。
  2. 然后写入内存中的 MemTable(通常用跳表)。
  3. MemTable 写满后,冻结为 Immutable MemTable,并后台刷盘成 SSTable(有序字符串表)。
  4. 后台定期合并(Compaction)多个 SSTable,消除冗余、保持有序。

写入放大的来源:

  • Compaction:合并过程会反复读写数据,是主要放大源。
  • 但写入路径本身是顺序追加,没有随机写和页分裂。

相比 B 树,LSM 树将随机写转化为顺序写,大幅提升写吞吐,尤其适合写多读少或写多读也多的场景(通过布隆过滤器、缓存优化读)。

为什么写多场景不该照搬 MySQL 的索引结构?

  1. 写入模式不匹配:MySQL 的 B 树索引适合读多写少、点查和范围查。写多时,随机 I/O 和页分裂成为瓶颈。
  2. 写入放大差异:B 树写入放大通常高于 LSM 树(尤其在高并发写入时)。LSM 树通过顺序写和批量合并,能更好地利用磁盘带宽。
  3. 硬件适应性:现代 SSD 虽然随机写性能提升,但顺序写仍更快、更省寿命。LSM 树更契合。
  4. 场景案例:日志收集(如 Elasticsearch 的 Lucene 段合并)、时序数据库(如 InfluxDB)、宽列存储(如 HBase)均采用 LSM 树变体,而非 B 树。

总结

  • B 树:读优化,写入放大高,适合读多写少、事务型场景(如 MySQL)。
  • LSM 树:写优化,写入放大主要来自合并,适合写多、高吞吐场景(如日志、时序)。
  • 选型时应根据读写比例、延迟要求、硬件特性决定,而非盲目照搬 MySQL 的索引结构。

理解写入放大,能帮助你更理性地选择存储引擎。

评论(0)

  • 还没有评论,来抢沙发~

相关文章