ddl在从库卡住sql线程本质是单线程串行重放导致停摆,因隐式flush tables with read lock使同库所有dml排队,且binlog中以query_log_event记录无法并发切片;有效方案是手动执行+校验一致性或改用gh-ost/pt-online-schema-change。

DDL在从库卡住SQL线程,本质是单线程串行重放
MySQL从库执行DDL时,SQL Thread会全程独占、阻塞所有后续事务——这不是“慢”,是“停摆”。SHOW PROCESSLIST里看到状态为altering table或Waiting for dependent transaction to commit,且Exec_Master_Log_Pos长时间不动,就是典型信号。
根本原因在于:DDL语句自带隐式FLUSH TABLES WITH READ LOCK语义,在slave_parallel_type=database模式下会强制整个库串行化。哪怕只改user_order一张表,同库下所有DML都得排队。
- DDL在binlog中以
Query_log_event记录(非row格式),无法按行级时间戳切片,logical_clock机制也无能为力 -
SHOW PROCESSLIST里永远只看到1个活跃SQL线程,哪怕你设了slave_parallel_workers=8 - 即使主库已写完binlog,从库relay log里也只有一条长事务记录,无法拆分并发
别信ALGORITHM=INPLACE,它不解决从库阻塞
主库加ALGORITHM=INPLACE, LOCK=NONE只能缓解主库锁表,对从库复制阻塞毫无帮助。从库重放时仍需获取相同MDL锁,若从库上有长查询正在读这张表,就会被阻塞。
更麻烦的是:主库执行完DDL才落binlog,从库必须等这个DDL完整执行完,才能处理后续所有事件。延迟不是累加的,是“堵死”的。
- 常见错误现象:
Seconds_Behind_Master突然跳到几百秒甚至上千秒 -
SHOW SLAVE STATUS\G中Slave_SQL_Running_State卡在altering table - 从库
innodb_flush_log_at_trx_commit=1或sync_binlog=1会进一步拖慢回放速度
真正有效的绕过方案:手动执行+校验一致性
与其等SQL Thread慢慢重放,不如把DDL从“被动重放”转为“主动绕过”。这是目前最可靠、影响最小的做法。
前提是:主从数据一致,且DDL本身支持LOCK=NONE(如加普通索引、末尾加列)。
- 先在从库执行
STOP SLAVE;,暂停复制 - 用
pt-table-checksum或mysqldiff确认master_pos_wait()位点前后表结构和数据一致 - 在从库手动执行相同DDL(注意检查是否真支持
LOCK=NONE,否则仍会锁表) - 执行
START SLAVE;恢复复制,观察Seconds_Behind_Master是否快速归零
注意:如果DDL涉及唯一索引、类型变更或非末尾加列,手动执行前必须确认从库没有长事务正在访问该表,否则一样会卡在Waiting for table metadata lock。
长期规避:用gh-ost或pt-online-schema-change替代原生命令
原生ALTER TABLE在主从架构下天然不适合大表DDL。工具的核心价值不是“更快”,而是“不依赖SQL Thread重放”。
gh-ost通过解析binlog流增量同步,pt-online-schema-change靠触发器捕获变更,两者都在主库完成全部操作,从库只需同步DML事件——DDL本身不进binlog,自然不卡SQL线程。
-
gh-ost更轻量,不依赖触发器,但要求主库开启binlog_format=ROW且log_slave_updates=ON -
pt-online-schema-change兼容性更好,但高频更新表上触发器可能引发锁争用 - 无论选哪个,都要提前在从库验证影子表切换阶段的短暂锁(
RENAME TABLE),确保业务能容忍毫秒级DML阻塞
DDL操作的复杂点不在语法,而在它如何与MDL锁、复制线程、binlog格式、事务隔离级别层层咬合。漏掉任意一环,LOCK=NONE就只是个幻觉。











