大表DDL让从库卡死几小时,因MySQL原生ALTER需持MDL锁且从库SQL线程串行执行,若表巨大、IO慢、buffer pool小,会停滞在altering table或Waiting for table metadata lock状态,Seconds_Behind_Master飙升至数万秒。

大表DDL为什么让从库卡死几小时
因为MySQL原生ALTER TABLE在主库执行时会持有元数据锁(MDL),binlog里这条语句仍以statement格式记录,哪怕binlog_format=ROW也无效。从库SQL线程必须串行执行完这个DDL才能继续——如果表有上亿行、IO慢、buffer pool小,它就真卡在altering table状态不动,Seconds_Behind_Master一路飙到几万秒,而SHOW PROCESSLIST里只看到一个Waiting for table metadata lock或copy to tmp table。
别直接在主库跑ALTER,改用gh-ost
gh-ost绕过MDL锁和单线程回放瓶颈,核心是不走MySQL原生DDL路径:
- 它在主库创建影子表,用轻量
INSERT/UPDATE/DELETE同步原表变更,从库只回放这些DML,天然支持并行 - 最终只做一次
RENAME,毫秒级原子切换,不阻塞主库写入 - 全程不依赖触发器,GTID模式下更稳定
必须满足的硬条件:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 主库开启
binlog_row_image=FULL - 操作用户要有
SELECT, INSERT, UPDATE, DELETE, CREATE, DROP权限 - 原表必须有唯一可排序索引(主键或
NOT NULL UNIQUE) - 命令里
--alter参数只写变更部分,例如--alter="ADD COLUMN status TINYINT",不带ALTER TABLE t1
如果必须用原生DDL,至少控制影响范围
线上实在没法切gh-ost,就得把风险收窄:
- 避开业务高峰,选凌晨低TPS时段执行
- 提前在从库上预热:
SELECT COUNT(*) FROM big_table触发buffer pool加载,避免DDL时大量磁盘读 - 临时调大从库资源:
innodb_buffer_pool_size加到物理内存70%,innodb_log_file_size翻倍减少刷脏压力 - 执行前停掉从库所有长查询,防止SQL线程被
Waiting for table flush卡住 - 监控
Relay_Log_Space增长速度,若持续上涨说明relay log写不过来,需调大relay_log_space_limit
并行复制不是万能解药,但能缓解DDL后的堆积
DDL本身无法并行,但它后面跟着的几十万条DML可以——前提是配置正确:
- MySQL 5.7+:设
slave_parallel_type = LOGICAL_CLOCK+slave_parallel_workers = 4~8,要求主库binlog_transaction_dependency_tracking = COMMIT_ORDER - MySQL 8.0+:优先用
WRITESET,需主库开binlog_transaction_dependency_tracking = WRITESET,对小事务加速明显 - 注意:如果DDL后紧跟着大批量
UPDATE,且没走索引,从库可能因全表扫描再次卡住,得先确保相关字段有索引
真正容易被忽略的是DDL执行期间的“静默延迟”:Seconds_Behind_Master可能还显示0,但SQL线程早已停在DDL上不动了,必须看SHOW PROCESSLIST里SQL线程的真实状态。

















