MySQL 5.6前ALTER TABLE全程锁表,5.6+虽支持INPLACE但加索引等仍可能锁表;pt-online-schema-change通过新建表、触发器同步、分块拷贝和原子切换实现无锁变更,但依赖主键/唯一索引,且不支持改主键、分区重组等操作。

为什么直接 ALTER TABLE 会锁表
MySQL 5.6 之前,ALTER TABLE 基本全程锁表(尤其是 MyISAM 和旧版 InnoDB),DML 操作全部阻塞;5.6+ 虽支持部分 ALGORITHM=INPLACE 操作,但像加索引、改列类型、增删列仍可能触发表拷贝或元数据锁升级,导致主从延迟飙升、应用超时。真实线上环境里,一个耗时 20 分钟的 ALTER 很容易引发雪崩。
pt-online-schema-change 是怎么绕过锁的
它不直接改原表,而是另起炉灶:建新表 → 同步数据 → 逐行迁移 → 原子切换。核心靠三样东西:触发器捕获变更、REPLACE INTO ... SELECT 分块拷贝、RENAME TABLE 瞬间交换。整个过程原表始终可读写,但要注意:触发器本身有开销,大事务可能堆积,且必须保证主键或唯一索引存在(否则无法定位变更行)。
常见错误现象包括:
-
Cannot find a unique index for the table—— 缺少主键或唯一索引,工具无法安全同步增量 - 长时间卡在
Copying rows阶段 —— 表太大或--chunk-size设得太小,建议从 1000 开始调优 - 从库复制中断 —— 触发器操作被写入 binlog,若从库启用了
replicate_ignore_table或过滤规则,需确认是否放行临时表名(如_tbl_new)
关键参数和最小安全执行命令
不要裸跑默认参数。生产环境至少要加这四个:
-
--alter "ADD COLUMN status TINYINT DEFAULT 0"—— DDL 语句必须用双引号包裹,空格和大小写敏感 -
--chunk-size=5000—— 控制每次迁移行数,太小增加 round-trip 次数,太大加重主库负载 -
--max-load="Threads_running=25"—— 当前活跃线程超 25 就暂停拷贝,防雪崩 -
--critical-load="Threads_running=50"—— 达到 50 直接退出,避免恶化 -
--dry-run --execute先试跑,确认无报错再真正执行
最简可用命令示例:
pt-online-schema-change \ --host=localhost \ --user=root \ --password=xxx \ --database=testdb \ --table=user_info \ --alter "ADD COLUMN updated_at DATETIME" \ --chunk-size=2000 \ --max-load="Threads_running=20" \ --critical-load="Threads_running=40" \ --dry-run
哪些操作它也搞不定
不是所有 DDL 都能“在线”。以下情况 pt-online-schema-change 会直接拒绝或行为异常:
- 修改
PRIMARY KEY或删除主键 —— 工具依赖主键做增量同步,禁用 - 对分区表执行
--alter且涉及REORGANIZE PARTITION—— 不支持,得用ALTER TABLE ... REORGANIZE PARTITION手动做 - 目标列名含反引号或特殊字符(如
`col name`)—— 解析失败,改用标准命名 - MySQL 5.7+ 开启了
sql_log_bin=OFF的会话中运行 —— 触发器不记 binlog,从库彻底失联
真正上线前,务必在从库上先停掉 SQL 线程,检查 SHOW PROCESSLIST 是否有残留的 pt_osc_* 触发器,以及 information_schema.TRIGGERS 里有没有漏删的条目 —— 这些细节不清理,下次变更就可能冲突。











