oracle触发器无法捕获死锁,因死锁是事务管理器强制回滚的内部决策,不触发dml事件;正确方式是启用事件10027生成trace文件并解析,或在应用层pl/sql中用exception捕获ora-0061记录日志。

Oracle 触发器本身无法直接捕获死锁事件——因为死锁发生时,数据库已中止事务并抛出 ORA-0061(Transaction backed out due to deadlock),而触发器运行在事务上下文中,此时事务已回滚,触发器逻辑不会执行。
为什么不能用 AFTER UPDATE / BEFORE INSERT 触发器监听死锁
死锁不是 DML 事件,而是事务管理器在检测到循环等待后强制终止某一方的结果。它不触发任何标准的 INSERT/UPDATE/DELETE 事件,所以常规触发器根本收不到通知。你写一个 AFTER UPDATE ON orders,哪怕死锁发生在 orders 表上,这个触发器也不会被调用。
-
ORA-0061是会话级错误,在语句执行完、事务回滚后才返回给客户端 - 触发器只能响应显式 DML 或 DDL 事件,无法响应锁管理器的内部决策
- 试图在触发器里查
V$LOCK或V$SESSION_WAIT不仅无效,还可能因视图访问权限或时机问题报错
真正能拿到死锁 SQL 的方式:启用死锁跟踪 + 解析 trace 文件
Oracle 原生方案是让数据库自动生成死锁 trace 文件,里面包含完整的 blocking session、victim session、inputbuf(即导致死锁的最后一条 SQL)和 executionStack。关键配置只有两条:
- 确保
ALTER SYSTEM SET EVENTS '10027 trace name context forever, level 1';—— 开启死锁检测 trace(level 1 记录基本死锁信息) - 确认
DIAGNOSTIC_DEST参数指向可写路径,并检查background_dump_dest(旧参数)是否有效 - 死锁发生后,去
$ORACLE_BASE/diag/rdbms/<db_name>/<instance_name>/trace/</instance_name></db_name>找最新生成的*_ora_*.trc文件
文件里会明确出现类似这样的块:
Deadlock graph: --------- Blocker(s) -------- --------- Victim(s) --------- Resource Name process session holds waits process session holds waits TX-000a8c45-000001f7 47 123 X 48 124 X session 123: DID 0001-002F-00000001 session 124: DID 0001-0030-00000001 session 124: DID 0001-0030-00000001 session 123: DID 0001-002F-00000001 Rows waited on: Session 123: no row Session 124: obj - rowid = 00012F3A - AAAVqxAAEAAAAOgAAA Information on the OTHER waiting sessions: Session 124: pid=48 serial=12345 audsid=123456789 Current SQL Statement: UPDATE inventory SET qty = qty - 1 WHERE sku = 'ABC123'
应用层补救:用 EXCEPTION HANDLER 捕获 ORA-0061 并记录上下文
虽然触发器做不到,但你在 PL/SQL 存储过程中可以主动捕获死锁错误,并把当时正在执行的逻辑、绑定变量、时间戳记入日志表:
BEGIN
UPDATE orders SET status = 'shipped' WHERE order_id = v_id;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -61 THEN
INSERT INTO deadlock_log (
error_code, sql_text, bind_values, log_time
) VALUES (
-61,
'UPDATE orders SET status = :1 WHERE order_id = :2',
v_status || ',' || v_id,
SYSDATE
);
COMMIT;
END IF;
RAISE;
END;
- 必须用
WHEN OTHERS+SQLCODE = -61判断,DUP_VAL_ON_INDEX等预定义异常不覆盖死锁 - 不能在触发器里做这事——触发器里
COMMIT会报ORA-04092,且事务已回滚,INSERT 也无效 - 日志内容靠人工拼接,
sql_text字段建议存模板而非动态值,避免注入和长度溢出
最常被忽略的一点:死锁 trace 文件默认只保留最近几次,且不自动归档。如果你没配 ADRCI 自动清理策略或外部轮转脚本,关键 trace 很可能在几天后被覆盖——别等出问题才找,先确认 DIAGNOSTIC_DEST 下的 trace 目录有足够空间和保留策略。











