ora-00060死锁异常不可用when others直接精准捕获,必须用pragma exception_init绑定-60错误号;因它无预定义异常名,仅靠sqlcode=-60或自定义异常名才能区分场景、触发重试与显式回滚。
ora-00060 死锁异常必须用 pragma exception_init 显式绑定才能精准捕获,不能靠 when others 或字符串匹配。
为什么不能直接用 WHEN OTHERS 捕获死锁
死锁是 Oracle 主动检测并中止事务时抛出的非预定义异常(错误号 -60),PL/SQL 不提供像 NO_DATA_FOUND 那样的内置名称。若只写 WHEN OTHERS,你无法区分它是死锁、超时还是权限错误;更危险的是,WHEN OTHERS THEN NULL 会静默吞掉死锁,导致业务逻辑误以为操作成功,数据状态不一致。
-
SQLCODE在EXCEPTION块内才有效,WHEN OTHERS里不查SQLCODE = -60就等于盲抓 - 依赖
SQLERRM包含 "deadlock" 字样做判断——不同 Oracle 版本或 NLS 设置下提示可能为英文、中文甚至缩写,不可靠 - 嵌套块中若外层没声明该异常,内层即使发生
ORA-00060也会直接向上抛到调用方,当前块的WHEN OTHERS根本不执行
PRAGMA EXCEPTION_INIT 的正确声明位置和写法
必须在 PL/SQL 块的声明段(DECLARE 后、BEGIN 前)完成绑定,且每个异常名只能绑定一次。名字可自定义,但错误号必须是 -60(不是 60 或 '-60')。
DECLARE
DEADLOCK_EXCEPTION EXCEPTION;
PRAGMA EXCEPTION_INIT(DEADLOCK_EXCEPTION, -60); -- ✅ 正确:负号不能漏
BEGIN
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
EXCEPTION
WHEN DEADLOCK_EXCEPTION THEN
ROLLBACK;
INSERT INTO deadlock_log (log_time, sql_text)
VALUES (SYSTIMESTAMP, 'UPDATE accounts...');
RAISE; -- 重抛以便上层感知,或改用 RAISE_APPLICATION_ERROR(-20999, 'Deadlock retry needed')
END;
- 不能把
PRAGMA放在BEGIN之后,否则报PLS-00103 - 不能重复声明同一异常名,否则报
PLS-00112 - 异常名作用域仅限当前块,子程序里要用需各自重新声明
捕获后该做什么:别只记日志就完事
死锁不是程序 bug,而是并发资源争抢的正常现象,处理重点是“可控重试”,而非掩盖或告警。直接 RAISE 或 RAISE_APPLICATION_ERROR 是必须的,否则调用链上游完全不知道事务已失败。
- 必须在
EXCEPTION块内显式ROLLBACK,因为 Oracle 已回滚被选为牺牲者的语句,但整个事务未自动结束 - 记录日志时用
SYS_CONTEXT('USERENV', 'SESSIONID')和DBMS_UTILITY.FORMAT_CALL_STACK,方便定位竞争路径 - 不要在存储过程中静默重试——应用层更适合控制退避策略(如指数等待)和最大重试次数
- 若用
RAISE_APPLICATION_ERROR(-20999, ...),调用方需用SQLCODE = -20999捕获,不能靠名字
真正容易被忽略的是异常传播的“断点”行为:内层块捕获了 DEADLOCK_EXCEPTION 却没 RAISE,外层事务就以为一切正常,后续 DML 可能基于错误状态继续执行。死锁处理不是加个 WHEN 分支就结束的事,它牵扯整个事务边界的清晰界定。











