两种写路径的本质差异
存储引擎决定系统的读写放大特征。B 树类引擎在原位置更新数据页并写预写日志,点查与范围扫描稳定,适合频繁更新与事务;LSM 类引擎把写入追加到内存表并周期性刷成不可变文件,后台再合并,写吞吐高但读取需要跨多个文件查找。理解这一点后,很多「调优」就变成了常识判断:攒批、避免小文件、给查询留可裁剪结构。
引擎对照
| 引擎 | 写入路径 | 读取特征 | 代表系统 |
|---|---|---|---|
| B 树 | 原地更新页 + 预写日志 | 点查稳定、范围扫描友好 | WiredTiger、InnoDB |
| LSM / 分段 | 内存表追加写 + 后台合并不可变文件 | 写吞吐高,读需跨多文件合并 | Lucene 段、ClickHouse part、RocksDB |
| 混合 | 内存结构 + 定期持久化 | 读快,恢复与重建需要额外关注 | Redis 持久化、内存数据库 |
基本信息
| 写放大 | LSM 因反复重写文件而放大,B 树因页分裂与日志而放大 |
|---|---|
| 读放大 | LSM 需要查多个文件,常配布隆过滤器减少无效读取;B 树通常一次定位 |
| 空间放大 | LSM 在合并完成前会同时保留新旧数据 |
| 小批写入 | 分段类引擎最怕高频小批:合并压力与碎片显著上升 |
| 删除语义 | B 树原地删除;LSM 常以标记删除并由后台合并清理 |
实践启示
- 日志与事件流写多读少:分段类引擎更合适,注意攒批写入
- 账户余额与状态类频繁更新:B 树加事务语义更稳
- 避免高频单条写入分段引擎:按秒或按量攒批能显著降低合并压力
- 理解删除与更新成本:分段引擎里删除是一次写入,不是原地抹除
- 容量规划要算上合并期间的临时空间:空间使用会出现阶段性高峰
- 选型看访问模式而非宣传指标:同一份数据在两种引擎上的表现可能相反
写法启示
写法启示
# 写入吞吐优先、可接受读放大:日志与事件流
# Lucene 段 / MergeTree part 追加写,批量提交优于逐条提交
# 点查与更新优先:账户余额、订单状态
# B 树 + 事务,单行更新不必重写整个文件
# 同一系统内可以分层:
# 明细分批落盘,热数据放内存表或缓存把引擎模型当作选型的第一性判断:写多读少偏向追加写结构,更新密集且要求点查稳定偏向原地更新结构;实在分不清就用真实数据与真实读写比例做压测。
分段类引擎的空间占用会高于数据本身:合并未完成时新旧文件并存,容量按数据量估算容易出现「磁盘先满」的意外。