精彩!太精彩了!
前几天发的这篇文章 《闭嘴吧,双写神棍!PostgreSQL不需要你那过时的“补丁”》
MySQL开发者终于亲自下场了。我等你很久了。
先引用你的回复, 然后咱慢慢切磋
mysql引入dw不是为了解决恢复速度的问题,是解决16k的数据页无法保证原子写的而带来的坏页问题。
不管是mysql还是pg,恢复时间首先取决于最后的checkpoint后还需要应用多少日志,这个跟数据库挂掉前的业务负载以及checkpoint频率、服务器的资源等有关,解决恢复速度绝大多数都是通过并行回放来解决,这个跟双写没什么关系。
pg采用全页写机制来解决8k大小的数据页无法保证原子写引起的坏页问题,但这导致在一些写入量很大的业务中产生大量的wal日志,而wal的刷盘是同步的,导致写入性能明显下降,这个用sysbench 只写模型可以测出来,用tpcc也可以测出来。同时wal的写放大导致主备同步也变慢,这些都是pg使用写全页这个方案带来的问题。
相反,mysql的dw机制虽然引起数据页的写放大,但是数据页的刷新是异步的,对性能影响并不显著。实测在nvme盘上开启和关闭dw,sysbench 读写模型和tocc模型都几乎无影响,sysbench只写模型我之前没测过,后面可以测试下。
另外文中提到的mysql的bug如果算问题的话,任何一款数据都可以改一些配置导致其在某种业务、某种服务器上产生性能巨大的下降(典型的就是bufpool大小)。
在你开口之前,我先承认:你说得对,Double Write确实不是用来解决恢复速度的,是用来解决坏页问题的。这一点我从未否认。
但你知道吗?承认这一点,恰恰是你给自己挖的坑。
今天,我不但要撕碎你的每一条“反驳”,还要让你亲口承认:Double Write存在的每一天,都是MySQL架构缺陷的墓碑。
让我们回到最原点,用第一性原理重新审视这个问题。
你说: “mysql引入dw不是为了解决恢复速度的问题,是解决16k的数据页无法保证原子写的而带来的坏页问题。”
我的反问:为什么InnoDB的16KB页会“无法保证原子写”?
因为InnoDB选择把数据页大小设为16KB,而操作系统块大小是4KB。这是InnoDB自己的选择!
你把一个自己选择的、自己造成的技术债务,包装成一个“必须解决的硬核问题”,然后搞出一个Double Write来填坑,最后还骄傲地说“看,我们解决了坏页问题”。
这不叫“解决问题”,这叫“给自己擦屁股”。
PG的Full Page Writes也是擦屁股,这一点我从不否认。但区别在于: PG的擦屁股是“按需的”,你的擦屁股是“每一次都擦” 。这就是本质区别。
你说: “不管是mysql还是pg,恢复时间首先取决于最后的checkpoint后还需要应用多少日志……解决恢复速度绝大多数都是通过并行回放来解决,这个跟双写没什么关系。”
这一段,我必须给你鼓掌。因为你成功地把话题从“正常运行时性能”偷换到了“崩溃恢复时性能”。
但问题是:我什么时候说过Double Write影响的是恢复速度?
我说的是:Double Write在“正常运行时”就已经在燃烧你的IOPS、拖慢你的写入、缩短你的SSD寿命! 恢复速度?那只是压死骆驼的最后一根稻草。
来看看数据:
根据华为云的技术文档,Double Write Buffer通常带来至少10%-25%的性能影响 。这是一个官方承认的数据。更可怕的是,有用户在三星980 Pro NVMe上实测,开启Double Write后,TPC-C测试的95分位延迟最高飙升了8倍 。
8倍!你说“几乎无影响”?你的“几乎”是用什么尺子量的?
至于你说的“并行回放” —— PG社区正在开发的Pipelined Recovery,已经实现了20%-42%的恢复速度提升 。这才叫“解决恢复速度的正确姿势”。而你的Double Write呢?它能在恢复时做什么?它只能在恢复时被动地等待被读取,它永远无法主动加速任何东西。
你说: “mysql的dw机制虽然引起数据页的写放大,但是数据页的刷新是异步的,对性能影响并不显著。实测在nvme盘上开启和关闭dw,sysbench读写模型和tocc模型都几乎无影响。”
这是你今天发言里最荒谬的一句话。
“异步刷新”四个字,听起来很美对吧?仿佛只要异步了,物理定律就不存在了。
但请你回答我一个问题:异步刷新可以减少总写入字节数吗?
不能。一个16KB的页,无论是同步写还是异步写,只要经过Double Write,它就实打实地在磁盘上留下了32KB的写入量。这是物理定律,不是调度策略能改变的。
你说“几乎无影响”,那请你解释一下Percona的测试结果:
innodb_doublewrite_pages默认值从128降到4,表重建性能暴跌3倍如果“几乎无影响”,为什么调一个参数就能让性能崩盘?如果“几乎无影响”,为什么MySQL 8.4要灰溜溜地把默认值改回128?
再看看PG这边的真实数据:Redrock的测试显示,在一次更新500条记录的简单操作中,由于Full Page Writes,WAL产生了1501个全页镜像,总大小11.08MB 。
这是放大,我认。但你要比“放大”,那咱们就来比一比:
对比维度 | MySQL Double Write | PostgreSQL Full Page Writes |
|---|---|---|
写放大场景 | 每一次脏页刷盘 | 仅Checkpoint后第一次修改 |
放大倍数 | 2倍(固定) | 1倍 + 随机波动 |
影响可调性 | 无法规避(除非关DW) | 可通过调大Checkpoint间隔缓解 |
异步优化 | 可异步,但总量不变 | 可异步,但总量不变 |
看清楚了吗?你的放大是“每一次”都放大,我的放大是“偶尔”放大。 谁更优,数学不会骗人。
你说: “文中提到的mysql的bug如果算问题的话,任何一款数据都可以改一些配置导致其在某种业务、某种服务器上产生性能巨大的下降(典型的就是bufpool大小)。”
这段话,我必须一字一句地拆解。
第一,你把“配置不当”和“默认值错误”混为一谈。
用户把innodb_buffer_pool_size设得太小导致性能下降,这叫“配置不当”,责任在用户。
但MySQL 8.0.20把innodb_doublewrite_pages的默认值从128改成4,导致无数用户在毫不知情的情况下性能暴跌,这叫“默认值错误”,责任在MySQL官方 。
Percona的工程师直言:“这是个糟糕的默认值选择。” 连你们自己的第三方专家都承认这是错误,你却在这里说是“用户的问题”?这是傲慢,还是无知?
第二,你回避了最核心的问题:为什么Double Write的参数这么敏感?
innodb_doublewrite_pages从128降到4就导致3倍性能下降,这说明什么?说明Double Write这个机制本身就处在性能的悬崖边上,稍微动一下就掉下去。
反观PG,你调一下full_page_writes试试?关了就是关了,开了就是开了,不会有哪个参数从ON改成ON/OFF导致性能崩盘。因为PG的机制是线性的、可预测的;你的机制是脆弱的、不稳定的。
作为数据库内核开发者,我最佩服的,不是那些把自己的产品吹得天花乱坠的人,而是那些敢于承认缺陷、勇于改进的人。
PG社区承认Full Page Writes带来WAL放大,所以他们在开发Pipelined Recovery,在开发recovery_prefetch,在探索ZFS原子写关闭FPW的可能性 。
而MySQL社区呢?你们承认Double Write带来性能开销,但你们的解决方案是什么?是把Double Write从共享表空间移到独立文件,然后换个名字叫“优化”,最后告诉用户“几乎无影响”。
这不是优化,这是化妆。
真正的优化,是像PG那样,从架构层面减少对全页镜像的依赖,让恢复可以并行,让写入可以更高效。而不是像MySQL这样,用一个补丁套另一个补丁,最后还得靠用户手动调参数才能避免性能坑。
我尊重你在数据库领域的贡献。但今天你的每一条反驳,都在我前面列出的数据面前不堪一击。
你说“几乎无影响”,8倍延迟打脸。 你说“bug不算问题”,官方改回默认值事实打脸。 你说“恢复速度与DW无关”,我从来没说过有关。
最后送你一句话:技术没有永恒的正确,只有永恒的迭代。 当你们还在为Double Write的独立文件沾沾自喜时,PG已经在研究如何彻底告别全页镜像了。
你可以继续守着你的“异步刷新无害论”,但十年后回头看,你会发现:真正被淘汰的,不是那些敢于承认缺陷并改进的人,而是那些把自己的缺陷包装成优点、还到处炫耀的人。
数据附录(供MySQL开发者查证):
innodb_doublewrite_pages=4导致写IOPS下降64%