首页/开发/系统分析与架构设计速查/扩展-收缩(并行变更)
架构风格与模式58 条

扩展-收缩(并行变更)

兼容性变更的标准手法:先扩展(新增字段/接口并双写、回填历史数据),迁移消费方后再收缩(删除旧结构);数据库与接口演进都适用。

扩展-收缩是什么

扩展-收缩是兼容性变更的标准手法:先扩展(新增字段或接口,双写并回填历史数据),确认消费方全部迁移后再收缩(删除旧结构)。数据库表结构演进与对外接口版本演进都适用同一套节奏,核心是让新旧版本在发布窗口内可以同时正确运行。

为什么不能一步到位

滚动发布期间旧实例与新实例会同时在线,直接把字段改名会让尚未更新的实例立刻报错。把变更拆成互不阻塞的三步之后,任何时刻新旧代码都能正确读写,回滚也只需要退回上一版本,不必逆转数据。

三个阶段

  • 扩展:新增结构,旧结构继续可用,写入同时更新两处
  • 迁移:读路径切换到新结构,并校验新旧数据一致性
  • 收缩:确认无依赖后删除旧结构与兼容代码

数据库列改名示例

阶段操作兼容性要求
扩展新增 new_col 并双写,读兼容新旧两列旧代码不改动照常运行
迁移回填历史数据,读切到 new_col 并抽样校验旧列保持可读,随时可回退
收缩停止写旧列,确认无读取方后删除先核对依赖方调用量清零

落地要点

回退要点每个阶段独立可回退,双写与回填期间监控延迟与数据差异
完成判据旧结构零读取、零写入,持续观察一个发布周期后再删除
接口侧字段先加后删,枚举先扩后收,消费方对未知字段保持容忍
清理登记旧结构的删除时间与责任人登记,到期前复核存量依赖

适合什么变更

这套手法适用于所有需要零停机的结构变更:字段改名、字段拆分与合并、类型调整、接口版本替换、枚举值扩展。不适合的是无法双写的场景,例如删除数据或收紧唯一约束,这类变更需要先确认存量数据满足新约束,再单独安排停机窗口或分批处理。

每个阶段都要能独立回退;回填与双写期间必须监控延迟与数据差异,否则偏差往往要等到收缩阶段才暴露,届时已经无法简单回退。

易错点

  • 未确认真实依赖就删除旧字段,遗留调用方直接报错
  • 双写阶段缺少一致性校验,数据偏差在收缩后才暴露
  • 回填与双写没有限速,大表更新拖慢在线库,影响正常读写
  • 收缩阶段没有登记责任人,旧字段长期残留成为无人敢动的历史包袱

数据来源与参考

  • Martin Fowler bliki《ParallelChange》(2014)
  • Sam Newman《Monolith to Microservices》相关章节
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌