从库执行DDL更易锁表因单线程回放导致阻塞;gh-ost和pt-osc通过影子表实现主库无锁迁移,不解决已卡死DDL;从库卡死时应先诊断再谨慎KILL;推荐主库执行+延迟从库验证。

从库执行 DDL 为什么比主库更容易锁表
因为从库默认是单线程回放(尤其 MySQL 5.7 及更早版本),SQL 线程串行执行 relay log 中的事件;一旦遇到 ALTER TABLE 这类需要全表扫描或重建的 DDL,就会卡住整个复制流,后续所有语句都得排队等它完成。而主库上可能只是瞬间操作,但从库上可能持续几分钟甚至几小时——期间 show processlist 里能看到 SQL 线程状态为 altering table,且 show open tables where in_use > 0 会显示对应表被占用。
gh-ost 和 pt-osc 到底解决的是哪个环节
它们不直接“绕过锁”,而是把原生 DDL 拆成无锁增量迁移:新建影子表、同步数据、逐步追平变更、最后原子切换。关键点在于——全程不阻塞主库写入,也不要求从库停复制。但注意:gh-ost 默认只在主库上运行(通过监听 binlog 实现变更捕获),pt-online-schema-change 同样默认作用于主库。如果硬要在从库上跑,必须加 --recursion-method=none 并手动指定 --host,否则工具会尝试探测拓扑并报错。
-
gh-ost依赖BINLOG_FORMAT=ROW,且要求从库开启log_slave_updates才能用于从库场景(否则无法读取自身 relay log) -
pt-osc在从库执行时,触发器无法创建(从库只读),所以必须用--no-drop-triggers+--no-swap-tables组合,仅做数据拷贝,切换需人工介入 - 两者都不能解决“当前正在执行的 DDL 已卡死”问题——它们是预防手段,不是急救方案
从库 DDL 卡死时,别急着 kill,先确认是否真能切走
如果 ALTER TABLE 已在从库上跑了很久,直接 KILL 可能导致复制中断、relay log 损坏,甚至引发 Slave_SQL_Running: No。正确做法是:
- 先查
SHOW SLAVE STATUS\G,看Seconds_Behind_Master是否持续增长、Exec_Master_Log_Pos是否停滞 - 再用
SELECT * FROM information_schema.innodb_trx ORDER BY trx_started LIMIT 1;确认是否有长事务在阻塞 DDL(常见于未提交的UPDATE或DELETE) - 若确定是 DDL 自身卡住(比如磁盘 I/O 瓶颈或内存不足),且业务允许短暂延迟,可考虑临时停止复制:
STOP SLAVE;,然后KILL对应线程 ID(来自SHOW PROCESSLIST中Command=Query且State=altering table的那条) - 重启复制前,务必检查
relay-log.info或 GTID 位置是否一致,避免跳过未执行事件
真正想在从库做 DDL 迁移?优先改用主库+工具+延迟从库策略
生产环境极少需要“专门在从库执行 DDL”。更健壮的做法是:所有结构变更统一在主库用 gh-ost 或 pt-osc 执行,再让从库自然同步。如果担心主库压力,可搭配一个延迟从库(CHANGE REPLICATION SOURCE TO SOURCE_DELAY = 3600),等变更在延迟库验证无误后,再在其他从库上手动执行(此时已无风险)。强行在从库跑在线 DDL 工具,反而容易因权限、binlog 设置、复制过滤规则等细节翻车。
最常被忽略的一点:无论用哪个工具,innodb_lock_wait_timeout 值不能太小——如果设成 5 秒,而工具内部重试等待锁超时,可能反复失败又重试,最终拖慢整体进度。


















