找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3420

积分

0

好友

470

主题
发表于 13 小时前 | 查看: 3| 回复: 0

到了2026年,AI真的能动手修改数据库内核这种核心代码了吗?为了验证这个想法,也为了探究一个困扰已久的技术问题,我用 Claude Code 实际操刀,把 PostgreSQL 的 Full Page Write (FPW) 机制替换成了 MySQL 的 Doublewrite Buffer (DWB),一番测试下来,性能差距竟达到了3倍。

起因

关于 Full Page Write 和 Doublewrite Buffer 这两种设计到底谁更合理,我想了挺久。之前在 pgsql-hackers 的邮件列表里发帖讨论过,但也没讨论出个所以然。

既然有疑问,最好的办法就是用代码和实验说话。正好,Claude Code 这类 AI 编码工具发展迅猛,何不试试看它能不能胜任修改数据库内核这种高难度任务?于是就有了这次从理论到代码、再到性能对比的完整实践。

Torn Page 问题:共同的敌人

无论是 PostgreSQL 还是 MySQL,它们都以“页”为单位管理数据,PostgreSQL 默认页大小是 8KB,MySQL InnoDB 默认是 16KB。然而,操作系统和磁盘的原子写入单元通常是 4KB(一个扇区大小)。

这就带来了一个问题:当数据库需要将一个完整的页(比如 8KB)写回磁盘时,实际上需要多次物理 I/O 操作。如果在这几次写操作之间系统断电或崩溃,磁盘上的页面就可能只写入了一部分——这就是 Torn Page(页撕裂)。新旧数据混杂在一起,这个页面就损坏了,无法保证数据一致性。

面对这个共同的敌人,PostgreSQL 和 MySQL 选择了完全不同的防御策略。

PostgreSQL 的策略:Full Page Write (FPW)

PostgreSQL 的解决方案是 Full Page Write。简单来说,在每次检查点(Checkpoint)之后,某个数据页的第一次被修改时,PostgreSQL 不仅会把“修改了什么”这条增量记录写入 WAL(Write-Ahead Log,预写日志),还会把整个页面的完整镜像也写入 WAL。

崩溃恢复时,如果发现某个数据页可能损坏(Torn Page),恢复进程就可以从 WAL 中找到这个页面的完整副本,直接覆盖掉坏页,然后再按顺序回放后续的 WAL 增量记录即可。这样一来,就解决了页撕裂问题。

但 FPW 带来一个非常棘手的问题:它让检查点的频率设置陷入两难境地。

  • FPW 希望少做检查点:因为每次检查点之后,大量数据页的首次修改都需要写入全页镜像到 WAL,导致 WAL 日志体积瞬间膨胀,写入性能会出现断崖式下跌。这也是为什么 PostgreSQL 的 checkpoint_timeout 参数最低也只能设到 30 秒(当然,检查点也可能因为 WAL 大小超过 max_wal_size 而触发)。
  • 数据库恢复理论希望多做检查点:检查点越频繁,崩溃后需要回放的 WAL 日志就越少,恢复速度就越快。

这两个目标在 FPW 机制下是直接冲突的:FPW 要求少做检查点来保证写入性能,而快速恢复要求多做检查点。

MySQL 的策略:Doublewrite Buffer (DWB)

InnoDB 存储引擎的思路则截然不同。它引入了一块独立的磁盘区域,称为 Doublewrite Buffer

当后台进程需要将脏页刷回数据文件时,它不是直接写入最终位置,而是分两步走:

  1. 先将一批脏页顺序地写入 Doublewrite Buffer 这块连续区域。
  2. 对 Doublewrite Buffer 调用一次 fsync(),确保这批页的副本已持久化。
  3. 再将这批页离散地写入各自在数据文件中的实际位置。

如果崩溃发生在第3步,导致某个数据页写入不完整(Torn Page),重启时 InnoDB 会检查 Doublewrite Buffer。只要在那里找到了该页的完整副本,就可以用它来修复损坏的数据页,同样解决了页撕裂问题。

为什么我认为 Doublewrite Buffer 设计更优?

1. 前台路径 vs 后台路径

如果我们忽略数据合并等细节,只考虑保证数据不撕裂的基本 I/O 开销:

  • FPW = 1 次 WAL 写入(前台) + 1 次数据页写入(后台)
  • DWB = 2 次数据页写入(都在后台)

虽然都是两次 I/O,但关键区别在于:FPW 的那一次“完整页写入”发生在前台的 WAL 写入路径上,这直接影响了用户提交 SQL 的延迟(Latency)。而 DWB 的两次写入都在后台刷脏路径上,对用户请求的直接影响要小得多。

2. 批量(Batch)优化的空间不同

DWB 的写入不需要每次调用 fsync(),可以积累一批脏页,写满一个 Buffer 后才同步一次,能更好地利用磁盘的顺序写入性能。WAL 写入虽然也能批量提交,但毕竟它处于事务提交的关键路径上,不能让用户等待过久,其批量优化的空间相对有限。

3. 不存在检查点频率的矛盾

DWB 机制不依赖检查点来防止页撕裂。这意味着 DBA 可以放心地提高检查点频率(例如,为了加速崩溃恢复),而不用担心会像 FPW 那样引发严重的写放大和性能下降。这是一个很大的设计优势。

性能测试对比

为了验证理论,我进行了实际的性能测试。

主要配置如下:

shared_buffers=4GB
wal_buffers=64MB
synchronous_commit=on
maintenance_work_mem=2GB
checkpoint_timeout=30s

每个测试场景都会重建数据库,测试前执行 VACUUM FULL 并进行 60 秒预热,每个负载运行 300 秒。

测试场景: IO 密集型,--tables=10 --table_size=10000000

测试结果图表如下,清晰展示了不同并发下三种模式的性能差异:

PostgreSQL FPW与MySQL DWB性能对比测试结果

详细的量化数据如下表所示:

场景 并发 FPW OFF (QPS) FPW ON (QPS) DWB ON (QPS) FPW OFF (TPS) FPW ON (TPS) DWB ON (TPS) FPW OFF (ms) FPW ON (ms) DWB ON (ms)
read_write 32 360,764 158,865 260,171 18,038 7,943 13,009 1.77 4.03 2.46
read_write 64 484,988 190,654 307,735 24,249 9,533 15,387 2.64 6.71 4.16
read_write 128 556,021 194,301 301,791 27,801 9,715 15,387 4.60 13.17 9.81
write_only 32 318,879 108,696 188,760 53,146 18,116 31,460 0.60 1.77 1.02
write_only 64 345,766 117,533 197,251 57,628 19,589 32,875 1.11 3.27 1.95
write_only 128 356,725 89,144 202,884 59,454 14,857 33,814 2.15 8.61 3.78

结果一目了然:

  • 以关闭 FPW 的性能作为基线(最佳)。
  • 开启 FPW 后,性能下降到基线水平的 ~25%
  • 而开启 DWB 后,性能可以保持在基线水平的 ~57%

尤其是在 write_only 128 并发的高压场景下,DWB 的吞吐量是 FPW 的 2.3倍,平均延迟也全面优于 FPW。这个性能差距相当显著。

代码实现与思考

这次内核修改的代码已经开源,你可以在 GitHub - baotiao/postgres 查看。整个修改过程主要依靠 Claude Code 完成,这本身也验证了 AI 辅助进行数据库内核级别代码修改的可行性,为开源实战和系统调优提供了新的思路。

从测试结果看,在本文设定的 IO 密集型场景下,DWB 机制相比 FPW 展现出了明显的性能优势,尤其是在高并发写入时。这主要得益于其将关键 I/O 压力转移到了后台,并且解耦了检查点与崩溃恢复完整性的绑定关系。

当然,数据库设计没有银弹。FPW 与 WAL 深度集成,逻辑上更简洁;DWB 作为独立模块,在特定负载下性能更优。这场“模块化”与“紧耦合”之间的设计哲学差异,以及它们在不同硬件(如 NVMe SSD)、不同工作负载下的表现,依然值得深入探讨。如果你对这类数据库底层机制对比或性能优化实践感兴趣,欢迎在 云栈社区 交流分享你的看法和经验。




上一篇:高性能HTTP服务器开发语言选型:C++、Golang、Rust、Java全方位对比与实战建议
下一篇:Etcd v3 API 增删改查详解:从基础命令到Kubernetes存储实战
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-10 18:06 , Processed in 0.309677 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表