4. 数据库迁移基础:Online Schema Change 原理、pt-online-schema-change 与 gh-ost 工作机制

在微服务与高并发场景下,数据库表结构的变更(如添加索引、修改列类型)如果采用传统 LOCK=EXCLUSIVE 方式,会导致长时间写阻塞,进而引发上游 gRPC 调用超时与连接池耗尽。Online Schema Change(OSC)技术正是为了解决这一矛盾而诞生。本章将深入剖析 OSC 的核心原理,并对比两款主流工具——pt-online-schema-change(Percona Toolkit)与 gh-ost(GitHub Online Schema Transfomer)的工作机制。

4.1 Online Schema Change 核心原理

OSC 的核心思想是:不直接修改原表,而是通过创建一个影子表,在影子表上执行 DDL,然后通过触发器或 binlog 同步增量数据,最后通过原子性的表名切换完成变更。整个过程对业务流量几乎无感知。

核心三阶段:
  1. 准备阶段:创建与原表结构一致的空影子表,并在影子表上执行目标 DDL。
  2. 数据同步阶段:将原表的存量数据分批拷贝至影子表;同时持续捕获原表上的增量变更(INSERT/UPDATE/DELETE),并应用到影子表。
  3. 切换阶段:在确认数据一致后,通过原子性的 RENAME TABLE 操作交换原表与影子表,完成最终变更。
Online Schema Change 三阶段流程 Online Schema Change 三阶段流程 阶段 1:准备 创建影子表 + 执行 DDL 阶段 2:数据同步 存量拷贝 + 增量捕获 阶段 3:切换 RENAME TABLE 原子交换 关键说明 • 存量拷贝:使用 INSERT … SELECT … LIMIT 分批读取原表数据,避免长事务。 • 增量捕获:pt-osc 使用触发器,gh-ost 使用 binlog 解析。 • 切换瞬间:RENAME 操作仅需毫秒级元数据锁,对业务几乎无影响。

4.2 pt-online-schema-change 工作机制

pt-online-schema-change(pt-osc)是 Percona Toolkit 中最常用的 OSC 工具。它基于 触发器(Trigger) 实现增量数据同步,架构简单且兼容性广。

4.2.1 核心工作流程

  1. 创建影子表: CREATE TABLE _new LIKE original_table,并在影子表上执行目标 DDL。
  2. 安装触发器:在原表上创建 AFTER INSERT、AFTER UPDATE、AFTER DELETE 三个触发器,用于将增量变更实时同步到影子表。
  3. 分批拷贝存量数据:使用 INSERT LOW_PRIORITY INTO _new (...) SELECT ... FROM original_table FORCE INDEX(...) WHERE ... 按主键范围分批拷贝。
  4. 等待与切换:当拷贝完成后,通过 RENAME TABLE original TO _old, _new TO original 完成原子切换。
  5. 清理:删除原表(_old)和触发器。

4.2.2 优缺点分析

维度 说明
优点
  • 兼容所有 MySQL 版本(5.5+)及 Percona Server。
  • 对主从复制无特殊要求,无需开启 binlog_row_image。
  • 社区成熟,文档丰富,参数可调性强。
缺点
  • 触发器带来额外性能开销,高并发写入场景下可能成为瓶颈。
  • 触发器可能导致死锁或复制延迟。
  • 需要 SUPER 权限创建触发器。
性能陷阱:当原表写入量极大时,触发器会显著增加单次 DML 的响应时间。在 gRPC 调用链中,这种微小的延迟放大可能导致上游超时重试风暴。建议在变更前使用 --chunk-time 和 --sleep 参数控制拷贝速率。

4.3 gh-ost 工作机制

gh-ost 由 GitHub 开发,采用 无触发器(Triggerless) 架构,通过解析 MySQL binlog 来捕获增量变更,从根本上避免了触发器带来的性能问题。

4.3.1 核心工作流程

  1. 连接与配置:gh-ost 作为 MySQL 客户端连接到主库或从库,读取 binlog 流。
  2. 创建影子表:与 pt-osc 类似,创建 _gho 影子表并执行 DDL。
  3. 读取 binlog 流:gh-ost 模拟一个从库,实时解析 binlog 中的行事件(Row-Based Replication),并将变更应用到影子表。
  4. 分批拷贝存量数据:使用 INSERT IGNORE INTO _gho (...) SELECT ... FROM original_table 按主键范围拷贝。
  5. 原子切换:通过 RENAME TABLE original TO _ghc, _gho TO original 完成切换。
  6. 清理:删除 changelog 表(_ghc)和影子表。

4.3.2 优缺点分析

维度 说明
优点
  • 无触发器,对原表写入性能影响极小。
  • 支持暂停/恢复、限速、校验等丰富控制能力。
  • 支持从从库读取 binlog,降低主库负载。
缺点
  • 要求 MySQL 开启 binlog 且格式为 ROW,且 binlog_row_image=FULL。
  • 对主从复制延迟敏感,延迟过高可能导致数据不一致。
  • 部署相对复杂,需要额外的 binlog 连接。
pt-osc 与 gh-ost 架构对比 pt-osc 与 gh-ost 架构对比 pt-online-schema-change 原表 (original) 触发器 (Trigger) 影子表 (_new) RENAME 切换 gh-ost (Triggerless) 原表 (original) Binlog 流 (ROW) gh-ost 应用 解析 binlog + 应用变更 影子表 (_gho)

4.4 工具选型对比

对比维度 pt-online-schema-change gh-ost
增量同步方式 触发器(Trigger) Binlog 解析(Row-Based)
对原表性能影响 较高(触发器增加 DML 开销) 较低(无触发器,仅 binlog 读取)
MySQL 版本要求 5.5+ 5.6+(需开启 binlog_row_image=FULL)
主从复制要求 无特殊要求 需稳定复制,延迟敏感
暂停/恢复能力 有限(通过 --sleep 参数) 原生支持(--throttle 命令)
适用场景 低写入、兼容性优先 高写入、性能敏感
选型建议:
  • 如果业务表写入量较小(< 1000 TPS),且 MySQL 版本较低,优先选择 pt-osc。
  • 如果业务表写入量较大(> 5000 TPS),且对 gRPC 调用延迟敏感,强烈建议使用 gh-ost,避免触发器带来的 GC 停顿放大效应。
  • 无论选择哪种工具,都应在变更前进行 压测,观察数据库 CPU、IO、复制延迟以及上游 gRPC 调用成功率。