一:gh-ost 介绍

gh-ost 是一款由GitHub开发的在线DDL(Online Data Definition Language)变更工具,专门用于MySQL数据库 。它允许在不锁定表的情况下执行数据库模式变更,从而避免了传统ALTER TABLE命令可能带来的长时间锁表问题,这对于高可用性要求严格的生产环境尤为重要。
它通过无触发器 (trigger-less)的方式实现表结构变更,减少了对数据库性能的影响,同时支持暂停、限速、动态配置等功能。相较于传统的 pt-online-schema-change,gh-ost 更安全、灵活,且对主库负载更低。

📖主要特点

  • 无锁表操作:与传统的ALTER TABLE不同,gh-ost不会长时间锁表,使得在生产环境中进行DDL变更更加安全。
  • 基于复制的迁移:gh-ost通过从库读取二进制日志来应用变更,这减少了对主库的压力。
  • 灵活的控制方式:支持动态调整速率限制、暂停/恢复操作等。
  • 模拟和预检查功能:提供dry-run选项用于测试变更效果,以及pre-flight checks确保操作的安全性。
  • 易于使用:虽然功能强大,但相对简单易用,适合DBA和技术人员快速上手。
  • 无触发器设计:不依赖数据库触发器,而是通过解析 MySQL 的 binlog 来同步数据变更,减少主库负载和潜在锁冲突。
  • 多种运行模式:可连接到主库(master)或副本库(replica),根据需求选择不同迁移策略。

其操作流程大致如下

  1. 创建两张内部管理表:包括 _表名_gho 的“影子表”(Ghost Table,即变更后的新表)和 _表名_ghc 的“日志表”,用于记录心跳、进度等元信息。
  2. 复制存量与同步增量(双线程并行)
    • 一个线程负责将原表的存量数据分批复制到影子表
    • 同时,另一个线程模拟从库拉取Binlog,解析出原表上的所有INSERTUPDATEDELETE增量变更,并同步到影子表
  3. 冲突处理策略:为保证双线程并行时数据一致,gh-ost 采用了特殊的语句改写策略:将复制线程的 INSERT 改写为 INSERT IGNORE,将 Binlog 中的 INSERT 改写为 REPLACE INTO
  4. 原子切换 (Cut-over):当数据同步完成,gh-ost会锁定原表并执行RENAME操作,瞬间将影子表替换为正式表

二:gh-ost 的三种模式架构

2.1 连接从库,在主库转换

这是 gh-ost 默认的工作模式,它会查看从库情况,找到集群的主库并且连接上去,对主库侵入最少。大体步骤是:

  • 在主库上创建 _xxx_gho(和原表表结构一致)、_xxx_ghc(记录变更日志),并修改 _xxx_gho 表结构;
  • 从 slave 上读取二进制日志事件,将变更应用到主库上的 _xxx_gho 表;
  • 在主库上读源表的数据写入 _xxx_gho 表中;
  • 在主库上完成表切换。

2.2 连接主库,在主库转换

需要使用 –allow-on-master 选项:

  • 在主库上创建 _xxx_gho、_xxx_ghc,并修改 _xxx_gho 表结构;
  • 从主库上读取二进制日志事件,将变更应用到主库上的 _xxx_gho 表;
  • 在主库上读源表的数据写入 _xxx_gho 表中;
  • 在主库上完成表切换。

2.3 在从库上测试和转换

这种模式会在从库上做修改。gh-ost 仍然会连上主库,但所有操作都是在从库上做的,不会对主库产生任何影响。在操作过程中,gh-ost 也会不时地暂停,以便从库的数据可以保持最新。

–migrate-on-replica 选项让 gh-ost 直接在从库上修改表。最终的切换过程也是在从库正常复制的状态下完成的。

–test-on-replica 表明操作只是为了测试目的。在进行最终的切换操作之前,复制会被停止。原始表和临时表会相互切换,再切换回来,最终相当于原始表没被动过。主从复制暂停的状态下,你可以检查和对比这两张表中的数据。

三:下载安装:

https://github.com/github/gh-ost/releases/latest

1.18已经支持MySQL 8.4了👍🏻

chmod +x gh-ost

基本使用

gh-ost \
  --host=<主库地址> \
  --port=<主库端口> \
  --user=<用户名> \
  --password=<密码> \
  --database=<数据库名> \
  --table=<表名> \
  --alter="<DDL语句>" \
  --execute
🔥关键参数说明
参数	说明
--alter	需要执行的 DDL 语句(如 ADD COLUMN,DROP INDEX 等)
--execute	实际执行迁移(默认是 dry-run 模式)
--allow-on-master	允许直接连接主库(默认连接到副本库)
--max-load=<metrics>	设置负载阈值(如 Threads_running=50)
--chunk-size=<rows>	每次迁移的数据块大小(默认 1000)
--critical-load=<metrics>	负载超限时终止操作
--switch-to-rbr	自动将副本库的 binlog 格式改为 ROW
--postpone-cut-over-flag-file=<path>	通过文件控制是否延迟切换表
--throttle-control-replicas	指定需要监控的副本库列表
--tmpdir	指定临时文件目录

🔥使用示例

1.添加新列

gh-ost \
  --host=127.0.0.1 \
  --port=3306 \
  --user=admin \
  --password=secret \
  --database=mydb \
  --table=orders \
  --alter="ADD COLUMN comment VARCHAR(255)" \
  --execute

2.修改字段类型

gh-ost \
  --alter="MODIFY COLUMN amount DECIMAL(10,2)" \
  --host=127.0.0.1 \
  --execute

3.添加索引

gh-ost \
  --alter="ADD INDEX idx_created_at (created_at)" \
  --host=127.0.0.1 \
  --execute

4.切换主库
在主库故障转移时,支持动态切换:

gh-ost \
  --assume-master-host=<new_master_host:port> \
  --execute

💥注意事项
权限要求

需要 SUPER、REPLICATION SLAVE、REPLICATION CLIENT 权限。
对原表和临时表有 CREATE、DELETE、ALTER 权限。
主键必须存在
目标表必须包含主键或唯一索引。

外键和触发器
gh-ost 不支持有外键约束或触发器的表。

大表操作
超大表迁移时需关注磁盘空间(临时表会占用额外空间)和网络负载。