面试知识库
困难

大表DDL操作实战#

一句话答案#

千万级大表加字段/加索引不能直接 ALTER,用 pt-online-schema-change(触发器复制)或 gh-ost(binlog 复制)实现不锁表的在线 DDL。

核心要点

直接 ALTER 的问题#

ALTER TABLE orders ADD COLUMN remark VARCHAR(255);  -- 千万级表可能锁表数分钟到数小时
sql

MySQL 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-oscgh-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——即使用工具也应低峰操作
  • ❌ 不检查磁盘空间就执行——新表 + 旧表 需要双倍空间