文章

如何正确地向 SSD 写数据:从跨层写放大到数据库与设备协同设计

拆解论文 How to Write to SSDs:为什么数据库应从原地覆盖写转向异地写入,以及压缩、页打包、按失效时间分组、NoWA、ZNS 与 FDP 如何协同压低跨层写放大。

如何正确地向 SSD 写数据:从跨层写放大到数据库与设备协同设计

原文:Lee, B., Ziegler, T., & Leis, V. (2026). How to Write to SSDs. Proceedings of the VLDB Endowment, 19(7), 1469-1483. DOI:10.14778/3801059.3801063

一句话结论

数据库不能继续把 SSD 当作“更快的磁盘”。要同时提高吞吐、减少介质磨损,就应让 DBMS 从原地覆盖写转向异地写入,并基于数据库语义主动塑造写入模式,联合控制数据库层和 SSD 内部的写放大。

论文解决了什么问题

数据库写一个页面时,真正落到闪存上的数据量往往远大于页面本身。论文把这条路径拆成两个连续的放大环节:

  1. 数据库层写放大(DB WAF):原地更新系统通常需要双写缓冲,以避免部分页写入造成数据损坏;异地写入系统又可能因垃圾回收而搬运仍然有效的页面。
  2. SSD 层写放大(SSD WAF):闪存按页写、按更大的块擦除。回收一个仍含有效页的块时,控制器必须先搬移有效数据,从而产生额外物理写入。

论文强调,最终影响吞吐与寿命的是两者的乘积:

\[\text{Total WAF} = \text{DB WAF} \times \text{SSD WAF}\]

因此,只优化其中一层并不可靠。数据库层减少了写入字节数,却可能改变写入分布、加剧 SSD 内部垃圾回收,最终让总写放大反而变差。

为什么“原地写入”是根本约束

原地写入把页面标识符固定映射到磁盘偏移,更新只能覆盖同一位置。这种设计有两个直接后果:

  • 为保证崩溃一致性,系统往往先保存安全副本,再覆盖目标页,形成接近 2 倍的数据库写入。
  • DBMS 无法决定新版页面实际写到哪里,也就无法按冷热、寿命或设备内部回收单元组织数据。

异地写入则先把新版页面写到新位置,确认持久化后再更新映射。旧版本在此之前仍然有效,因此可以依靠旧页与 WAL 恢复,双写缓冲不再必要。更重要的是,DBMS 获得了“写什么、何时写、写到哪里”的控制权。

这并不意味着“换成异地写入就自动更好”。实验中,朴素异地写入虽然去掉了双写,却因为数据库垃圾回收而使 DB WAF 从 2.00 上升到 4.06。论文真正的贡献,是补齐异地写入之后所需的一整套优化。

优化方案全景

层次技术主要目标核心机制
DBMS页面压缩减少写入量与数据占用每个 4 KiB 页面独立压缩
DBMS页面打包避免压缩后的读放大将多个压缩页装入对齐的 4 KiB 槽位,单页一次读取
DBMS按失效时间分组(GDT)降低数据库 GC 搬移量将预计在相近时间失效的页面写入同一区域
设备协同对齐 DB 与 SSD 的 GC 单元避免不同寿命数据落入同一物理回收单元用 FDP 的 RU 大小或探测结果设置数据库 zone 大小
普通 SSDNoWA 写入模式将 SSD WAF 压至接近 1管理并发开放 zone,检测频率失衡并做补偿写
ZNS SSDZone append/reset把设备 GC 上移给主机顺序追加,天然消除 SSD 内部写放大
FDP SSDPlacement hints避免内部多路混写用 RUH/Placement ID 隔离不同写流

关键技术一:页面压缩与页面打包

压缩为何必须与异地写入结合

4 KiB 页面压缩后通常小于 4 KiB,而且长度可变。如果仍按固定偏移原地覆盖,底层设备常常还是写满一个 4 KiB 单元,空间和写入量都没有真正下降。若改用可变偏移,页面变长或变短又会牵连相邻布局。

异地写入消除了这个限制:系统可以批量压缩页面,把压缩结果顺序写入新位置,再记录“页面 ID → 偏移与压缩长度”的映射。

论文给出的数据集压缩后占原始大小的比例为:

算法TPC-CYCSBEmailURLWiki
LZ449.0%41.2%40.1%25.0%31.3%
ZSTD36.5%34.4%27.4%14.5%17.8%

页面打包解决什么问题

压缩页若跨越 4 KiB 边界,一次逻辑读取可能触发两次物理读取。论文的 FIO 微基准显示,在测试的四款企业级 SSD 上,对齐的 4 KiB 随机读取具有最低延迟和最高吞吐。

作者采用 best-fit bin packing:把一批独立压缩的页面放进按 4 KiB 对齐的槽位,确保任一页面不跨槽。这样既保留压缩带来的写入与空间收益,又让每个页面只需一次 4 KiB 读取。

关键技术二:按“死亡时间”组织垃圾回收

异地写入会留下旧版本,因此数据库必须做垃圾回收。若一个回收区中有效页占比为 $v$,为了释放剩余空间,需要反复搬移有效页,近似写放大为:

\[\text{WAF} = \frac{1}{1-v}\]

例如有效页比例为 75% 时,回收产生的 WAF 为 4。问题不只是选择“无效页最多”的区域,还在于区域里是否混入了寿命差异很大的页面。

论文提出 Grouping by Death Time(GDT):预测页面下一次被覆盖的时间,把预计同时失效的页面放在一起。页面头部保存最近若干次写入时间戳,并用近期写入间隔估计下一次写入:

\[\text{EDT} = \text{current\_lsn} + \frac{WH_n-WH_1}{n-1}\]

写入时,系统选择平均预计失效时间最接近的 zone;回收时,也按预计失效时间重新排序和打包。目标不是精确预测每个页面,而是减少“很快失效的热页”和“长期有效的冷页”混装。

关键技术三:让数据库 GC 与 SSD 物理布局对齐

SSD 通常把并发写入追加到内部 superblock,再以厂商特定粒度回收。如果数据库 zone 比内部回收单元小,不同 zone 的页面就可能混在一个 superblock 中;即使数据库完整淘汰了某个 zone,SSD 的对应物理块仍有其他有效数据,仍需搬移。

作者提出两种确定 GC 单元的方法:

  • FDP 设备:直接读取 Reclaim Unit(RU)大小,并令数据库 zone 与其对齐。
  • 普通 SSD:逐渐增大 zone,用类似 ZNS 的单写流测试观察 SSD WAF 何时降到 1,据此估计内部 GC 粒度上界。论文测试中,多款普通企业级 SSD 的上界通常位于 4-8 GB;无法测量时,作者建议把 32 GB 作为保守上界。

关键技术四:NoWA 如何在普通 SSD 上抑制写放大

多线程会同时向多个 zone 追加数据。普通 SSD 看不到这些逻辑边界,会把不同写流交错放入多个 superblock,这被称为 multiplexing。如果同组 zone 的更新频率不同,一些物理块只会部分失效,SSD GC 就必须搬移有效页。

NoWA 用两条规则维持物理失效的均衡:

  1. 当前开放的 zone 未写满前,不开启下一组 zone。
  2. 追踪同组 zone 的失效频率;发现不平衡时,对更新不足的 zone 做补偿写,使相关物理块能够整体失效。

补偿写会略微增加 DB WAF,但把原本不可控的 SSD 内部搬移上移到了解工作负载的 DBMS 层。论文在六款不同厂商和容量的企业级 SSD 上都把 SSD WAF 降到约 1;作者也谨慎指出,固件调度、磨损均衡等行为仍可能使理论保证在某些设备上不完全成立。

ZNS 与 FDP 分别带来什么

ZNS

ZNS 要求每个 zone 内顺序写入,并把回收责任交给主机,因此设备内部 WAF 天然为 1。它还把普通 SSD 为内部回收预留的空间更多地暴露给主机,等价于给数据库 GC 更多余量。ZLeanStore 已经具有 zone、映射和 GC,适配工作主要是对齐 zone、使用 zone append,并在回收后 reset。

FDP

FDP 不强制 ZNS 的顺序写接口,而是暴露 RU 大小和多个 RU Handle。DBMS 给不同 zone 分配 Placement ID,让设备把写流送入不同 RUH,避免多路混写。这样无需 NoWA 的补偿写,也能让 SSD WAF 达到 1。

系统实现:ZLeanStore

作者把原本采用 B-tree 与原地写入的 LeanStore 改造成 ZLeanStore,主要改动包括:

  • 缓冲管理器:根据页面写入历史计算预计失效时间;
  • I/O 层:完成 LZ4 压缩、页面打包,并选择普通、ZNS 或 FDP 后端;
  • 空间管理器:维护页面到物理偏移的映射、反向映射与 zone 元数据;
  • 垃圾回收器:支持 greedy、GDT 和 NoWA-aware 策略;
  • 日志与恢复:先持久化页面,再提交映射更新;检查点保存映射表与 active-group 历史,恢复时重放 WAL。

代价是额外元数据与 CPU 开销。在设备完全使用的最坏配置中,额外内存可达 10.9 GB;全优化版本 CPU 使用率由原地写入的 5% 升至 8.3%。

实验设计

  • 设备:来自五家厂商的八款企业级 SSD,包括普通、ZNS 和 FDP 设备。
  • 服务器:AMD EPYC 9654P / 360 GB DRAM 与 Intel Xeon Gold 6342 / 500 GB DRAM。
  • 工作负载:YCSB-A(50% 读、50% 更新)和 TPC-C。
  • 内存压力:缓冲池为数据集的 5%-20%,确保超内存、I/O 密集。
  • 稳态要求:每次实验累计写入至少达到设备容量的 4 倍,主要报告最后一小时。
  • 指标:吞吐、DB WAF、SSD WAF、总 WAF、每操作逻辑/物理写入、命中率、CPU 与内存。

主要实验结果

800 GB YCSB-A:逐步加入优化

配置OPSDB WAFSSD WAF逻辑写入(B/op)物理写入(B/op)
原地写入229K2.002.361,8584,378
朴素异地写入230K4.061.943,7457,274
+ 压缩与页面打包380K0.621.95566566*
+ GDT458K0.591.965661,110
+ NoWA510K0.601.07567606
+ GC 单元对齐535K0.601.00567567

* 原论文表 1 的这一行同时给出 SSD WAF 1.95、逻辑写入 566 B/op 和物理写入 566 B/op,三者在算术上不能同时成立;结合图 13b,物理写入应在约 1,100 B/op 的量级。这很可能是原表的排版或录入错误,因此该单元格按原表保留,但不用于本文结论。

最终配置相对原地写入:吞吐从 229K 提升到 535K OPS,约为 2.34 倍;总 WAF 从 4.72 降至 0.60,约减少 7.8 倍

跨设备与跨工作负载

  • 六款普通企业级 SSD 上,总 WAF 相对原地写入减少 6.2-9.76 倍
  • 800 GB 数据集上,普通 SSD 的全优化异地写入相对原地写入吞吐约翻倍;ZNS 在相同数据集下进一步小幅领先。
  • FDP 测试中,NoWA 与 Placement Hints 都让 SSD WAF 达到 1;FDP 省去补偿写后,DB WAF 从 0.57 降至 0.54,吞吐从 541K 升至 553K OPS。
  • TPC-C、15,000 个 warehouse、两小时运行中,全优化异地写入完成的 new-order 事务为原地写入的 2.45 倍;相同事务量下,原地写入产生 7.2 倍闪存写入。

应如何理解这些结果

论文真正证明了什么

  1. 总 WAF 必须跨层测量。 只看主机写入量会漏掉 SSD 内部搬移,只看设备 WAF 又看不到双写和数据库 GC。
  2. 异地写入是必要条件,不是充分条件。 朴素转换可能更差;压缩、寿命分组和设备布局协同才构成完整方案。
  3. 压缩不仅少写数据,也改变 GC 的空间条件。 800 GB 数据压缩至 418 GB 后,设备可用余量显著增加,GC 有效页比例从 75% 降至 14%。
  4. 数据库语义是一种设备优化资源。 页面的访问历史、索引归属和预计寿命,是文件系统与 SSD 固件难以准确推断的。
  5. 新接口降低了猜测成本。 ZNS 直接交出布局与回收控制;FDP 则以较低侵入性提供回收单元与放置提示。

不能直接外推的地方

  • 实验主体是经过改造的 LeanStore,不能直接等同于 MySQL、PostgreSQL 或所有 B-tree 引擎的实际收益。
  • 工作负载集中于写密集型 OLTP,且缓冲池远小于数据集;内存充足或以读为主的场景,收益和 CPU 权衡可能不同。
  • 页面可压缩性决定了最大收益。不可压缩或已压缩数据不能复制同样的 WAF 降幅。
  • NoWA 依赖对设备内部粒度与行为的估计;消费级 SSD、复杂固件调度、热节流和磨损均衡可能改变结果。
  • 额外映射、GC 与恢复逻辑提高了系统复杂度,并带来内存和 CPU 成本。
  • 论文讨论“寿命延长”主要依据物理写入量下降推断,并非多年期实际失效测试。

对工程实践的启示

如果正在设计新的页式存储引擎,可以按以下优先级考虑:

  1. 先建立端到端测量:区分用户写、DBMS 额外写和设备物理写。
  2. 将页面寻址从固定偏移改为显式映射,为异地写入、压缩和布局控制创造条件。
  3. 先落地独立页压缩与 4 KiB 对齐打包,这通常是最大单项收益来源。
  4. 用热度或预计失效时间组织写入与 GC,避免冷热混装。
  5. 让数据库 GC 粒度匹配设备回收粒度;优先读取 FDP/ZNS 暴露的信息,否则通过稳态实验探测。
  6. 普通 SSD 再考虑 NoWA 类写流整形;FDP 设备优先使用 placement hints。
  7. 把映射更新、页面持久化和 WAL 的顺序纳入恢复设计,而不是在性能优化完成后补救。

总结

这篇论文的价值不只是提出若干 SSD 技巧,而是改变了优化边界:设备寿命与写入性能并非 SSD 固件单方面负责,DBMS 发出的写入模式才是上游变量。异地写入让数据库能够利用自身掌握的工作负载语义,压缩与 GDT 减少数据库层写放大,GC 单元对齐、NoWA、ZNS 和 FDP 则把设备层写放大压到接近最优。

最值得记住的一点是:减少“数据库写了多少”不等于减少“闪存实际写了多少”;系统应以总 WAF 为优化目标。

论文信息

  • Lee, B., Ziegler, T., & Leis, V. (2026). How to Write to SSDs. Proceedings of the VLDB Endowment, 19(7), 1469-1483.
  • DOI:10.14778/3801059.3801063
  • 论文代码与实验材料:ZLeanStore
  • 原文许可:CC BY-NC-ND 4.0。
本文由作者按照 CC BY 4.0 进行授权