用途
PostgreSQL Database Migrations 是一份用于规划、测试并安全执行 PostgreSQL 模式迁移的指南,重点面向生产数据或共享数据库环境。其核心目标是解决「在空开发库上可行的迁移,在大表上可能失败、锁表或损坏数据」的问题。
主要能力
DDL 锁参考
指南强调每次模式变更都会获取锁,关键问题是该锁是否阻塞读写以及持续多久,并列出两类操作:
- 快速、非阻塞操作:无论表多大都在毫秒级完成,仅短暂持有 AccessExclusiveLock 用于目录更新,而非重写数据。包括 ADD COLUMN(可空、无默认值)、ADD COLUMN ... DEFAULT x(PG 11+、非易变默认值)、DROP COLUMN、SET/DROP DEFAULT、CREATE INDEX CONCURRENTLY、DROP INDEX CONCURRENTLY、RENAME COLUMN、RENAME TABLE、ADD CONSTRAINT ... NOT VALID、VALIDATE CONSTRAINT、CREATE/DROP TRIGGER。
- 缓慢或阻塞操作:会重写表或扫描所有行,大表上可能锁住全部访问数秒到数小时。包括带易变默认值(如 now()、gen_random_uuid())的 ADD COLUMN、大多数 ALTER COLUMN TYPE、SET NOT NULL(PG < 12 或没有现有 CHECK 时)、带校验的 ADD CONSTRAINT CHECK/UNIQUE/FK、非并发 CREATE INDEX、CLUSTER、VACUUM FULL。
- 关键洞察:AccessExclusiveLock 会阻塞读写;即使操作本身很快,也必须等待所有进行中的事务结束才能获得锁,一个长查询或空闲事务可导致 ALTER TABLE 挂起,并让后续查询排队。
安全迁移模式
针对常见场景给出安全写法与不安全写法对比:添加列、删除列、重命名列、修改列类型、添加 NOT NULL 约束、添加外键、添加索引、添加唯一约束、重定义主键、删除约束。
- 添加列:可空无默认值列、PG 11+ 非易变默认值列为安全;易变默认值会全表重写,应改为「先加可空列、再批量回填、最后设默认值和 NOT NULL」。
- 删除列:瞬间完成,但需应用层停止引用;零停机部署需先部署不再读写该列的代码,再单独迁移删除。安全提示:DROP COLUMN 不会物理删除数据,敏感数据需 VACUUM FULL 或 dump/restore。
- 重命名列:瞬间元数据变更,但会破坏引用旧列名的应用代码、视图或函数;零停机可用列交换模式(新增列、双写、回填、切换读、删除旧列)。
- 修改列类型:多数会重写全表,安全替代是用新列加回填再加交换。部分转换不需要重写:VARCHAR(n)→VARCHAR(m)(m>n)、VARCHAR(n)→TEXT、NUMERIC(p,s)→NUMERIC(p2,s)(同 scale 且 p2>p)为元数据变更;INTEGER→BIGINT、TIMESTAMP→TIMESTAMPTZ 会全表重写。
- 添加 NOT NULL 约束:PG 18+ 可用简化两步模式;PG 12–17 在已有有效 CHECK 约束时较快,分三步(加 CHECK NOT VALID、VALIDATE、SET NOT NULL、删除冗余 CHECK);PG < 12 总会全表扫描。
- 添加外键:先用 NOT VALID 添加不校验,再 VALIDATE。
- 添加索引:用 CREATE INDEX CONCURRENTLY;并发建索引失败会留下 INVALID 索引,需检查并清理后重试。
- 添加唯一约束:先并发创建唯一索引,再将其附加为约束。
- 重定义主键:分步构建各要素——加 CHECK NOT NULL NOT VALID、VALIDATE、并发建唯一索引、删除旧主键、用现有索引添加新主键、删除冗余 CHECK。
- 删除约束:瞬间元数据变更;删除外键后若其支撑索引不再需要也可删除。
回填策略
始终分批回填,绝不使用单条 UPDATE;批量按主键模式、可恢复进度跟踪和调优指引见 backfill-strategies。
迁移验证
每次迁移前后运行验证查询,完整检查集见 validation-queries,包括 NULL 检测、重复检测、孤儿行、转换失败、耗时估算、模式验证、数据完整性、查询性能。
回滚规划
每次迁移执行前应记录回滚计划。可逆操作如 ADD COLUMN↔DROP COLUMN、ADD CONSTRAINT↔DROP CONSTRAINT、CREATE INDEX↔DROP INDEX、RENAME COLUMN x TO y↔RENAME COLUMN y TO x、SET DEFAULT↔恢复旧值或 DROP DEFAULT;不可逆操作包括 DROP COLUMN、有损转换的 ALTER COLUMN TYPE(如 NUMERIC→INTEGER、TEXT→VARCHAR(50))、DELETE/TRUNCATE、DROP TABLE,这些需从备份或数据库 fork 恢复。
事务策略
两种执行多条 DDL 的方式:包在一个事务中(要么全部成功要么全部回滚,适合原子性优先且操作都很快时);分开事务(每条 DDL 独立提交,适合锁持续时间优先时,因为单事务中所有锁会持有到 COMMIT)。注意 CREATE INDEX CONCURRENTLY、DROP INDEX CONCURRENTLY 不能在事务中使用。
处理长事务查询
快速 ALTER TABLE 可能因等待长查询释放 AccessExclusiveLock 而挂起,并阻塞后续所有查询。因此设置超时很关键:lock_timeout 是等待锁的时间,应加在每个生产 DDL 语句上;statement_timeout 是获得锁后语句可运行的时间。超时值选择有保守(50-100ms lock_timeout,多次重试)与务实(3-5s lock_timeout,少量重试)两种思路,取决于流量特征;statement_timeout 可设为预期耗时的宽裕倍数。若因锁超时失败,可查询 pg_stat_activity 找到阻塞者,等待或视情况取消,然后重试 ALTER TABLE。
适用场景
适用于 PostgreSQL 模式迁移的规划、测试与安全执行,尤其是在生产数据或共享数据库、大表场景下需要评估风险、用真实数据测试并安全执行迁移时。
限制
资料未提供具体价格、收费、平台支持范围或效果数据;也未提供独立安装命令。