ALTER TABLE在生产库易锁表拖垮服务,因旧版本全表复制加写锁,新版本虽支持INPLACE但仍可能触发重建或MDL锁;pt-osc和gh-ost可规避锁表,但需严格匹配版本、binlog格式及参数配置。

为什么 ALTER TABLE 在生产库上会锁表甚至拖垮服务
MySQL 5.6 以前的 ALTER TABLE 基本等于“复制原表 + 改结构 + 切换”,期间对原表加写锁,大表一改就是几小时不可写。5.7+ 虽支持部分 ALGORITHM=INPLACE 操作,但像加列、改列类型、加索引等仍可能触发重建,且是否真正免锁取决于存储引擎和具体操作——InnoDB 对某些 DDL 仍需元数据锁(MDL)阻塞后续查询。
常见错误现象:SHOW PROCESSLIST 里卡着一堆 Waiting for table metadata lock;应用报 Lock wait timeout exceeded;从库延迟突然飙升。
- 别信“小表就没事”——哪怕只有 10 万行,如果并发高或有长事务,
ALTER仍可能被堵住 -
pt-online-schema-change和gh-ost是目前最成熟的两个方案,原理都是“不锁原表”,但实现逻辑差异大,选错容易翻车 - 务必确认 MySQL 版本和 binlog 格式:
gh-ost要求binlog_format=ROW,pt-osc在MIXED下可能出错
gh-ost 怎么跑才不丢数据、不卡主库
gh-ost 用的是“读取 binlog + 拦截写入 + 异步回放”的方式,不依赖触发器,对主库压力更小,但配置稍复杂。它本质是启动一个影子表,把原表数据拷过去,同时监听 binlog 补增量,最后原子切换。
- 必须加
--allow-on-master才能在主库直接运行(默认只允许在从库试跑) - 关键参数要设对:
--max-load="Threads_running=25"防止拷贝时压垮主库;--critical-load="Threads_running=50"到阈值自动暂停 - 切表前会停写一小段时间(毫秒级),但如果你的业务有强一致性要求(比如金融记账),得提前在应用层做写保护,不能只靠
gh-ost自身的--postpone-cut-over-flag-file - 示例命令:
gh-ost --host=127.0.0.1 --user=root --password=xxx --database=testdb --table=t1 --alter="ADD COLUMN c2 INT" --allow-on-master --cut-over-default-policy=two-step --initially-drop-ghost-table --initially-drop-old-table --max-load="Threads_running=25"
pt-online-schema-change 哪些坑最容易踩
pt-osc 用触发器捕获变更,简单直接,但触发器本身是性能黑洞——尤其在高并发写场景下,可能让原表 QPS 断崖下跌。它更适合中小流量、结构变更不频繁的系统。
- 千万别在有
ON DELETE CASCADE或外键的表上用,pt-osc不处理级联逻辑,会导致数据不一致 - 触发器名长度有限制,如果库名+表名太长(比如带 UUID 后缀),
pt-osc会报错Identifier name '...' is too long,得加--chunk-index指定短索引 - 默认会检查从库延迟,如果从库落后太多,它会等——你得加
--max-lag=5控制等待上限,否则可能挂起半天 - 执行完别急着删旧表:
pt-osc默认保留原表为_t1_old,确认无误再手动DROP TABLE _t1_old
变更窗口期怎么控制真实影响范围
工具再好,也不能掩盖“结构变更本身就是高危操作”这个事实。真正的风险不在工具本身,而在你没看清它的行为边界。
-
gh-ost的--test-on-replica模式只是验证流程通不通,不验证数据一致性——它不会校验影子表和原表内容是否完全一样 - 所有在线 DDL 工具都不支持修改
PRIMARY KEY或分区规则,这类操作仍得停机 - 如果表上有
FULLTEXT索引或虚拟列,gh-ost可能失败,而pt-osc会静默跳过某些列,导致最终结构不一致 - 上线前务必在同规格从库上完整跑一遍,观察
SHOW SLAVE STATUS\G的Seconds_Behind_Master波动,比在测试环境空跑更有说服力
DDL 不是“点一下就完事”,它暴露的是你对数据链路的理解深度:binlog 怎么流、主从怎么同步、应用怎么写、监控怎么看异常。工具只是杠杆,支点还得你自己找。


















