创建员工ID级联更新Oracle触发器执行时报deadlock detected死锁错误
死锁错误的根本原因拆解
核心冲突来自自治事务的锁上下文隔离
- 你在触发器中声明的
PRAGMA AUTONOMOUS_TRANSACTION,会把整个触发器逻辑从外层主事务中剥离,成为一个独立运行的事务上下文,和执行UPDATE employees的外层事务在数据库层面是完全独立的两个会话级执行单元,不存在锁权限共享。 - 外层主事务执行员工表更新时,会先给匹配到目标员工的行加上排他行锁,该锁仅在事务完成提交/回滚后才会释放,过程中其他所有独立事务都无法获取该行的任何共享锁权限。
- 如果没有加自治事务,触发器内的更新操作属于外层主事务的一部分,同一事务内申请自己已持有的锁时,数据库会直接放行,不会触发锁等待。但拆成独立自治事务后会出现循环等待:外层主事务需要等触发器逻辑执行完成才能继续流程;触发器内的更新操作需要申请员工行的共享锁,却发现锁被外层未提交的主事务占用,永远等不到锁释放,直接被数据库判定为死锁。
外键约束的锁检查机制会放大上述冲突
- Drivers、Radiomen表上指向employees主键的外键约束,在执行子表外键字段更新时,数据库会自动触发强制锁检查:
- 校验新外键值在父表中存在,会主动给父表上对应新员工ID的行申请共享锁
- 校验旧外键值对应的关联关系完整性,会给旧外键值对应的父表行申请共享锁
- 本例中你要写入的新外键值,就是外层事务正在更新、尚未提交的员工ID,对应的排他锁本来就由外层主事务持有,自治事务的共享锁申请永远无法满足,直接形成死锁闭环。
代码本身的附加缺陷
- 触发器内第一行
UPDATE radiomen SET employees_id_employee = :NEW.id_employee WHERE employees_id_employee = :OLD.id_employee语句末尾漏了分号,实际执行时会触发SQL语法解析错误,和本次死锁问题无直接关联,但属于代码本身的疏漏。
之前的认知偏差来源:默认行级触发器逻辑会完全跟随外层主事务运行,没有意识到自治事务把原本同一事务内的自循环锁访问,拆成了两个独立事务的跨会话锁等待,才触发了数据库的死锁检测逻辑。
内容的提问来源于stack exchange,提问作者Ciuboss
相关产品推荐
相关产品推荐

