日期:2026-07-12
标签:#面试 #八股 #后端 #灰度发布 #数据库 #Schema演进
一句话答案
使用 Expand-Migrate-Contract:先扩展向后兼容 Schema,再发布兼容新旧结构的代码,分批回填和校验,全部切换后才收缩旧字段;灰度期间避免双版本写出互不理解的数据。
面试口语版
数据库变更必须和代码分阶段发布。第一步 Expand:新增可空字段/新表,不删除、不改旧字段语义;第二步发布兼容版,读取优先新字段但可回退旧字段,写入可在同一事务双写或写旧字段后由 CDC 同步。第三步分批回填历史数据并做新旧值对账。第四步灰度新业务逻辑,确认旧版本仍能读取新数据。全部实例升级后停止旧写并观察,最后 Contract 删除旧字段和兼容代码。若新旧数据模型完全不兼容,使用版本化表、适配层或独立存储,不能强行同时写一个不兼容 Schema。
演进流程
flowchart LR
A[Expand兼容Schema] --> B[发布双读双写兼容代码]
B --> C[分批回填与对账]
C --> D[灰度新逻辑]
D --> E[全量切换]
E --> F[Contract清理旧结构]
关键细节
- 先 Schema 后代码还是先代码取决于变更,但任一步都必须兼容当前线上版本。
- 双写有失败和乱序风险,同库尽量同事务,跨库用 Outbox/CDC 和版本。
- 回滚不仅是流量回滚,新版已写数据也要旧版可理解。
- 回填限速并记录 checkpoint,监控锁、复制延迟和业务差异。
面试官追问
- 双写不一致怎么办?
- 新版本写入了旧版本不认识的枚举值怎么办?
- 什么时候可以删除旧字段?
面试官追问参考答案
1. 双写不一致怎么办?
同库字段尽量在一个事务中更新;跨系统明确单一事实源,通过 Outbox/CDC 同步,事件携带版本防旧值覆盖。持续对账新旧字段,差异自动修复,切换前要求误差收敛到可接受范围。
2. 新版本写入了旧版本不认识的枚举值怎么办?
灰度前旧版本必须升级为“可容忍未知值”的兼容版,例如映射为 UNKNOWN 而非崩溃;或新值仅对新版本隔离数据启用。不能在旧代码仍在线时直接写破坏性语义。
3. 什么时候可以删除旧字段?
确认所有生产、回滚版本、离线任务和外部消费者都不再读取/写入,回填和对账完成,并经过至少一个稳定观察窗口后再删除。删除仍走在线 DDL、备份和回滚计划。
学习清单
- 掌握 Expand-Migrate-Contract。
- 能处理双写、回填、回滚和枚举兼容。