1gh-ost 简介
gh-ost(GitHub's Online Schema Transmogrifier)是 GitHub 于 2016 年开源的 MySQL 在线 DDL 工具。
核心优势:
• 无触发器:不在原表上创建触发器,减少锁冲突
• 可控性强:支持运行时动态调整参数(限速、暂停)
• 可审计:变更过程可通过 Unix Socket 实时监控
• 可测试:支持测试模式,不实际切换
GitHub Stars: 12.5k+
语言: Go
最低要求: MySQL 5.5+(需开启 ROW 格式 binlog)
2工作原理
gh-ost 的核心原理是通过 binlog 捕获变更,而非触发器。
执行流程:
1. 连接到 MySQL,创建 ghost 表(_gho 后缀)和 changelog 表
2. 在 ghost 表上执行 ALTER 操作
3. 连接 binlog(可以连主库或从库的 binlog)
4. 使用 INSERT ... SELECT 分批复制行数据到 ghost 表
5. 同时通过 binlog 同步增量变更到 ghost 表
6. 全量数据复制完成后,通过 RENAME TABLE 原子切换
三种运行模式:
a) 连接主库(默认,最常用):
binlog 从主库读取,读写都在主库执行
b) 连接从库测试:
binlog 从从库读取,迁移在从库执行,不影响主库
c) 在从库测试后迁移到主库:
先在从库验证,确认无误后在主库执行
vs pt-osc 的本质区别:
┌─────────────────────────────────────────┐
│ pt-osc: 原表触发器 → _new 表 │
│ gh-ost: binlog 解析 → _gho 表 │
└─────────────────────────────────────────┘
触发器会与业务 DML 竞争行锁,而 binlog 异步应用不会。
3安装部署
方法一:下载预编译二进制(推荐)
# 从 GitHub Releases 下载
wget https://github.com/github/gh-ost/releases/download/v1.1.6/gh-ost-binary-linux-amd64-20231207144046.tar.gz
tar xzf gh-ost-binary-*.tar.gz
chmod +x gh-ost
mv gh-ost /usr/local/bin/
方法二:从源码编译
git clone https://github.com/github/gh-ost.git
cd gh-ost
./script/build
前置要求:
• MySQL binlog_format = ROW
• binlog_row_image = FULL
• 执行用户需要 SUPER, REPLICATION SLAVE, REPLICATION CLIENT 权限
验证:
gh-ost --version
4基本使用
添加索引:
gh-ost \
--host=db-master \
--database=mydb \
--table=orders \
--alter="ADD INDEX idx_user_date (user_id, created_at)" \
--allow-on-master \
--execute
添加列:
gh-ost \
--host=db-master \
--database=mydb \
--table=users \
--alter="ADD COLUMN phone VARCHAR(20) DEFAULT '' AFTER email" \
--allow-on-master \
--execute
修改列类型:
gh-ost \
--host=db-master \
--database=mydb \
--table=products \
--alter="MODIFY COLUMN description TEXT" \
--allow-on-master \
--execute
常用参数说明:
--allow-on-master 允许直接在主库执行
--execute 实际执行(不加则为 dry-run)
--chunk-size=1000 每批处理行数
--max-lag-millis=1500 从库延迟超过此值暂停
--critical-load="Threads_running=50" 负载阈值
--nice-ratio=0 CPU 友好度(0=最快,>0 每 chunk 后暂停)
--cut-over=default 切换方式(default=原子 rename)
--initially-drop-ghost-table 开始前删除遗留的 ghost 表
--ok-to-drop-table 完成后删除旧表
5高级功能
1. 运行时动态控制(通过 Unix Socket)
# 暂停迁移
echo throttle | socat - /tmp/gh-ost.mydb.orders.sock
# 恢复迁移
echo no-throttle | socat - /tmp/gh-ost.mydb.orders.sock
# 修改 chunk 大小
echo chunk-size=500 | socat - /tmp/gh-ost.mydb.orders.sock
# 查看状态
echo status | socat - /tmp/gh-ost.mydb.orders.sock
# 紧急终止(不切换,保留 ghost 表)
echo panic | socat - /tmp/gh-ost.mydb.orders.sock
2. 从从库读取 binlog(减少主库负载)
gh-ost \
--host=db-master \
--assume-master-host=db-master:3306 \
--database=mydb \
--table=orders \
--alter="ADD INDEX idx_status (status)" \
--allow-on-master \
--execute
3. 测试模式(在从库验证)
gh-ost \
--host=db-replica \
--database=mydb \
--table=orders \
--alter="ADD INDEX idx_status (status)" \
--test-on-replica \
--execute
4. 限流策略
# 基于复制延迟
--max-lag-millis=1500
--throttle-control-replicas="replica1:3306,replica2:3306"
# 基于负载
--critical-load="Threads_running=50"
--max-load="Threads_running=25"
6gh-ost vs pt-osc 对比
全方位对比 gh-ost 和 pt-online-schema-change:
┌────────────────┬──────────────────────┬──────────────────────┐
│ 维度 │ gh-ost │ pt-osc │
├────────────────┼──────────────────────┼──────────────────────┤
│ 变更捕获方式 │ Binlog 解析 │ 触发器 │
│ 锁影响 │ 极小(仅切换瞬间) │ 触发器与 DML 竞争锁 │
│ 前置要求 │ ROW binlog │ 无特殊要求 │
│ 运行时控制 │ Socket 动态调整 │ 无 │
│ 从库延迟感知 │ 精确到毫秒 │ 秒级 │
│ 触发器冲突 │ 无 │ 表不能有已有触发器 │
│ 外键支持 │ 有限 │ 较好 │
│ 主键要求 │ 必须有主键或唯一键 │ 必须有主键或唯一键 │
│ 语言 │ Go │ Perl │
│ 维护状态 │ 活跃(GitHub 维护) │ 活跃(Percona 维护) │
│ 社区规模 │ 12.5k Stars │ 1.1k Stars │
└────────────────┴──────────────────────┴──────────────────────┘
选型建议:
• 表有已有触发器 → gh-ost(唯一选择)
• 需要精确限流和动态控制 → gh-ost
• 不想开启 ROW binlog → pt-osc
• 有外键依赖的表 → pt-osc(更成熟)
• 超大表(>1亿行)→ gh-ost(锁影响更小)
7生产环境实践
生产环境使用 gh-ost 的最佳实践:
1. 变更前检查清单
• 确认 binlog_format = ROW,binlog_row_image = FULL
• 确认表有主键
• 评估表大小和预计执行时间
• 确认磁盘空间充足(ghost 表 + binlog 增长)
• 低峰期执行,通知相关团队
2. 标准执行模板
gh-ost \
--host=$MASTER_HOST \
--database=$DB \
--table=$TABLE \
--alter="$ALTER_SQL" \
--allow-on-master \
--chunk-size=1000 \
--max-lag-millis=1500 \
--critical-load="Threads_running=50" \
--max-load="Threads_running=25" \
--initially-drop-ghost-table \
--ok-to-drop-table \
--execute 2>&1 | tee /tmp/gh-ost-$TABLE-$(date +%Y%m%d).log
3. 监控要点
• 通过 Socket 定期查看 status
• 监控从库延迟(应保持 < 1.5s)
• 监控磁盘空间和 Threads_running
• 日志输出中关注 ETA(预计完成时间)
4. 回滚方案
• 切换前:echo panic | socat - /tmp/gh-ost.sock(终止,不切换)
• 切换后:旧表默认保留为 _del 后缀,可手动 RENAME 回滚
• 建议:保留旧表至少 24 小时确认无误后再删除