Yihui’s Blog

数据库表读写中新增字段,如何做到不影响现有读写?

日期:2026-07-12
标签:#面试 #八股 #后端 #MySQL #在线DDL #场景题

一句话答案

先确认数据库版本和变更是否支持 Instant/Online DDL;采用兼容性发布顺序、短元数据锁控制和监控,重型变更使用 gh-ost/pt-online-schema-change 影子表迁移。

面试口语版

我不会直接高峰执行 ALTER TABLE。先在同版本、同数据量环境确认算法和锁级别,简单尾部加 nullable/有默认值字段若支持 Instant 可快速完成,但仍需获取短暂元数据锁,所以要检查长事务并设置较短 lock wait timeout。应用按 Expand-Contract:先加可空字段,旧代码继续可用;发布兼容新旧 Schema 的代码并双写/回填;数据验证后再切读,最后增加约束。若操作会重建大表,使用 gh-ost 或 pt-osc 建影子表、增量同步、小流量复制后原子切换。

发布顺序

flowchart LR
  A[检查版本与长事务] --> B[新增兼容字段]
  B --> C[发布兼容代码]
  C --> D[分批回填]
  D --> E[校验并切读]
  E --> F[最后加约束]

关键细节

  • 在线 DDL 不等于零影响,可能消耗 I/O、日志和复制带宽。
  • 避免一开始加 NOT NULL 且业务立即依赖。
  • 回填按主键分批限速,观察复制延迟和锁。
  • ORM 使用 SELECT * 或严格列映射时需做兼容测试。

面试官追问

  1. 为什么 Instant DDL 仍可能被阻塞?
  2. 新字段历史数据如何回填?
  3. gh-ost 的基本原理是什么?

面试官追问参考答案

1. 为什么 Instant DDL 仍可能被阻塞?

即使不重写表,也要获取 Metadata Lock 修改表定义;已有长事务或未结束查询持有相关 MDL 时,ALTER 会等待,并可能让后续请求排队。执行前需清理长事务并设置超时。

2. 新字段历史数据如何回填?

按主键范围小批量更新,设置速率和休眠,记录 checkpoint,监控 CPU、I/O、undo、锁和复制延迟。应用在回填期间兼容 null,并用幂等条件只更新未回填行。

3. gh-ost 的基本原理是什么?

创建带新结构的影子表,分批复制旧数据,同时读取 binlog 把增量变更应用到影子表;追平后短暂锁定并原子交换表名。它降低长时间阻塞,但仍需额外磁盘、复制流量和切换评估。

学习清单

  • 理解 Instant、Online 和影子表迁移。
  • 掌握 Expand-Contract 发布顺序。
维护与整理 · Yihui在 GitHub 上编辑

继续阅读

浏览全部文章