pt-online-schema-change在大表上卡住或失败的根本原因是其依赖触发器实时捕获变更,当源表写入压力高、主从延迟大或存在长事务时,工具会主动暂停拷贝以保护系统,而非性能缺陷。

pt-online-schema-change 为什么会在大表上卡住或失败
根本原因不是工具本身慢,而是它依赖触发器实时捕获变更,一旦源表写入压力高、主从延迟大、或存在未处理的长事务,pt-online-schema-change 就会主动暂停拷贝——这是保护机制,不是 bug。常见表现是日志里反复出现 Waiting for the slave to catch up 或 Pausing due to high load。
关键限制必须提前确认:
- 源表必须有主键或唯一索引,否则工具直接退出
- 数据库不能已有触发器,否则报错
ERROR 1442 (HY000): Can't update table in stored function/trigger - 外键约束必须显式处理,否则报错
Foreign key constraints are not supported,需加--alter-foreign-keys-method=auto - 磁盘空间要预留至少 2 倍表大小,临时表 + 原表 + binlog 日志都会占用空间
gh-ost 如何绕过触发器限制并降低主库压力
gh-ost 不在源表上建触发器,而是解析 binlog 拿到 DML 变更,再异步应用到临时表。这意味着它天然兼容带触发器的旧系统,也避免了触发器带来的额外锁和性能抖动。
但要注意它的依赖条件:
- 必须开启
binlog_format=ROW,否则无法准确解析变更内容 - 主库上
binlog_row_image必须为FULL(MySQL 5.6+ 默认),否则 UPDATE/DELETE 可能丢失旧值 - 需要一个可连接的、权限足够的 MySQL 账号,且该账号需有
REPLICATION SLAVE和REPLICATION CLIENT权限 - 不支持修改主键列、分区表、或包含
ENUM/SET类型的列(部分版本已支持,但需实测)
典型执行命令:gh-ost --host=xxx --database=test --table=t_user --alter="ADD COLUMN c4 VARCHAR(32)" --chunk-size=1000 --max-load="Threads_running=25" --allow-on-master --execute
什么时候该选 pt-osc,什么时候必须切 gh-ost
如果线上环境满足以下全部条件,pt-online-schema-change 仍是首选:有主键、无触发器、外键少、DBA 对 Percona Toolkit 熟悉、且升级窗口允许短时元数据锁(rename 阶段约几百毫秒)。
但只要出现以下任一情况,就该直接上 gh-ost:
- 表上有业务强依赖的触发器,改表前不敢删
- 主库 CPU/IO 已长期高于 70%,不能再加触发器开销
- 主从延迟波动大,
pt-osc频繁暂停导致总耗时不可控 - 需要在 RDS 类托管服务上操作(如阿里云 RDS、腾讯云 CDB),它们通常禁用触发器或
SUPER权限
注意:gh-ost 的 rename 阶段同样需要元数据锁,但持续时间更短、更可控;而 pt-osc 在 rename 前还多一次 ANALYZE TABLE,可能意外触发统计信息重算,拖慢切换。
线上执行前最容易被忽略的三件事
不是参数调优,也不是备份——而是这三点没做,90% 的失败都发生在这儿:
- 没检查
max_allowed_packet:若表中有大字段(如TEXT/BLOB),默认 4MB 可能导致INSERT失败,建议设为64M并重启会话 - 没确认
wait_timeout和interactive_timeout:工具连接可能因空闲超时中断,尤其在慢速拷贝阶段,建议临时调高至28800(8 小时) - 没关闭
autocommit的客户端行为:某些 ORM 或监控工具会自动设autocommit=1,干扰 gh-ost 的事务控制逻辑,应确保连接初始化时显式执行SET autocommit=0
这些配置项不写在工具命令里,也不报错,但会在某次 chunk 拷贝后静默失败,日志只显示 “unexpected EOF”。











