1. 技术背景与版本演进

在 MySQL 的发展历程中,MySQL 5.6 是一个关键的分水岭。

  • MySQL 5.5 及以前 (Offline):

    执行 ALTER TABLE 添加索引时,数据库通常采用 COPY 模式。

    • 表的动作: 新建临时表 -> 锁死原表(禁止写入)-> 全量复制数据 -> 替换旧表。

    • 后果: 业务中断(Downtime),对于大表来说是不可接受的。

  • MySQL 5.6 及以后 (Online):

    引入了 Native Online DDL 特性。

    • 表的动作: 允许在变更表结构的过程中,并发执行 DML(增删改)操作。


2. 方案一:MySQL 原生 Online DDL

这是 MySQL 引擎层(InnoDB)自带的能力。

核心指令

SQL

ALTER TABLE `users` ADD INDEX `idx_name` (`name`), ALGORITHM=INPLACE, LOCK=NONE;

表的状态流转 (In-Place 模式)

当执行上述语句时,表经历了以下过程:

  1. 准备阶段 (Prepare):

    • 持有原表的元数据锁 (MDL),这只是瞬间的动作,为了确定表结构快照。

    • 创建新的 frm 文件(表定义文件)。

  2. 执行阶段 (Execute) —— 耗时最长:

    • 原表 (Original Table): 降级锁,允许读写

    • Row Log (增量日志): 这是一个内存缓存区。在此期间,业务对原表的所有写入(Insert/Update/Delete),除了写入原表外,还会被记录到 Row Log 中。

    • 索引构建: 数据库在后台扫描原表数据,构建新的索引树。

  3. 提交阶段 (Commit):

    • 再次短暂持有排他锁。

    • 应用增量 (Apply Log): 将 Row Log 中记录的变更应用到新的索引结构中。

    • 完成元数据更新,释放锁。

局限性

  • 资源不可控: 一旦开始,MySQL 会全力跑索引构建,无法暂停,容易占满 CPU 和 I/O,影响线上业务。

  • 主从延迟: 主库执行完后,从库需要串行回放整个 DDL 过程,导致从库在一段时间内严重滞后。


3. 方案二:gh-ost (外部工具方案)

这是目前美团、GitHub 等大厂处理 TB 级大表高峰期变更 的首选方案。它不依赖 MySQL 内部的 Online DDL 机制,而是通过外部程序控制。

核心机制: 影子表 (Shadow Table) + Binlog 模拟

表的详细流转过程

使用 gh-ost 时,数据库中会同时存在两张表进行交互:

阶段一:初始化 (Setup)

  • 原表 (users): 正常对外提供读写服务。

  • 影子表 (_users_gho): 工具创建的一张空表

    • 结构与原表一致。

    • 已包含新索引

阶段二:数据同步 (Copy & Sync)

这是最关键的阶段,两张表同时处于活动状态:

  1. 存量数据拷贝 (Row Copy):

    • 工具生成 SELECT ... INTO ... 语句。

    • 原表 分批次读取数据,写入 影子表

    • 特点: 支持动态限速。工具会监控 Threads_running,负载高则暂停写入影子表。

  2. 增量数据追平 (Binlog Apply):

    • 原表 继续接收业务写入,生成 Binlog。

    • 工具模拟成 Slave,读取 Binlog。

    • 将解析出的变更操作(Insert/Update/Delete),实时在 影子表 上重放。

阶段三:原子切换 (Cut-Over)

当存量拷贝完成,且增量追平(延迟几乎为 0)时:

  • 表的动作: 发生表名交换。

  • SQL 原理:

    SQL

    RENAME TABLE `users` TO `users_del`, `_users_gho` TO `users`;
    
  • 结果: 这一瞬间,应用程序原本连接的是旧表,下一毫秒请求就落到了带有新索引的新表上。


4. 总结对比

特性

MySQL 原生 Online DDL

gh-ost (影子表方案)

推出时间

MySQL 5.6

2016 (GitHub)

是否生成新表

否 (In-Place 原地操作)

是 (Shadow Table 影子表)

IO/CPU 控制

不可控 (容易打挂数据库)

完全可控 (可随时暂停/限速)

主从延迟

高 (从库需回放 DDL)

低 (分批写入)

高峰期执行

高风险

安全 (因为可以避让负载)

失败回滚

代价大 (需漫长回滚)

极快 (只需删除影子表)

结论:

如果不关心负载且表不大,用 MySQL 原生 Online DDL 即可;如果是在生产环境、数据量大且需要在高峰期执行,gh-ost 是唯一的安全选择。

比较是偷走幸福的小偷