ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

同样是断电写了一半,为什么MySQL库毁人亡,Oracle却稳如泰山?

同样是断电写了一半,为什么MySQL库毁人亡,Oracle却稳如泰山? 为了省一半IO关掉这个参数一次断电足以让你提桶跑路每次做数据库性能压测或者遭遇写IO瓶颈时总有人会提出一个看似能“一键起飞”的方案关掉MySQL的innodb_doublewrite双写缓冲区。毕竟刷脏页时InnoDB要先把这页写进双写文件再写进真正的.ibd文件看着就像同一份数据傻乎乎地写了两遍。把这参数一关写盘压力瞬间掉一截监控曲线别提多好看了。但在普通的ext4或xfs文件系统上这一刀砍掉的可不止是IO而是数据库的保命底线。你赌的是断电那一瞬间16KB的数据刚好能一次性安全落地。一旦赌输了重启时这页的校验和Checksum对不上redo日志也补不上去你的数据库实例可能永远都起不来了。致命诱惑一份真实的Sysbench压测数据为什么总有人想关双写因为跑分压测的诱惑实在太大了。我们来看一份标准的纯写模型oltp_write_only的Sysbench真实压测数据。在8C16G服务器配合普通NVMeSSD环境下并发128个线程狂刷脏页测试指标开启双写(ON)关闭双写(OFF)性能变化QPS14,50017,100提升~18%95%响应延迟16.2ms12.8ms下降~21%磁盘平均写IO~310MB/s~175MB/s锐减~43%看懂这组数据你就明白为什么新人总想着关掉它了。仅仅改一行配置写IO几乎腰斩QPS还能白嫖将近20%的提升。在应对双十一或大促秒杀时这种“一键起飞”的诱惑极大。但命运赠送的礼物早就在底层标好了昂贵的价格。16KB的页磁盘根本不会一次写完很多人对底层IO有个错觉以为写盘是原子性的。InnoDB默认一页是16KB。但Linux操作系统文件系统的块通常是4KB底层的磁盘扇区更小以前是512字节现在多为4KB。这意味着数据库引擎发出去的一次16KB写入到了操作系统和磁盘底层往往会被拆成连续的几次小块写入。如果正好在写到一半时机房断电或内核崩溃比如前8KB是新数据后8KB还是旧数据磁盘上的这16KB数据就变成了一个怪胎新旧掺杂。在圈子里这叫残页(torn page)。它不是数据“有点旧”而是内部物理结构彻底错乱。重启时InnoDB一算校验和立刻认定这页坏了。页头和页尾各有一份LSN日志序列号这种拼盘数据这两处根本对不齐。醒醒redo log救不了残页“不是有redo log兜底吗怕什么断电”——这是我听过最多的外行话。redo记的是“在某一页上做了什么修改”。恢复时InnoDB得先把这页从数据文件读出来这页必须是物理结构完整的才能往上补修改。完整的旧页只是LSN旧一点redo可以顺着旧LSN接着补。但残页连这个起点都不是。InnoDB绝对不会把redo硬套到一页已经拼坏的乱码上那样套出来的东西只会更灾难。一句话总结redo管的是完整页上的修改还没刷盘残页是这页本身写到一半撕裂了。这是两码事。**刷盘之前InnoDB还会先看内存里这页头尾的LSN是否一致对不上它宁可自己宕机也绝不把坏页写进文件。双写到底在买什么保险虽然名字叫Doublewrite Buffer感觉像在内存里但真正在掉电后救命的是磁盘上的文件。在MySQL8.0.20以后去数据目录下看那些#ib_16384_0.dblwr文件就是它的真身。刷脏页是个严格的两步走流程顺序绝不能乱先写双写文件把这一批脏页顺序写进双写文件并落盘。这步是顺序IO一批页摊销一次fsync所以哪怕多写一遍性能也没掉到底。再写数据文件然后才把各页分散写回自己的.ibd文件。这步是随机IO。断电落在哪一步命运完全不同死在写双写文件时双写副本是残的但.ibd还没碰里面仍是完整的旧页。重启时丢掉残副本直接拿旧页加redo恢复。死在写.ibd文件时(最致命)数据文件里的页校验失败了。此时恢复机制会从双写文件里拿出那份完整的16KB替身盖回去日志里会打印正在从doublewrite file恢复然后再做redo。只有当两份都写残了才会起不来。虽然有双写也不是100%免死但它把致命的“数据文件写残”窗口缩小到了“双写好页能覆盖”的安全区里。Oracle凭什么不需要这层双写很多玩过Oracle的人会问Oracle常规运行怎么没有Doublewrite机制Oracle默认块通常是8KB写一半断电重启一样会报ORA-01578块损坏它怎么不怕其实Oracle采取的是“降维打击”的设计哲学它把“平时少写一遍”和“坏了怎么修”拆开了修的能力完全依托于其强大的生态工具依靠ADG(Active Data Guard)主库读到坏块直接通过网络向物理备库要这一块好的。备库传回来主库在内存里换上再补齐redo。业务侧毫无感知只是这条SQL慢了几十毫秒。依靠RMAN精确打击没有备库只有备份时直接一条RECOVER DATAFILE 4 BLOCK 1234;从备份集里精准捞出一个块盖回去。库都不用停别的文件照常读写。MySQL这种平民数据库默认跑在普通机器上没有RMAN块级恢复这种贵族工具。双写就是MySQL刷脏路径上自带的那个平民版“备用页”。别踩参数陷阱关了没事不代表安全在MySQL8.0里innodb_doublewrite这参数有不少坑。 默认是ON(或者DETECT_AND_RECOVER)双写里放整页坏了能盖回去。OFF就是直接裸奔。最坑的是**DETECT_ONLY**模式。它依然会写一个后缀为.bdblwr的文件但里面只有表空间号、页号和LSN没有那真实的16KB数据。断电重启时它能精准发现“页撕了”但因为没有整页数据可以盖回去启动流程会直接报错中止。你想兼顾少写IO又想断电自修这个参数做不到。最后别拿幸存者偏差来骗自己。“我关了几个月也没出事啊”——那只是因为断电的那一瞬间脏页刚好没有在往磁盘上写。一旦运气不好撕在没有完整副本的页上这页数据彻底报废接下来的烂摊子只能靠拉从库或者全量备份binlog去慢慢重做了。什么时候真能关 只有当你的底层盘能保证一次物理写16KB要么全在要么全不在时双写才是多余的。比如高端企业级NVMe(支持16KB原子写)或者ZFS这种按16KB对齐的写时复制(COW)文件系统。普通的消费级盘、云盘、ext4和xfs老老实实保持默认开启。为了降那一半IO去赌命真出了事谁也救不了你的数据。
返回列表