gh-ost在线变更需满足硬性前提:binlog_format必须为ROW(STATEMENT/MIXED不可靠)、目标表须有主键或非空唯一索引、外键须提前删除;--test-on-replica是上线前必验步骤,cut-over依赖双连接锁确保原子性。

能在线变更,但必须满足几个硬性前提,否则直接失败或中途退出。
binlog_format 必须为 ROW
gh-ost 依赖解析 binlog 事件来同步增量变更,STATEMENT 或 MIXED 格式无法可靠还原 DML 行级影响,会报错退出。
- 检查当前格式:
SHOW VARIABLES LIKE 'binlog_format'; - 临时切换(需 SUPER 权限):
SET GLOBAL binlog_format = 'ROW'; - 永久生效:在 my.cnf 中配置
binlog_format = ROW并重启 MySQL(推荐) - 若主库不能改格式,可连一个已开启
log_slave_updates的从库,由它提供 ROW 格式 binlog
目标表必须有主键或非空唯一索引
gh-ost 用它做数据分块(chunk)和顺序拷贝的依据,没有则无法启动迁移,报错类似 Cannot find primary key or unique not null key on table。
- 不接受普通唯一索引含 NULL 值的列(例如
UNIQUE (email),但email允许 NULL 就不行) - 复合唯一索引可以,但所有列都必须
NOT NULL - 无主键表可先加主键:
ALTER TABLE t ADD COLUMN id BIGINT PRIMARY KEY AUTO_INCREMENT FIRST, ALGORITHM=INPLACE, LOCK=NONE;
--test-on-replica 是上线前必跑步骤
它在从库上完整走一遍迁移流程(建 ghost 表、拷数据、追 binlog、checksum 对比),但不执行最终换表。这是验证 --alter 是否合法、权限是否足够、外键是否存在、行数估算是否合理的关键防线。
- 命令示例:
gh-ost --test-on-replica --database=testdb --table=user_log --alter="ADD COLUMN status TINYINT DEFAULT 0" --host=10.0.1.5 --user=ghuser --password=xxx --assume-rbr --initially-drop-ghost-table --initially-drop-old-table --execute - 成功后务必人工比对从库上的原表和
_user_log_gho表数据一致性(如pt-table-checksum) - 若跳过此步直接在主库执行,遇到外键冲突或字段类型不兼容,只能中断并清理残留表(
_user_log_gho、_user_log_del、_user_log_ghc)
Cut-over 阶段锁表时间虽短,但依赖两个连接协同
最后换表不是简单 RENAME TABLE,而是通过「哨兵表 + 双连接锁」实现原子性。这意味着:如果主库连接数紧张、或网络抖动导致其中一个连接断开,cut-over 会失败并回滚,ghost 表保留,原表不变。
- 确保
max_connections足够,gh-ost 至少需要 2 个空闲连接(一个用于加锁,一个用于 rename) - 避免在 cut-over 期间执行其他长事务或 DDL,否则可能触发
Lock wait timeout exceeded - 可通过
--cut-over-lock-timeout-seconds=3缩短等待锁超时(默认 3 秒),防止长时间阻塞 - 真正耗时的是锁住原表后等待 binlog 追平——这一步取决于最后一批事件量,一般毫秒级,但若此时主库正批量导入,可能稍长
最容易被忽略的是外键约束:gh-ost 明确不支持任何外键,哪怕只是被引用方。迁移前必须手动 DROP FOREIGN KEY,完成后自己补回。这不是 bug,是设计取舍——加外键校验会破坏异步 binlog 应用的确定性。


















