用pt-online-schema-change修改大表结构,核心是「不碰原表ddl、靠影子表+触发器同步、最后原子rename」;它仅在rename瞬间持mdl锁(毫秒级),需满足主键/唯一索引、无原触发器、外键显式处理、复制配置合规四大前提,并严格配置--dry-run、--execute、--chunk-time等参数保障稳定。

直接上结论:用 pt-online-schema-change 修改大表结构,核心是「不碰原表 DDL、靠影子表 + 触发器同步、最后原子 rename」。它不是真·零锁,但锁表时间极短(仅 rename 那一瞬),对业务几乎无感——前提是参数配对、环境合规。
为什么不能直接用 ALTER TABLE?
MySQL 5.7 及更早版本中,ALTER TABLE 对千万级以上表加字段、改类型、删索引等操作,默认走 COPY 算法,会全程持有写锁(LOCK=EXCLUSIVE),导致 INSERT/UPDATE/DELETE 全部阻塞。即使开启 ALGORITHM=INPLACE,也只对部分操作生效(比如加二级索引),且仍可能触发 MDL 锁争用,尤其在高并发场景下容易卡住复制或业务线程。
常见错误现象包括:
-
Waiting for table metadata lock持续几十秒甚至分钟 - 从库延迟陡增,
Seconds_Behind_Master突然跳涨 - 应用报错
Lock wait timeout exceeded或连接池耗尽
pt-online-schema-change 执行前必须检查的 4 个硬条件
工具启动时会做安全校验,任一不满足就直接退出,不执行任何变更:
- 原表必须有主键或唯一非空索引(否则无法分 chunk 拷贝,会报错
No PRIMARY KEY or unique not null key found) - 原表不能已有任何触发器(
ERROR: Table has triggers—— pt-osc 要自己建 INSERT/UPDATE/DELETE 三个,冲突即拒) - 若存在外键,必须显式指定
--alter-foreign-keys-method(常用值为auto或rebuild_constraints,否则报错Foreign key constraints exist...) - 主从复制不能启用过滤规则(如
replicate-ignore-db),否则默认被拦截;需加--no-check-replication-filters才绕过(但不推荐,应先清理复制配置)
关键参数怎么设才稳?
参数选错轻则拖慢进度,重则压垮数据库。重点关注以下几项:
-
--dry-run:必须第一步加!只建影子表、不拷数据、不启触发器,验证语法、权限、外键处理逻辑是否 OK -
--execute:真正执行开关,正式运行时必须显式带上,缺了就是白跑 -
--chunk-time=0.5:控制每批拷贝耗时上限(秒),值越小对线上影响越小,但总耗时变长;0.3~1.0 是常用区间 -
--max-lag=5:当从库延迟超过 5 秒,自动暂停拷贝;避免雪崩式延迟累积 -
--critical-load="Threads_running=50":若SHOW STATUS LIKE 'Threads_running'> 50,立即终止操作,防 DB 过载 -
--set-vars="innodb_lock_wait_timeout=1":让 pt-osc 在锁冲突时快速失败,避免成为“锁霸占者”
示例命令:
pt-online-schema-change \ --host=localhost \ --user=root \ --password=xxx \ --database=shopdb \ --table=user_order \ --alter="ADD COLUMN status TINYINT DEFAULT 0 AFTER id" \ --dry-run
rename 那一刻到底锁多久?哪些坑最容易被忽略?
整个流程中唯一需要元数据锁(MDL)的操作,就是最后一步 RENAME TABLE user_order TO user_order_old, user_order_new TO user_order。这个操作本身很快(毫秒级),但前提是:
- 没有长事务正在访问该表(否则会卡在等待 MDL 上)
- 没有其他 DDL 正在执行(如另一个 pt-osc 或手工
ALTER) - 表名不能含特殊字符或大小写混合(MySQL 默认大小写不敏感,但某些配置下 rename 会失败)
最容易被忽略的是:rename 后旧表(_user_order_old)默认保留,不自动删除。如果批量跑多个 pt-osc,磁盘空间可能悄悄爆掉;务必确认是否加了 --drop-old-table,或手动清理。











