PostgreSQL Field Guide

PostgreSQL 安全迁移与零停机 Schema 变更

使用 expand-and-contract、lock_timeout、Squawk、Testcontainers 和 pgTAP 降低 PostgreSQL DDL 锁表、回填与跨版本升级风险

零停机不是某条 DDL 的属性,而是旧应用与新应用能否在迁移窗口内同时工作。安全迁移需要兼容阶段、锁预算、真实数据规模测试、可观察执行和明确回退点。

默认采用 expand-and-contract

以重命名或替换一个高流量字段为例:

  1. Expand:添加新字段或新表,不删除旧结构;尽量使用短元数据操作。
  2. Dual compatible:应用能读取新旧结构,并在必要时双写;写入必须幂等。
  3. Backfill:按主键范围或时间窗口小批回填,限制事务时长、WAL 与 replica lag。
  4. Switch:先切读路径,再停止旧写入;用业务指标和校验查询确认。
  5. Contract:经过一个可回退发布窗口后,才删除旧结构。

直接把“加列、回填、设 NOT NULL、删旧列”放进一个长事务,通常会扩大锁、WAL、rollback 和复制延迟风险。

给锁等待设上限

BEGIN;
SET LOCAL lock_timeout = '2s';
SET LOCAL statement_timeout = '15min';

ALTER TABLE app.orders
  ADD COLUMN IF NOT EXISTS fulfillment_state text;

COMMIT;

示例超时不是通用默认值。lock_timeout 防止迁移长时间排队后在不可控时刻获得强锁;statement_timeout 限制执行时间。失败后应退出并调查 blocker,而不是无限重试。

添加约束时可把扫描与短锁阶段拆开:

ALTER TABLE app.orders
  ADD CONSTRAINT orders_total_nonnegative
  CHECK (total_cents >= 0) NOT VALID;

ALTER TABLE app.orders
  VALIDATE CONSTRAINT orders_total_nonnegative;

先在目标 PostgreSQL 版本和代表性数据上检查具体 DDL 的锁级别。CREATE INDEX CONCURRENTLY 也会消耗 I/O、WAL 和更长时间,并需要检查失败后留下的 invalid index。

推荐 CI 流水线

Schema / reviewed SQL

Migration generation

Squawk static checks

Disposable PostgreSQL 18.4

Apply every migration from an empty and upgraded state

pgTAP + application integration + RLS negative tests

PostgreSQL 19 Beta compatibility lane

Representative-data rehearsal → staging → production
  • Squawk 检查常见危险迁移,例如非并发索引、未使用 NOT VALID 的约束和一些锁风险;它不是零停机证明。
  • Testcontainers for Node.js 在 CI 中启动真实 PostgreSQL,适合验证事务、锁、RLS、JSONB、扩展与驱动行为。
  • pgTAP 在数据库内部测试函数、触发器、约束和策略。

如果使用 Drizzle ORM,可让 Drizzle Kit 生成普通 schema 变更,再对生成 SQL 进行 Squawk 与人工审核。复杂 index、policy、function、extension 和 PostgreSQL 19 新语法可以使用受审查的原生 SQL;ORM 无法表达不代表数据库不应该使用。

测试升级路径,而不只测试空库

CI 至少需要两种数据库状态:

起点能发现的问题
空数据库执行全部 migration顺序、依赖、语法和初始化问题
生产版本 schema/脱敏数据执行增量 migration锁、回填、旧数据、约束与性能问题

再将测试拆成两条版本通道:

  • 生产门禁:当前生产 major/minor,例如 PostgreSQL 18.4,失败时阻止发布;
  • 前瞻兼容:PostgreSQL 19 Beta 2,可允许失败但必须归类、跟踪和在 GA 前清零。

不要让 Beta 测试替代稳定版本门禁。版本状态见 PostgreSQL 19 专题

RLS 与安全对象必须做反向测试

迁移成功不代表权限正确。为每个 tenant 和 role 验证:允许的 SELECT/INSERT/UPDATE/DELETE 成功,不允许的跨租户读写失败;应用运行角色不是表 owner,必要时启用 FORCE ROW LEVEL SECURITY。详见 安全基线

回滚应用不等于回滚数据库

删除列、不可逆回填、类型收窄和外部副作用可能无法安全逆转。每次发布应写明最后可回退时点、旧应用能否读取新 schema,以及 forward fix 的触发条件。

何时采用更重的工具

工具适用场景采用前先验证
pgroll高频、兼容窗口明确的零停机 schema change支持的 DDL、代理/连接方式、回滚语义
Bytebase多团队审批、SQL review、环境和审计治理权限边界、部署模型、现有 CI 集成
Database Lab Engine大型数据库的快速 clone 与迁移演练存储、脱敏、clone 生命周期与成本

小团队先把 expand-and-contract、真实 PostgreSQL 测试、锁观察与恢复演练做好,再引入控制面。

Last updated on

On this page