pt-osc修改大表字段时“卡住”并非真正锁表,而是因长事务、ddl阻塞或主从延迟导致copy末尾的rename操作等待mdl锁,表现为日志停在copying rows...且processlist出现waiting for table metadata lock。

pt-osc 修改大表字段时为什么还会锁表?
根本原因不是 pt-osc 本身“无锁”,而是它依赖 MySQL 的触发器和原子性重命名实现在线变更——但触发器执行阶段若遇到长事务、主从延迟或 ALTER TABLE 被阻塞,就会卡在 copy 阶段末尾的 RENAME 操作上,表现为“最后几秒卡住”,看起来像锁表。
常见错误现象:pt-online-schema-change 日志停在 Copying rows... 后长时间不动,或报错 Waiting for the new table to catch up with the original table;同时 SHOW PROCESSLIST 中能看到大量 Waiting for table metadata lock 状态。
- 检查是否有未提交的长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60; - 确认目标表没被其他 DDL 占用(如另一个
pt-osc或原生ALTER正在运行) - 避免在业务高峰执行;
--chunk-time=0.5可让每批次更短,降低单次锁行时间
添加 NOT NULL 字段必须指定 DEFAULT 吗?
是的,而且这个 DEFAULT 值会实际写入老表所有行(即使你后续用 ALTER TABLE ... DROP DEFAULT),不加会导致 pt-osc 直接失败并报错:Cannot add a NOT NULL column with default value NULL。
实操建议:
- 如果字段允许为空,优先用
NULL+ 显式DEFAULT NULL(MySQL 8.0+ 默认行为,但显式写更安全) - 如果必须
NOT NULL,且不想污染历史数据,先加NULL字段,再用UPDATE分批填充,最后MODIFY COLUMN ... NOT NULL(用原生ALTER,此时表已小) - 慎用
DEFAULT CURRENT_TIMESTAMP:MySQL 5.6 不支持对非第一TIMESTAMP列设该默认值,会报错Invalid default value
如何安全地修改主键或唯一索引字段?
pt-osc 默认禁止修改含主键或唯一索引的列,因为可能引发复制冲突或数据校验失败。绕过限制(--no-check-alter)风险极高,不推荐。
真正可行路径:
- 新增字段 + 建唯一索引(不带
UNIQUE约束),用应用层双写确保一致性 - 等新字段数据同步完成,用
pt-osc删除旧字段、重命名新字段 - 最后一步才加
UNIQUE约束:此时表已无旧字段,可用原生ALTER TABLE ... ADD UNIQUE(速度快,影响小) - 如果必须改主键类型(如
INT→BIGINT),先确认所有外键、分区、触发器都兼容,再用--alter "MODIFY id BIGINT UNSIGNED AUTO_INCREMENT",但务必加--dry-run和--print先看生成的语句
复制延迟大时 pt-osc 会自动暂停吗?
不会自动暂停,但会持续检测从库延迟。一旦延迟超过 --max-lag(默认 1s),它就停止拷贝新 chunk,进入等待状态,并每 --check-interval(默认 1s)轮询一次 Seconds_Behind_Master。这是保护从库不拖垮的核心机制。
关键参数组合:
-
--max-lag=30:允许最大延迟 30 秒,避免频繁中断 -
--check-interval=10:每 10 秒查一次延迟,减少主库压力 -
--critical-load="Threads_running=50":主库并发线程超 50 就终止,防雪崩 - 务必配合
--recursion-method=none(如果你只监控本机主库)或--recursion-method=hosts(明确指定从库 IP),否则可能误判延迟
最易被忽略的是:pt-osc 检测延迟依赖 SHOW SLAVE STATUS,如果用了 GTID 或 MGR,得用 --recursion-method=dsn 配合自定义查询,否则延迟永远为 0。











