中 困难
大表DDL操作实战#
一句话答案#
千万级大表加字段/加索引不能直接 ALTER,用 pt-online-schema-change(触发器复制)或 gh-ost(binlog 复制)实现不锁表的在线 DDL。
核心要点
直接 ALTER 的问题#
ALTER TABLE orders ADD COLUMN remark VARCHAR(255); -- 千万级表可能锁表数分钟到数小时sqlMySQL DDL 的三种算法:
| ALGORITHM | 过程 | 是否锁表 |
|---|---|---|
| COPY | 创建新表→复制数据→交换表名 | 锁表(全程不可写) |
| INPLACE | 在原表上就地修改 | 部分操作短暂锁表 |
| INSTANT | 只修改元数据(MySQL 8.0.12+) | 不锁表(毫秒级) |
INSTANT 支持的操作有限(加列到末尾、修改默认值等),加索引、改列类型仍需 INPLACE 或 COPY。
pt-online-schema-change(Percona)#
原理:触发器 + 数据复制
1. 创建新表(schema 已修改)
2. 在原表上创建三个触发器:INSERT/UPDATE/DELETE
→ 增量变更实时同步到新表
3. 按 chunk 批量复制原表历史数据到新表
4. 交换表名(RENAME TABLE,原子操作)
5. 删除旧表和触发器plaintext优点: 成熟稳定、使用广泛 缺点:
- 触发器有性能开销(写入 QPS 下降 10%-30%)
- 原表已有触发器时无法使用
- 外键处理复杂
gh-ost(GitHub)#
原理:binlog 复制,无触发器
1. 创建 ghost 表(schema 已修改)
2. 伪装为 MySQL 从库,从 binlog 读取增量变更
→ 实时回放到 ghost 表
3. 按 chunk 批量复制原表历史数据到 ghost 表
4. 原子切换表名(cut-over)plaintext优点:
- 无触发器,对原表写入无额外开销
- 可暂停/限速,灵活控制迁移速度
- 支持动态调整参数(不用重启)
缺点:
- 需要 binlog 为 ROW 格式
- 实现更复杂
pt-osc vs gh-ost 选型#
| 维度 | pt-osc | gh-ost |
|---|---|---|
| 增量同步方式 | 触发器 | binlog |
| 写入性能影响 | 较大(触发器开销) | 较小 |
| 可控性 | 一般 | 好(可暂停、限速) |
| 适用场景 | 通用 | 写入密集型、binlog=ROW |
| 复杂度 | 简单 | 较复杂 |
操作注意事项#
- 磁盘空间:需要额外一倍的表空间(新旧表并存)
- 执行时间:千万级表通常 30 分钟到数小时,取决于表大小和限速
- 低峰执行:尽管不锁表,但会占用 IO 和带宽
- 数据校验:迁移完成后用 pt-table-checksum 校验数据一致性
- 回滚方案:保留旧表一段时间,确认无误后再删除
面试回答(2分钟版)
千万级大表直接 ALTER TABLE 可能锁表数小时导致业务不可用,生产环境通常用 pt-online-schema-change 或 gh-ost 来做不锁表的在线 DDL。两者核心思路一样:创建一张结构已修改的新表,一边批量复制存量数据,一边同步增量变更,最后原子切换表名。区别在于增量同步方式:pt-osc 在原表上建三个触发器实时捕获增量,简单但写入性能有 10%-30% 的开销;gh-ost 则伪装为从库直接从 binlog 读取增量,无触发器开销且支持暂停限速,但要求 binlog 格式为 ROW。实际操作中需要注意:执行前检查磁盘空间是否足够容纳两倍表大小,尽量在低峰期执行,完成后用 pt-table-checksum 校验数据一致性,并保留旧表一段时间作为回滚兜底。MySQL 8.0 的 INSTANT DDL 只支持加列到末尾等有限操作,加索引、改列类型仍然需要这些工具。
追问与易错
追问方向:
- “gh-ost 的 cut-over 怎么做到原子的?”→ 用 RENAME TABLE 原子交换
- “迁移过程中原表还能写吗?”→ 可以,增量通过触发器/binlog 同步
- “MySQL 8.0 的 INSTANT DDL 了解吗?”→ 只支持有限操作,加索引仍需 INPLACE
易错点:
- ❌ “MySQL 5.7+ 的 Online DDL 不锁表”——INPLACE 在元数据变更阶段仍有短暂的 MDL 锁
- ❌ 直接在高峰期执行 ALTER——即使用工具也应低峰操作
- ❌ 不检查磁盘空间就执行——新表 + 旧表 需要双倍空间