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 模式)
当执行上述语句时,表经历了以下过程:
准备阶段 (Prepare):
持有原表的元数据锁 (MDL),这只是瞬间的动作,为了确定表结构快照。
创建新的 frm 文件(表定义文件)。
执行阶段 (Execute) —— 耗时最长:
原表 (Original Table): 降级锁,允许读写。
Row Log (增量日志): 这是一个内存缓存区。在此期间,业务对原表的所有写入(Insert/Update/Delete),除了写入原表外,还会被记录到 Row Log 中。
索引构建: 数据库在后台扫描原表数据,构建新的索引树。
提交阶段 (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)
这是最关键的阶段,两张表同时处于活动状态:
存量数据拷贝 (Row Copy):
工具生成
SELECT ... INTO ...语句。从 原表 分批次读取数据,写入 影子表。
特点: 支持动态限速。工具会监控
Threads_running,负载高则暂停写入影子表。
增量数据追平 (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 是唯一的安全选择。