MySQL在线DDL在5.6之前全表锁,5.6+仍有多数场景退化为COPY锁表;pt-online-schema-change通过影子表+触发器+分批同步实现真正在线变更,但要求主键/唯一索引、无同名触发器、足够权限及ROW/MIXED日志格式。

MySQL在线DDL为什么不能直接用ALTER TABLE
5.6之前MySQL执行ALTER TABLE会锁全表,DML全部阻塞;5.6+虽支持部分ALGORITHM=INPLACE操作,但仍有大量场景不适用——比如修改列类型、加全文索引、变更主键,或表上有触发器/外键时,MySQL仍会退化为COPY算法,锁表时间与数据量正相关。线上大表(千万级以上)做这类操作,基本等于停服。
pt-online-schema-change核心原理和启动条件
pt-online-schema-change本质是“影子表+触发器+数据同步”:新建目标结构的空表(影子表),用触发器捕获原表的增删改,再分批次把老数据拷贝过去,最后原子切换表名。它不依赖MySQL原生在线DDL能力,所以兼容性极广(支持5.1+)。
但必须满足几个硬性前提:
- 原表必须有主键或唯一非空索引(否则无法按主键分块同步,会报错
Cannot chunk table) - 不能存在同名的触发器(工具会自己建
INSERT/UPDATE/DELETE触发器,冲突则失败) - 执行账号需具备
SELECT, INSERT, UPDATE, DELETE, DROP, CREATE, ALTER, SUPER, TRIGGER权限 - 目标库不能开启
binlog_format=STATEMENT(触发器在SBR下可能复制异常,推荐MIXED或ROW)
常用命令参数和避坑要点
最简可用命令:
pt-online-schema-change --host=localhost --user=root --password=xxx D=test,t=users --alter "ADD COLUMN status TINYINT DEFAULT 0" --execute
但生产环境必须加关键控制参数:
-
--chunk-size:默认1000行,大表建议调小(如200),避免单次拷贝太久导致触发器积压 -
--max-load:如"Threads_running=25",超过阈值自动暂停,防止拖垮数据库 -
--critical-load:如"Threads_running=50",达到即中止,避免雪崩 -
--check-interval:默认1秒检查一次负载,高并发下可设为0.5 - 务必加
--dry-run先试跑,看是否能正常创建影子表和触发器;加--print可打印每一步SQL,方便审计
常见翻车点:--alter里的语句不能带ENGINE=或COMMENT=(工具会自动继承原表属性);如果加索引,别名不能重复(ADD INDEX idx_name (col)中的idx_name必须全新);切换瞬间会有短暂元数据锁(rename操作),应用层要做好重试。
如何确认操作真正完成且安全
执行完pt-osc不会自动删影子表(以防回滚),需手动检查:
- 查
SHOW PROCESSLIST确认无pt_osc相关连接残留 - 查
SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_NAME LIKE 'old_%',确认旧表已重命名(格式为old_dbname_tablename_20240510_123456) - 对比新旧表行数:
SELECT COUNT(*) FROM usersvsSELECT COUNT(*) FROM old_test_users_* - 抽样校验数据一致性(尤其时间戳、浮点字段),可用
pt-table-checksum辅助
旧表保留至少24小时再DROP,因为binlog里所有DML事件都还指向原表名,过早删除会影响闪回或从备库恢复。


















