本文目录
数据库发布的关键不是一条迁移语句,而是跨版本兼容窗口
生产系统修改字段、索引或约束时,应用发布与数据库变更很难真正同时完成。更稳妥的做法是先增加兼容结构,让旧版和新版应用都能运行;再分批回填并核对数据;随后切换读写、观察业务指标;最后在确认不再回退旧版后,单独删除旧结构。能否不停服要根据数据库类型、版本、数据量、锁行为和业务流量实测判断,不能只看测试库执行速度。
先确认四个发布条件
| 判断项 | 可执行标准 | 需要避免 |
|---|---|---|
| 版本兼容 | 数据库扩展后,当前线上版本与待发布版本都能正常读写核心流程 | 迁移完成后旧实例立即报错,滚动发布或应用回退都失去空间 |
| 锁与耗时 | 在接近生产规模的数据上测过执行计划、锁等待、复制延迟和超时边界 | 测试库秒级完成,生产大表却阻塞写入或拖慢业务请求 |
| 数据回填 | 任务按稳定主键分批、可重复执行、可限速,并能记录进度与错误 | 一次大事务占满资源,中断后无法判断哪些记录已经处理 |
| 故障回退 | 应用可回退到仍兼容新结构的旧版本,破坏性清理安排在独立发布窗口 | 把删除字段和新代码同时上线,回退应用也无法恢复服务 |
先判断这次变更是否需要分阶段发布
这份指南适合已有真实流量、需要滚动或分批发布的关系型数据库系统,例如订单、客户、工单和内部管理平台。首次上线的空库、可以明确停机维护的小型内部工具,通常不必引入完整的多阶段流程;跨数据库迁移、分库分表和超大规模在线变更则需要单独方案。
同一句 ALTER TABLE 在不同数据库产品、版本、字段定义和数据规模下,可能只改元数据,也可能扫描或重写大量数据,并取得不同级别的锁。本文提供发布方法,不代替目标数据库当期文档、生产规模演练和有权限的数据库负责人评审。
- 列出受影响的表、索引、约束、接口、报表、定时任务和数据同步链路。
- 确认是否允许维护窗口、可接受的请求失败和延迟上限,以及发布停止条件。
- 记录数据库产品与版本、表行数和容量、写入峰值、长事务、只读副本与备份状态。
把迁移拆成结构、数据和应用三个变更面
评审时不要只提交一份 SQL。结构变更决定锁与兼容性,数据变更决定扫描量和回填时间,应用变更决定新旧字段何时被读写。三者分开列出,才能看清每一步失败后系统处于什么状态。
先在接近生产数据量的副本或脱敏环境验证,再结合生产监控安排窗口。重点观察执行时间之外的指标:锁等待、连接池占用、慢查询、CPU 与磁盘、事务日志增长、复制延迟和业务错误。若无法获得接近生产规模的数据,应把这种不确定性写进风险与停止条件。
- 逐条标注操作是新增、修改、校验还是删除,并查明目标版本的锁级别和限制。
- 为每一步写明预计持续时间、资源上限、负责人、监控项、停止方式和可重试条件。
- 先处理长事务和异常任务,避免迁移等待旧事务时无期限占用发布窗口。
第一阶段只扩展结构,暂时保留旧字段与旧接口
先增加新列、新表或新关系,使现有应用无需理解新结构也能继续工作。新增字段通常先允许为空或提供经过评审的兼容策略,不要在同一次发布里立刻重命名或删除旧字段。
需要把字段从旧含义迁移到新含义时,可以让新版应用在兼容期内同时维护两套表示,或把写入集中到一个可审计的适配层。双写并非天然安全:必须定义部分成功、重试、乱序与补偿,否则表面上兼容,实际会制造两份不一致的数据。
- 结构扩展脚本应可识别已执行状态,重复运行不会创建冲突对象或覆盖数据。
- 新版代码在新字段为空时仍能读取旧数据,旧版代码不会因新结构存在而失败。
- 用功能开关或明确的配置控制新读路径,不把“部署完成”直接等同于“全部切换”。
数据回填按小批次执行,并把进度做成可核对状态
历史数据回填应与用户请求解耦,按稳定且有索引的主键范围分批处理。每批限制记录数或执行时长,提交后记录进度;出现负载升高、复制延迟或错误率超限时可以暂停,恢复后从安全位置续跑。
回填逻辑要幂等:同一批数据再次执行,结果仍然正确。更新时还要避免覆盖用户在回填期间产生的新值,可使用条件更新、版本字段或明确的数据主责规则。完成数量不是唯一验收依据,还要比较空值、重复值、聚合结果和业务抽样。
- 记录总量、待处理量、成功量、跳过量、失败量、最后主键和最近错误,不只看任务是否退出。
- 为批次大小、并发和间隔提供可调整参数,以生产指标而非固定速度决定是否加速。
- 对无法转换的数据进入隔离清单并指定处理人,不用默认值静默掩盖业务异常。
先验证新路径,再逐步切换读写和应用实例
回填达到约定条件后,先让少量内部用户、租户或实例读取新结构,对比关键结果,再逐步扩大范围。滚动发布期间旧实例与新实例会短暂共存,因此数据库必须同时满足两者的查询和写入假设。
不要把耗时且不可控的数据库迁移塞进每个应用实例的启动流程。应用就绪检查应回答当前实例能否接收流量;数据库迁移应由单独、可观测且只执行一次的发布步骤负责。切换时同时观察技术指标和业务指标,例如下单失败、状态流转异常、金额差异与后台待处理积压。
- 在切换前验证新旧版本混跑,覆盖读旧写新、读新写旧和重复请求等兼容场景。
- 按可控制的范围放量,达到错误率、延迟或数据差异阈值时停止扩大并回到旧读路径。
- 确认所有旧实例、定时任务和临时脚本已退出后,才停止维护旧字段。
索引和约束按数据库能力单独安排
索引构建、非空约束、唯一约束和外键校验可能扫描整表或影响并发写入。应查阅目标数据库与版本文档,选择适合在线业务的方式,并在发布脚本中设置合理的锁等待与语句超时,让拿不到锁的变更尽快失败,而不是长时间排队拖住业务。
以 PostgreSQL 为例,普通 CREATE INDEX 会阻塞表上的写入;CONCURRENTLY 允许写入继续,但工作量更大、限制更多,失败后还可能留下无效索引,且不能放在事务块中。因此“并发”不是无需监控的开关,执行后仍要核对索引有效性、查询计划、耗时和资源影响。
- 先清理不符合新约束的历史数据,再启用并验证约束,避免在流量高峰才发现脏数据。
- 索引建成后检查其有效状态和实际查询计划,不以命令返回成功作为唯一验收。
- 超时失败时保留现场信息并评估重试窗口,不临时取消保护参数强行执行。
回退应用与回退数据分开设计,清理旧结构最后做
应用回退可以很快,已经发生的数据写入却未必能安全逆转。发布前要说明故障后是关闭新路径、回退应用、执行补偿,还是从备份恢复;备份恢复会丢失恢复点之后的数据,不能被当成日常的一键回滚。
删除旧列、旧表和旧索引属于收缩阶段,应在新版稳定运行、对账完成、回退窗口关闭后单独发布。验收证据至少包括迁移记录、回填统计、差异核对、监控截图或日志引用、故障演练结果和最终清理审批。完成这些步骤仍不代表任何变更都能不停服;若演练显示锁或数据转换无法控制,应选择明确的维护窗口。
- 回退演练验证旧应用能在扩展后的数据库上运行,不依赖已经删除的字段或约束。
- 对账覆盖记录数量、关键金额或状态、抽样明细及新旧口径差异,不只比较总行数。
- 清理前再次搜索代码、报表、任务和外部同步对旧结构的引用,并保留审批与恢复方案。
需求讨论中的问题
有数据库备份,为什么还要做兼容发布?
备份主要解决灾难恢复,不等于快速发布回退。恢复通常需要时间,也可能丢失备份点之后的新交易;兼容发布让应用先退回旧读写路径,处理范围更小。
小表变更可以把加字段、回填和设为非空写在一个脚本里吗?
只有在目标数据库版本、真实数据量和业务窗口上验证过锁与耗时后才能判断。即使当前是小表,分阶段也能留下更清楚的失败边界;若合并执行,应记录依据和可接受的阻塞时间。
应用发布成功,能马上删除旧字段吗?
不建议。应先确认所有旧实例、异步任务、报表和外部同步都不再访问旧字段,并经过约定观察期与对账,再把删除操作作为独立发布。
生产数据库变更上线前,逐项确认这 12 件事
可逐项勾选,辅助本次阅读;刷新页面后不保留。
参考资料
技术概念可进一步阅读以下资料;文中的业务场景与实施清单用于方案讨论。
- PostgreSQL Documentation · ALTER TABLE
用于核对不同 ALTER TABLE 子命令的锁级别、表扫描与约束验证行为;具体实施以项目使用版本的文档为准。
- PostgreSQL Documentation · CREATE INDEX
用于核对普通与并发索引构建对写入的影响、事务限制、额外工作量和失败后无效索引风险。
- PostgreSQL Documentation · Client Connection Defaults
statement_timeout 与 lock_timeout 的官方定义及配置注意事项。
- Kubernetes Documentation · Deployments
滚动更新中新旧应用实例逐步替换、就绪状态与发布进度的机制参考。
准备修改正在运行的业务数据库?
把数据库类型与版本、表规模、变更目标、发布方式和可接受维护窗口简单发来,月弦科技可以先帮你拆分兼容步骤、回填策略与验收边界。