pt-online-schema-change通过触发器+影子表双写实现不锁表,依赖主键或唯一非空索引定位行;ADD COLUMN等操作基本不锁表,但ENGINE=InnoDB会触发短暂全表拷贝;需配置ROW格式、开启binlog、调大innodb_lock_wait_timeout。

pt-online-schema-change 为什么能不锁表
它不是靠 MySQL 本身“在线”能力,而是用触发器+影子表双写来实现的。主表照常读写,所有变更同时写进影子表;等数据追平后,用原子性 RENAME TABLE 切换,真正阻塞时间通常在毫秒级。
关键前提是:主表必须有主键或唯一非空索引,否则 pt-online-schema-change 直接报错退出,因为无法精准定位和同步每一行。
常见错误现象:Cannot chunk the table because no suitable index exists —— 不是没加索引,而是索引含 NULL 值、或者复合索引最左前缀不满足扫描条件。
哪些操作它真能“不锁”,哪些其实还是会卡
DDL 类型决定行为底线:ADD COLUMN、DROP COLUMN、MODIFY COLUMN、ADD INDEX 这些都走标准流程,基本不锁主表;但 ALTER TABLE ... ENGINE=InnoDB 或 OPTIMIZE TABLE 类操作,即使套了 pt 工具,也会在切换前后触发短暂全表拷贝,期间 SELECT ... FOR UPDATE 或大事务可能被堵住。
使用场景要注意:
- 线上核心库建议加
--max-load="Threads_running=25",防复制延迟飙升 - 如果主从延迟已超 1 秒,
pt-online-schema-change默认会暂停,可加--critical-load调整阈值 - 千万级以上表务必加
--chunk-size=1000(默认 1000),避免单次更新太久触发锁等待
执行前必须检查的三项配置
不是跑命令就完事,MySQL 服务端几个参数不配好,工具会默默降级成锁表模式:
-
binlog_format必须是ROW,MIXED或STATEMENT下触发器无法正确捕获变更 -
log_bin必须开启,否则从库同步中断,pt 工具检测到延迟后直接中止 -
innodb_lock_wait_timeout建议设为 50 以上,避免 chunk 更新时因锁冲突频繁重试
漏查其中任一,现象可能是:进度条卡在 “Copying rows” 十几分钟不动,日志里反复出现 Waiting for the new table to catch up。
迁移中途失败怎么安全回滚
pt-online-schema-change 自带回滚机制,但前提是没加 --no-drop-old-table —— 这个参数一旦启用,旧表删了就真没了。
典型失败场景:
- 磁盘空间不足:影子表 + 触发器日志会吃双倍空间,执行前用
SELECT SUM(data_length+index_length) FROM information_schema.tables算下原表大小,留出至少 1.5 倍余量 - 触发器冲突:已有自定义触发器的表,pt 会拒绝执行,报错
Triggers exist on `db`.`tbl`,得先DROP TRIGGER - 权限不足:需要
ALTER、DROP、INSERT、UPDATE、DELETE、SELECT、TRIGGER全部权限,少一个都会在 “Creating triggers” 阶段失败
真正容易被忽略的是:切表完成后,旧表名变成 _tbl_old,但应用代码、监控脚本、备份策略里如果硬编码了表名,这里就会悄悄出问题。










