科技 · 实用指南
PostgreSQL 提示“当前事务已中止”?重试前先找到第一条错误
把 SQLSTATE 25P02 视为更早失败留下的状态:先保存第一条 SQLSTATE,确认连接与事务边界,再选择完整回滚或只回到失败前已经存在的保存点。

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、事务所有者、连接身份、恢复分支、丢失工作、重试决定与预防改动。缩短事务、显式上下文、预置保存点和保留首错日志都能降低复发风险。
