doublewrite buffer 通过“先存副本、再写正本”的物理顺序拦截 partial page write:刷脏页时先将整页(16kb)拷入内存双写缓冲区,再分两次、每次1mb顺序写入磁盘双写文件(如ib_dblwr_1),fsync落盘后才随机写入目标.ibd文件;因双写区为连续空间、顺序i/o可靠,而目标写为随机i/o易中断,只要双写页写成功,断电后重启即可从中恢复完整页,从根本上避免页断裂落地。

Doublewrite Buffer 怎么拦住 partial page write
它不靠“检测”,而是靠“先存副本、再写正本”的物理顺序保障。InnoDB 刷脏页时,memcpy 把整页(16KB)拷进内存 doublewrite buffer,再分两次、每次 1MB 顺序写入磁盘上的双写区(如 ib_dblwr_1),调用 fsync() 落盘后,才开始把这页分散写到目标 .ibd 文件的随机位置。
关键点在于:双写区是连续空间,顺序 I/O 稳定;而目标数据文件是离散写,易受断电中断。只要双写区那 1MB 写成功了,哪怕后面写 .ibd 时断电,重启时也能从双写区原样恢复出完整页——页断裂根本没机会落地。
为什么 redo log 单独救不了断裂页
redo log 记录的是「对某页某 offset 执行了什么修改」,前提是该页结构完好、能读出页头、space id、page number 和 LSN。一旦发生 partial page write,页头可能被截断或校验和错,InnoDB 连这个页属于哪张表、上次更新到哪一步都解析不出来,redo log 直接失效。
此时没有 doublewrite buffer,就只能:
- 从磁盘读原始页(可能也是撕裂的)
- 再靠 redo log 重放——但起始状态已不可信,整个链路崩了
- 最终报错:InnoDB: Database page corruption on disk 或启动失败
MySQL 8.0.20+ 的双写文件位置变了,怎么看
老版本(ibdata1 开头;新版本默认启用独立双写文件,命名类似 ib_dblwr_1、ib_dblwr_2,位置由 innodb_doublewrite_dir 控制(若未设,则落在 datadir 下)。
确认方式:
- 查文件:ls -l $MYSQL_DATADIR/ib_dblwr_*
- 查元数据:SELECT * FROM information_schema.INNODB_TABLESPACES WHERE NAME = 'mysql/innodb_doublewrite';
- 若查不到记录,说明还在用旧模式(系统表空间内),或配置被覆盖
关闭 innodb_doublewrite 的真实代价是什么
不是“少一次写”,而是主动放弃页级原子性兜底。常见误判:
- “我用的是 NVMe SSD,断电不怕” → 错,SSD 内部也有写放大和掉电保护盲区,且 OS 层仍按 4KB 块刷盘
- “我有 RAID10” → RAID 只保块级原子,不保 16KB 页完整性
- “性能差一点无所谓” → 实测在高并发刷脏场景下,关闭后 Innodb_buffer_pool_wait_free 上升,反而拖慢整体吞吐
真正只该关的场景极少:只读归档库 + 每日全量备份 + 故障演练验证过恢复路径。其余一律保持 innodb_doublewrite = ON。
最容易被忽略的一点:配置文件里写了 innodb_doublewrite = ON 不等于生效。必须运行时检查 SHOW VARIABLES LIKE 'innodb_doublewrite' 和 SHOW STATUS LIKE 'Innodb_dblwr%',否则崩溃时才发现双写没开,已经晚了。











