PostgreSQL下如何确保Alembic在ADD与VALIDATE CONSTRAINT间提交?
在Alembic中实现PostgreSQL非锁定Check约束的分阶段创建
要确保ADD CONSTRAINT ... NOT VALID和VALIDATE CONSTRAINT在独立事务中执行,有两种可靠方案,且都不需要关闭PostgreSQL的事务性DDL:
方案一:拆分到两个独立的Alembic迁移版本
这是最符合Alembic设计规范的做法:
- Alembic默认每个迁移版本的
upgrade()/downgrade()逻辑会在独立事务中执行,执行完成后自动提交事务。 - 把两步操作分别放在两个版本文件中:
- 第一个版本只执行添加NOT VALID约束:
def upgrade(): op.execute("ALTER TABLE foo ADD CONSTRAINT foo_check_bar CHECK (bar > 0) NOT VALID") def downgrade(): op.execute("ALTER TABLE foo DROP CONSTRAINT foo_check_bar") - 第二个版本只执行验证约束:
def upgrade(): op.execute("ALTER TABLE foo VALIDATE CONSTRAINT foo_check_bar") def downgrade(): # VALIDATE操作无法回滚,降级时只需保留NOT VALID约束或直接删除 pass
- 第一个版本只执行添加NOT VALID约束:
- 执行迁移时,两个版本会依次运行,各自的事务独立提交,完全满足PostgreSQL对并发友好的约束创建要求。
方案二:在同一个迁移版本中手动控制事务提交
如果不想拆分版本,可以通过手动提交事务实现两步分离,但需注意打破Alembic默认的单事务逻辑:
from alembic import op def upgrade(): # 第一步:添加NOT VALID约束 op.execute("ALTER TABLE foo ADD CONSTRAINT foo_check_bar CHECK (bar > 0) NOT VALID") # 获取数据库连接并手动提交当前事务 conn = op.get_bind() conn.commit() # 第二步:验证约束 op.execute("ALTER TABLE foo VALIDATE CONSTRAINT foo_check_bar") def downgrade(): op.execute("ALTER TABLE foo DROP CONSTRAINT foo_check_bar")
- 这种方式同样能保证两步在独立事务中执行,且不会关闭事务性DDL,但回滚时需注意:第一步的约束已提交,降级只能直接删除约束,无法回到未验证状态。
注意事项
- 优先选择方案一,它更符合迁移脚本的原子性原则,便于后续的追踪、回滚和维护。
- 无论哪种方案,都要确保在验证约束期间,业务的并发写入会自动遵守新约束(PostgreSQL在添加NOT VALID约束后,新写入的数据会被检查)。
内容的提问来源于stack exchange,提问作者Philip Couling
相关产品推荐
相关产品推荐

