跳至内容
11 守正出奇:模式变更与安全发布

第 11 章 守正出奇:模式变更与安全发布

PostgreSQL 能在事务中执行许多 DDL,但“能回滚”不等于“能在线发布”。一次模式变更能否安全进入生产,至少同时取决于:

logical compatibility
  × lock mode and lock duration
  × scan/rewrite/WAL/resource cost
  × old/new application coexistence
  × resumable data migration
  × observable switch and rollback window
  × explicit contract gate

所以本章不把 ALTER TABLE 写成命令速查。我们把变更拆成一个有状态、有停止线、有证据的发布协议:

legacy
  → expand       add backward-compatible shape
  → migrate      bounded, restartable backfill
  → validate     prove old and new rows satisfy invariants
  → switch       move traffic while retaining rollback shape
  → observe      prove old readers/writers have disappeared
  → contract     remove old semantics only after a separate gate

任何箭头失败,都先问“当前已提交状态是什么、服务是否仍兼容、下一步是重试、暂停、回退流量还是前滚修复”,而不是下意识运行一份机械 down.sql

本章目标

完成本章后,读者应当能够:

  • 从 lock、scan、rewrite、WAL 与 application compatibility 五个维度评审 DDL;
  • 说明元数据 fast path 为什么仍会等待 ACCESS EXCLUSIVE
  • lock_timeout 把无限排队变成可识别的 55P03
  • 区分 non-volatile constant default 与 volatile default 的物理行为;
  • 识别类型变更、约束验证、列删除和表重写的不同恢复语义;
  • 设计 expand–migrate–validate–switch–contract 状态机;
  • 为旧写入、新双写和影子读取定义兼容矩阵;
  • 用 keyset 批次、原子 checkpoint 与停止线实现可中断回填;
  • 解释为什么双写需要单一一致性权威,不能依赖两个独立写调用;
  • 使用 NOT VALID 先保护新写入,再用 VALIDATE CONSTRAINT 检查历史行;
  • 在 PostgreSQL 14–17 与 18 之间正确处理 NOT NULL 目录差异;
  • CREATE INDEX CONCURRENTLY 放在事务块外,并复用第 9 章的失败回收纪律;
  • 预建可证明的 CHECK,降低 ATTACH PARTITION 的验证扫描风险;
  • 说明 ATTACH 对父表和子表的实际锁,以及 default partition 的额外边界;
  • 正确使用 PostgreSQL 14+ 的 DETACH PARTITION CONCURRENTLY
  • 在 Pigsty 中隔离发布连接,观察锁、WAL、复制、磁盘、连接和延迟;
  • 区分 PostgreSQL 原生证据、Pigsty 平台证据与应用发布证据;
  • 拒绝在旧依赖或观察窗口证据缺失时执行 contract;
  • SAFE-MIGR-006DEFAULT-VERS-010 产出 v0.6 candidate evidence。

实验边界

实验基线为 PostgreSQL 18.4、Pigsty v4.4.0、Ubuntu 24.04 L1;主体发布路径面向 PostgreSQL 14–18。所有可重建对象都位于 shop_private,以 ch11_ 开头并带固定 marker,不修改 shop.* 业务表:

ch11_order                 50,000-row legacy order fixture
ch11_migration_state       monotonic phase and backfill checkpoint
ch11_default_probe         constant/volatile default A/B
ch11_default_probe_result  physical and WAL evidence
ch11_event                 range-partitioned parent
ch11_event_2025q1          preloaded standalone attach candidate

订单发布的最终自动验收故意停在:

phase=switched
shipping_code NOT NULL and validated
shipping_method retained
compatibility bridge retained
contract not executed

这是设计结果,不是“少做一步”。本地脚本能证明目录、数据和 SQLSTATE,不能证明生产中的旧容器、离线作业、BI 查询和临时脚本已经退出,也不能把几秒钟等待冒充约定的回退观察期。

下载资产:

所属位置

  • 卷别:上卷:应用开发(独立导读页,不构成章节父目录)
  • 教学分组:第二篇:应用——从 SQL 正确走向稳定交付
  • 兼容入口:/ch11//volume-1/schema-change-release/

本章目录

11.1 识别 DDL 的四类风险

11.2 Expand–Migrate–Contract

11.3 索引与约束的在线化路径

11.4 在线分区化

11.5 数据回填与流量切换

11.6 发布窗口中的平台观察

11.7 实战:无中断演进订单模式

实测摘要

一次 PostgreSQL 18.4 全量验收得到:

risk:
  ACCESS SHARE holder + ADD COLUMN
    → waiter requests AccessExclusiveLock
    → SQLSTATE 55P03
    → shipping_code remains absent

default:
  50,000 rows + constant 7
    → same relfilenode / atthasmissing=true / WAL ≈ 12 KB
  50,000 rows + clock_timestamp()
    → new relfilenode / atthasmissing=false / WAL ≈ 11.5 MB

compatibility:
  old insert → standard/STD
  old update → express/EXP
  new dual write → pickup/PUP
  mismatch → 23514 / named constraint identity

backfill:
  initial legacy nulls=49,999
  two × 5,000 batches → exit 75 / remaining 39,999
  resume eight batches → migrated 49,999 / remaining 0
  checkpoint total=10 batches / last_order_id=50,000

constraints:
  pair CHECK NOT VALID → VALIDATE
  non-null CHECK NOT VALID → VALIDATE → SET NOT NULL
  SET NOT NULL relfilenode unchanged
  PG18 relation NOT NULL appears in pg_constraint and pg_attribute

partition:
  ATTACH parent=ShareUpdateExclusiveLock
  ATTACH child=AccessExclusiveLock
  validated bound CHECK present before attach
  DETACH CONCURRENTLY outside transaction / rows retained=20,000
  child relfilenode unchanged / final reattached

release:
  phase=switched / contract=P3612 refused
  old column and bridge retained / worker=0
  business checksum=f8a7bfae59c6d16cd323abecfefe1014

WAL 字节、filenode OID、PID、毫秒和具体 lock backend 都不是跨环境 golden。稳定结论是 fast/rewrite 的相对关系、锁边、SQLSTATE、单调状态、批次原子性、零遗漏、数据保留和最终兼容边界。

章节验收

  1. 变更说明同时回答 lock、scan/rewrite、WAL/space/lag 与兼容性;
  2. lock_timeout 小于发布允许排队时间,statement_timeout 留出执行预算;
  3. 55P03 被当作“未取得发布条件”,不是盲目无限重试;
  4. constant/volatile default 的物理差异有目录与 WAL 证据;
  5. expand 对所有仍受支持的旧版本保持可读写;
  6. 双写只有一个一致性 authority,并有命名约束反例;
  7. backfill 使用有界 keyset batch,不用巨型 OFFSET;
  8. 数据更新与 checkpoint 同事务提交;
  9. 受控中止能从精确水位继续,不能跳过低 key 遗留行;
  10. NOT VALID 不被误写成“暂不执行约束”;
  11. VALIDATE CONSTRAINT 的扫描和 lock budget 单独规划;
  12. PostgreSQL 18 的关系级 NOT NULL catalog 差异已隔离;
  13. concurrent index 命令位于事务块外,失败残留有 exact cleanup;
  14. ATTACH 候选列定义完全匹配父表且 bound CHECK 已验证;
  15. default partition 和 subpartition 的额外扫描边界已评审;
  16. DETACH CONCURRENTLY 只在 PostgreSQL 14+ 且事务块外使用;
  17. Pigsty 发布连接、应用流量和只读分析入口职责分离;
  18. 锁、WAL、replication lag、disk、connection 与 SLI 同窗观察;
  19. application deployment 与 database migration 各有独立 identity;
  20. contract 需要旧读写依赖清零与真实观察期证据;
  21. reset 的错误 token、错误 target 和 active-worker 反例全部拒绝;
  22. 最终业务 checksum 不变,v0.6 仍是 candidate 而非已发布 baseline。

下一章 ch12《一气呵成:从数据库契约到后端服务》 将从数据库状态机继续走向 driver、连接池、应用发布和端到端验收。

参考资料


上一章:顾此失彼:并发控制与隔离异常 · 返回上卷导读 · 下一章:一气呵成:从数据库契约到后端服务 · 查看全书目录 · 查看索引中心

最后更新于