从库sql thread因ddl串行执行而停摆,根本原因是mysql 5.7+复制机制中ddl强制单线程重放:binlog仅以statement格式记录,不支持并行切片,且隐式mdl锁使所有事务退化为单worker处理。

ALTER TABLE 在主库执行后,从库不是“慢”,而是“停摆”:SQL Thread 被独占占用,后续所有事务排队等待,Seconds_Behind_Master 持续上涨、Exec_Master_Log_Pos 长时间不动——这是典型的从库 DDL 阻塞现象,根本原因不在性能,而在 MySQL 5.7+ 的复制机制设计。
为什么从库 SQL Thread 必须串行执行 DDL
DDL 在 binlog 中只记录为一条 Query_log_event(statement 格式),不支持按行切片或时间戳排序。即使你设置了 slave_parallel_type='LOGICAL_CLOCK',该机制只对 DML 有效;DDL 自带隐式 FLUSH TABLES WITH READ LOCK 语义,会触发全库级 MDL 锁升级,强制把当前库下所有 pending 事务路由到同一个 worker,实际退化为单线程。
常见误判点:
-
slave_parallel_workers=8对 DDL 完全无效 ——SHOW FULL PROCESSLIST里永远只看到 1 个活跃 SQL 线程在跑altering table -
binlog_format=ROW无法改变 DDL 的记录方式 —— DDL 始终走 statement,和 row/image 设置无关 -
SHOW VARIABLES LIKE '%slave_parallel_type%'返回logical_clock≠ DDL 能并行,只是让 DDL 前后的 DML 有机会插队
如何快速确认是 DDL 导致的阻塞
别只看 SHOW SLAVE STATUS\G,重点查实时状态:
-
Slave_SQL_Running_State显示altering table或Waiting for dependent transaction to commit -
SHOW PROCESSLIST中 SQL Thread(通常 ID 较小)状态卡住,且Time持续增长 -
SELECT * FROM sys.schema_table_lock_waits WHERE waiting_query LIKE 'alter%';—— 直接定位阻塞源头,字段sql_kill_blocking_connection给出可 kill 的线程 ID - 无
sys库时:用performance_schema.data_lock_waits+data_locks关联查blocking_trx_id和对应线程
绕过阻塞的实操路径只有两个可靠选项
调参数不能根治,必须改执行逻辑:
- STOP SLAVE 后手动执行 DDL:需提前校验主从数据一致性(如
pt-table-checksum),执行前设SET sql_log_bin = 0,避免写入主库 binlog;注意业务是否允许短暂主从结构不一致 - 用
gh-ost替代原生 DDL:它连接主库,在主库建影子表、监听 binlog 增量同步,从库只回放轻量 DML 和最终RENAME—— 从库完全不执行原 DDL;前提:主库binlog_row_image=FULL,且目标表有唯一可排序索引(如主键或非空唯一键)
最容易被忽略的细节
DDL 阻塞不是孤立事件。一个 ALTER TABLE 可能因上游未提交事务而卡在“Waiting for table metadata lock”,此时从库重放的不是 DDL 本身,而是那个被阻塞的等待状态——你看到的“卡住”,源头可能在主库一个忘了 COMMIT 的长事务里。所以查从库阻塞前,先去主库跑一遍:SELECT * FROM information_schema.INNODB_TRX WHERE TIME > 60,看有没有 running 超 1 分钟的事务。











