主库秒级,从库延迟2小时:深挖Online DDL回放的MDL阻塞与算法降级机制

大家好,我是小耶,写功课只是为了我踩过的坑,你们别再踩了!

前阵子遇到一次极其典型的生产事故:凌晨2点,我在主库给一张8000万行的核心交易表加了个字段。

使用的是 MySQL 8.0 的 Online DDL,ALGORITHM=INSTANT,主库毫秒级完成。当时我看了一眼监控,主从延迟是0,就安心睡觉了。

结果早上9点,业务群炸了:"从库读出来的数据全是旧状态,报表严重失真!"

登录从库一看,SHOW SLAVE STATUS 里的 Seconds_Behind_Master 赫然显示:7200秒 。而 SHOW PROCESSLIST 显示,从库的 SQL 线程一直卡在 Waiting for table metadata lock。

主库秒级完成的 DDL,为什么到了从库就变成了一场灾难?今天我们把这件事的底层链路彻底拆开。

一、深层剖析:从库的 MDL 锁雪崩

在 ROW 格式的 Binlog 下,主库的 DDL 会作为一个 Query_event 写入 Binlog。从库的 SQL 线程在读取到这个事件并尝试重放时,必须经历以下链路:

  1. 申请 MDL 排他锁 :从库执行 ALTER TABLE 前,必须获取该表的 MDL X锁。

  2. 阻塞后续所有事务 :由于从库的 SQL 线程(如果是单线程,或者遇到跨线程的表级依赖)正在持有这张表的 MDL X锁,所有针对这张表的后续 DML 操作(INSERT/UPDATE/DELETE)在回放时,统统只能进入 MDL 的 PENDING 队列排队。

  3. 雪崩效应:主库还在持续不断地产生新的 Binlog(包含大量针对该表的 DML),从库 SQL 线程卡在 DDL 上,后面的日志堆积如山。

为什么 Seconds_Behind_Master 没提前报警?

如果在 DDL 执行前,从库已经处于空闲状态(延迟为0),DDL 事件到达后,从库开始卡住。此时 Seconds_Behind_Master 可能会显示为0或很小的数字,直到主库有新的事务过来,时间戳开始产生差值,这个数字才会开始飙升。这就导致了一个监控盲区。

二、隐秘的角落:INSTANT 算法为什么会"悄悄降级"?

我明明写了 ALGORITHM=INSTANT,主库也确实是秒完的,为什么从库会卡这么久?

MySQL 8.0.12 引入的 INSTANT 算法极其严苛。在以下场景中,即使你指定了 INSTANT,MySQL 也会静默降级为 INPLACE 甚至 COPY,且不报错:

  • 表包含全文索引(FULLTEXT)或空间索引(SPATIAL)。

  • 在表的中间位置添加列(8.0.29之前)。

  • 修改列的数据类型、字符集。

如果是 COPY 算法,意味着主库和从库都需要新建一张临时表,把原表数据逐行拷贝过去。8000万行的表,拷贝一次可能耗时十几分钟,这期间从库的 MDL 锁死死不放,主从延迟瞬间爆炸。

实操验证:如何防止算法降级?

不要盲目相信自己的 SQL,在执行 DDL 时,强制带上 ALGORITHM 和 LOCK 子句:

bash 复制代码
ALTER TABLE orders ADD COLUMN remark VARCHAR(100), ALGORITHM=INSTANT, LOCK=NONE;

如果当前表结构不支持 INSTANT,这条语句会直接报错退出。宁可报错,也不要让它悄悄降级导致主从瘫痪。

三、实操解法:大表 DDL 的正确打开方式

对于千万级以上的大表,哪怕支持 INSTANT,也建议走以下方案:

方案一:使用 gh-ost 规避锁等待(大厂标配)

gh-ost 的核心原理是:创建幽灵表 -> 模拟从库拉取 Binlog -> 应用增量 -> 原子 Rename。

深层机制 :gh-ost 在主库上执行的是大量的 INSERT INTO ghost_table SELECT ... 的按行拷贝,产生的 Binlog 都是正常的 DML 操作。从库在回放这些 DML 时,不会触发表级 MDL 排他锁,因此业务读取正常,延迟极小。

但要注意 gh-ost 的限流机制 。它默认会检查主库的 Threads_running,如果超过阈值就会暂停拷贝。你可以通过 --max-load 参数调整,避免拷贝打满主库 CPU。

方案二:利用主备倒换(Smart DBA 的杀手锏)

如果是在云环境或有成熟的高可用架构(如MHA/Orchestrator),可以采用"切主"方案:

  1. 在业务低峰期,主动触发一次主备切换。原备库提升为主库(承接读写业务)。

  2. 原主库(现已被降级为备库),它上面的数据与主库是完全同步的。

  3. 在这个"新备库"上执行你的 DDL 操作。因为它不承载业务读取,你可以随便锁表、随便跑 COPY 算法,不会影响业务。

  4. 等 DDL 执行完毕,主从延迟追平后,在下一个低峰期,再把它切回主库。

方案三:精准延迟监控(替换掉不可靠的 Seconds_Behind_Master)

既然 Seconds_Behind_Master 有盲区,做大表 DDL 时,必须开启 pt-heartbeat 来监控真实的延迟:

bash 复制代码
# 在主库启动心跳写入
pt-heartbeat --user=root --password=xxx --create-table --database=test --update --daemonize

# 在从库监控真实延迟
pt-heartbeat --user=root --password=xxx --database=test --monitor --print-master-server-id

它会每秒在主库写入一个带时间戳的探针,从库读取后对比当前时间,哪怕从库没写入,你也能精准看到当前回放落后了多少秒。一旦超过阈值,立即通过 kill 命令终止从库的 DDL 回放,防止雪崩。

四、小结

不要被主库上 Online DDL 的"秒级"现象迷惑。在主从架构下,DDL 回放涉及到 MDL 锁的排他性、算法的降级以及并行复制的失效。对于大表,尽量避免在主库直接执行 DDL;优先使用 gh-ost 或"主备倒换";并在 DDL 期间用 pt-heartbeat 替换掉原生的延迟监控。宁可提前排查,不要等到业务大面积超时再来救火。

小耶在手,SQL 不愁

还有什么想了解的,欢迎留言!小耶一定知无不言言无不尽......我们下次见~

相关推荐
郭小李1 小时前
mybatis 动态 sql
java·sql·mybatis
xcLeigh1 小时前
KingbaseES数据库教程:初识数据库,从安装到跑通第一条SQL
android·数据库·sql·安装·数据库教程·kes
知行产研1 小时前
解读〈工程机械行业“十五五“发展规划〉:从卖整机到卖解决方案
数据库·mysql
handsome_sai1 小时前
SQL Server 快速入门
数据库·sqlserver
底层玩家老张1 小时前
RAG向量库拆分复盘:内存、延迟与权限的三笔账
数据库·向量数据库·向量检索·ai数据库
莫晓燚2 小时前
阿里云若依项目部署与日志采集---实战
运维·数据库·redis·阿里云·ruoyi
香蕉大香蕉(06敬畏市场)2 小时前
MySQL 单点详解(08)
java·数据库·mysql·面试·八股
l1t3 小时前
DeepSeek总结的如何在 PostgreSQL 中删除一张 4 TB 的表
数据库·postgresql
ClouGence3 小时前
开源数据库管理工具 CloudDM 发布新版本,支持团队多级审批工单,无需依赖第三方系统
数据库·sql·开源