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

科技 · 实用指南

PostgreSQL SQLSTATE 40001?应重试整个事务,而不是失败语句

记录 SQLSTATE、隔离级别与失败边界;回滚后在有限重试循环中重新执行全部读取和决策,并把不可逆副作用放到提交之后。

原创技术示意图: 原创图:40001 后从 BEGIN 重放完整事务,并把外部副作用放在提交之后。
原创技术示意图: 原创图:40001 后从 BEGIN 重放完整事务,并把外部副作用放在提交之后。 Watch at Ease

SQLSTATE 40001 是 PostgreSQL 的 serialization_failure。它可能在 Repeatable Read 或 Serializable 下出现,也可能直到 COMMIT 才返回。数据库拒绝的是整个事务尝试;只重跑最后一条语句会继续使用来自失效快照的应用决策。

安全处理器应包围整个事务回调和 COMMIT:确认 40001、回滚、有限退避并加入抖动,然后从 BEGIN 开始重新执行读取、决策和写入。数据库无法自动重放选择 SQL 与参数的应用逻辑。

重试不是掩盖争用。设置次数或时长预算并记录指标;持续失败要暴露出来。邮件、扣款、发布等不可逆操作应在提交后执行,可用事务外箱或幂等键连接数据库状态与后续交付。

重试前记录失败边界

记录五位 SQLSTATE、隔离级别、尝试次数、事务时长,以及错误来自语句还是 COMMIT。控制流程应匹配 SQLSTATE,不依赖本地化消息文本。

COMMIT 阶段失败并不反常;服务器可能需要完整读写集合才能判断是否存在安全串行顺序。

从 BEGIN 重放读取与决策

回滚后从事务第一次读取重新进入。存在性检查、计数、价格、库存、权限和派生值都必须重新计算。只重试失败的 UPDATE 或保存点会保留失效快照的假设。

让一次事务回调调用对应一次数据库尝试,并同时捕获回调与 COMMIT 的错误。

限制重试并显示争用

设置少量最大次数或总时长,采用指数或去相关退避加抖动。每次都重置事务与连接状态,不能继续使用已中止事务。

按操作统计重试。持续耗尽应检查长事务、热点键、索引访问、连接池和隔离级别需求,而不是直接降低隔离级别。

把副作用放在提交安全边界之外

重试体内的代码可能执行多次。不要在提交前发邮件、扣款、发布消息或调用非幂等 API。

可在同一事务写入 outbox,再由独立工作进程在提交后发送;外部系统支持时使用稳定业务请求生成的幂等键。

为相邻 SQLSTATE 选择不同闸门

40P01 死锁也会回滚并可重试,但需要诊断锁顺序。23505 有时像并发冲突,但自动重试要有应用级证明。

40003 表示完成状态未知,应先对账再重复;把所有 class 40 错误统一重试可能造成重复工作。

原创技术示意图: 原创表:按 SQLSTATE 区分 40001、40P01、23505 与 40003 的处理路径。
原创技术示意图: 原创表:按 SQLSTATE 区分 40001、40P01、23505 与 40003 的处理路径。 Watch at Ease