Watch at Ease看见世界,读懂生活。

科技 · 实用指南

PostgreSQL 提示“当前事务已中止”?重试前先找到第一条错误

把 SQLSTATE 25P02 视为更早失败留下的状态:先保存第一条 SQLSTATE,确认连接与事务边界,再选择完整回滚或只回到失败前已经存在的保存点。

一次数据库操作失败后,后续操作被中止事务状态阻止的原创插画。
原创插画:后续 25P02 是结果;恢复前先保存第一条失败操作。 Watch at Ease

PostgreSQL 的“当前事务已中止”通常不是根因,而是同一事务中更早语句失败后的结果。反复执行最后一条命令既不能修复事务,也可能掩盖第一条错误。

安全顺序是先保存首错证据,把它对应到准确连接和事务所有者,再选择有效的恢复分支,最后验证连接后才重试或归还连接池。

恢复前冻结第一条失败证据

25P02 表示同一事务里更早的语句已经失败,PostgreSQL 会忽略后续命令,直到事务被恢复。保存最早的服务器错误、SQLSTATE、语句名或安全指纹、参数类型、时间与调用栈,再执行回滚。

若第一条可见日志已经是 25P02,就扩大时间窗口,检查同一连接上更早的客户端自动语句、触发器、约束错误与异常;不要把密钥或完整个人数据写进事故记录。

确认准确连接和事务边界

记录应用实例、连接池、连接标识、数据库用户、后端进程号、自动提交设置与事务开始时间。一个请求可能跨多个连接,而一个池连接也可能先后服务多个请求。

找出谁开启了事务:显式 BEGIN、ORM 工作单元、迁移框架、笔记本、数据库客户端,还是驱动的隐式事务。Psycopg 默认连 SELECT 也会启动事务,除非启用 autocommit。

没有安全保存点时完整回滚

如果这次事务可以放弃,就调用驱动或框架的 rollback,并确认连接已离开失败状态后再重试。Psycopg 在操作失败后要求回滚,才能继续复用同一连接。

完整回滚会撤销事务内全部未提交修改。交互式修复或迁移前,要记录会丢失的工作和事务归属;不要用 COMMIT 试图掩盖错误。

保存点必须早于风险操作

只有在失败语句之前已经建立的保存点,才能保留更早工作。先执行 ROLLBACK TO SAVEPOINT,让事务恢复可用,再决定继续或释放保存点;看到 25P02 后才创建保存点已经太晚。

事务仍中止时 PostgreSQL 不允许 RELEASE SAVEPOINT;块内恢复要用 ROLLBACK TO。若保存点由驱动或框架创建,应使用其事务 API,避免客户端状态与服务器脱节。

检查连接池、ORM 和客户端隐藏语句

检查健康探针、计数查询、元数据读取、回调或自动 flush 是否先于界面显示的 SQL 执行。DBeaver 问题只用来证明这种需求分支真实存在,不代表你的客户端一定有同一缺陷。

连接池归还前要明确提交或回滚并核实空闲状态;ORM 要保存被包装前的第一条数据库异常;新开标签页可能暂时绕过坏连接,却没有解释根因。

重试完整工作单元,而非最后一条语句

回滚后先修复首错:语法、约束、序列化、权限、类型、超时或客户端自动 SQL。再从定义好的边界重试最小且幂等的业务单元。

依据首个 SQLSTATE 判断是否可重试。确定性约束错误需要修正输入,暂时并发错误可采用有上限的重试,权限错误则需要配置变更。

归还服务前验证连接干净

通过同一框架路径运行无害状态检查,确认下一事务能正常开始和结束;查看连接池重置日志或连接状态,不要仅凭没有新异常就认为清理成功。

事故记录应包含首个 SQLSTATE、事务所有者、连接身份、恢复分支、丢失工作、重试决定与预防改动。缩短事务、显式上下文、预置保存点和保留首错日志都能降低复发风险。

比较完整回滚与返回先前保存点两种恢复路径的原创插画。
原创插画:只有保存点早于风险操作且符合事务所有者预期时,才能走保存点分支。 Watch at Ease