oracle存储过程无法预防死锁,但可捕获ora-00060错误并重试:需在exception中显式捕获,配合随机延迟、次数限制(如≤3次)及savepoint回滚。
oracle 存储过程中无法“预防”死锁,但可以捕获并重试——关键在正确识别 ora-00060 并控制重试边界。
如何捕获 ORA-00060 死锁错误并进入重试逻辑
Oracle 不提供死锁发生前的钩子,只能在 EXCEPTION 块中捕获已发生的死锁。注意:ORA-00060 是唯一需关注的死锁错误码,其他如 ORA-00054(资源忙)不是死锁。
必须用 EXCEPTION 显式匹配,不能依赖通用 WHEN OTHERS 后再判断 SQLCODE,否则会掩盖真实异常:
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -60 THEN
-- 这样写风险高:WHEN OTHERS 已吞掉堆栈,调试困难
END IF;
推荐写法:
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
NULL; -- 其他业务异常
WHEN OTHERS THEN
IF SQLCODE = -60 THEN
DBMS_OUTPUT.PUT_LINE('Deadlock detected, will retry...');
-- 重试逻辑
ELSE
RAISE; -- 非死锁异常必须重新抛出
END IF;
重试时为什么必须加随机延迟和次数限制
不加延迟的立即重试大概率再次触发死锁——两个会话同步重试,又撞上同一资源顺序。Oracle 的死锁检测是事后机制,重试窗口内竞争依然存在。
重试策略要点:
- 使用
DBMS_RANDOM.VALUE(0, 0.1)生成毫秒级随机休眠,避免共振 - 最多重试 3 次,超过则让调用方处理(如返回错误或落库待人工干预)
- 每次重试前用
SAVEPOINT回滚到事务起点,避免部分更新残留
示例片段:
DECLARE v_retry_count PLS_INTEGER := 0; v_max_retries CONSTANT PLS_INTEGER := 3; BEGIN > WHILE v_retry_count <h3>哪些操作根本不能靠重试解决死锁</h3><p>重试只对“短暂竞争型死锁”有效,比如两个会话按不同顺序更新同一组行。但以下场景重试无意义,必须从设计层面规避:</p>
- 长事务中嵌套调用多个存储过程,且各过程锁表顺序不一致 —— 应统一约定锁表顺序(如总是先
UPDATE order_header再UPDATE order_line) - 在触发器里执行 DML —— 触发器内锁行为不可控,极易引发隐式死锁,应移出触发器
- 使用
SELECT ... FOR UPDATE NOWAIT失败后直接重试 ——NOWAIT报ORA-00054,不是死锁,需区分处理 - 批量处理用
FORALL+SAVE EXCEPTIONS时发生死锁 —— 异常集合里不会包含ORA-00060,它会中断整个批处理,需改用逐条带重试的循环
死锁重试不是银弹。真正健壮的存储过程,80% 功夫花在锁顺序标准化、事务粒度控制和前置 SELECT 的隔离级别选择上,剩下 20% 才是 ORA-00060 的兜底重试逻辑。











