PostgreSQL回滚未释放锁/死锁无法解决:异常处理器引发的问题
异常处理器导致锁未释放的原因解析
核心逻辑:PostgreSQL异常块的保存点机制 vs 事务级回滚的差异
PostgreSQL里的BEGIN...EXCEPTION块,本质是基于**保存点(savepoint)**实现的,和Oracle的异常处理逻辑有关键区别:
- 进入异常块时,PostgreSQL会自动创建一个保存点
- 当块内触发异常(比如死锁),首先会回滚到这个保存点,撤销块内的所有操作,但整个事务并没有被终止
- 哪怕你在异常块里
RAISE重抛异常,这个异常只会向上传递,只有当它最终到达**事务顶层(没有被任何上层异常块捕获)**时,才会触发整个事务的完全回滚
对应你的场景:
你用的是非顶层异常处理器,意味着这个异常块嵌套在更大的事务上下文里:
- 第一次死锁触发后,异常块回滚到自身的保存点,重抛的异常被上层的重试循环捕获
- 此时整个事务并没有终止,会话之前持有的锁(哪怕是异常块内操作获取的锁,PostgreSQL保存点回滚不会释放锁)依然被持有
- 重试时,同一个会话在当前存活的事务里继续执行插入,锁没释放,自然反复触发死锁
而移除异常处理器后,死锁异常直接传播到事务顶层,PostgreSQL会立即终止整个事务并完全回滚,所有绑定到该事务的锁都会被释放。重试时是开启新的事务,没有锁残留,因此可以正常执行。
关键细节:
PostgreSQL的锁是和整个事务生命周期绑定的,不是和单个代码块绑定。保存点回滚只撤销数据修改,不会释放事务持有的任何锁——只有事务完全终止(提交或回滚),锁才会被释放。
内容的提问来源于stack exchange,提问作者Goran Stefanović
相关产品推荐
相关产品推荐

