Oracle死锁判定必须验证循环等待链(A等B、B等A),仅查v$locked_object会漏判;job报警需联合检查启用状态、执行状态及最近日志,避免误报。
oracle死锁检查不能只靠 v$locked_object 单表查询就下结论——它只能反映“当前有行被锁住”,但无法区分是正常事务阻塞、长事务等待,还是真正的循环等待型死锁;而自动化报警若直接封装 check_jobs_gl 这类简单计数逻辑,会把 transient failure(如网络抖动导致 job 暂时失败)误判为故障,且完全不感知 job 是否已 broken 或被禁用。
怎么确认真的是死锁,而不是普通阻塞
死锁的判定必须满足“循环等待”条件:A 等 B 持有的锁,B 又等 A 持有的锁。仅查 v$locked_object 会漏掉关键链路信息。
- 先执行
SELECT * FROM v$lock WHERE block = 1找出正在阻塞别人的会话(block=1表示该会话持有锁并造成阻塞) - 再关联
v$session和v$lock,查出被阻塞会话的blocking_session字段是否反向指向原会话 —— 只有双向引用才构成死锁 -
v$transaction中的start_time和used_ublk可辅助判断是否为长事务假死锁;若used_ublk > 0且start_time超过 5 分钟,大概率是人为未提交,不是死锁
为什么 SELECT * FROM v$session WHERE sid IN (SELECT session_id FROM v$locked_object) 不够用
这条语句输出的只是“当前持有锁的对象”,但 Oracle 死锁检测器(LMD/LCK 进程)已在后台自动介入并回滚其中一个事务,此时 v$locked_object 可能早已为空,而你看到的只是残留的阻塞痕迹。
- 真正权威的死锁证据在
v$diag_info里找路径:SELECT value FROM v$diag_info WHERE name = 'Diag Trace',然后去对应 trace 目录下搜ORA-00060文件 -
v$session的state字段值为ACTIVE并不等于死锁;要看event字段是否为enq: TX - row lock contention且blocking_session非空 - 如果
lockwait字段为空,但status = 'ACTIVE'且sql_id对应的语句长时间没返回,大概率是锁升级(如 DML 触发了 TM 锁)或索引争用,不是死锁
怎么写一个不误报的 job 失败报警存储过程
单纯统计 failures 0 是危险的——job 可能刚失败一次就被自动重试成功,也可能因 broken = TRUE 被永久停用,但 failures 字段仍为 0。
- 必须联合查
USER_SCHEDULER_JOBS:重点看enabled(是否启用)、state(STOPPED或BROKEN都算异常)、last_start_date是否超期(比如每天跑的 job,last_start_date距今超过 25 小时就该告警) - 查
USER_SCHEDULER_JOB_LOG时,过滤条件要加status IN ('FAILED', 'STOPPED'),且限定log_date > SYSDATE - 1/24(最近一小时),避免历史失败干扰 - 发送邮件前,用
UTL_HTTP.get_response或UTL_SMTP连通性测试先验证 ACL 和 SMTP 配置是否有效,否则报警本身失败还无感知
报警脚本里最容易被忽略的三个细节
很多 DBA 把 send_mail_gl 改个名就上线,结果在生产环境静默失效。
-
DBMS_NETWORK_ACL_ADMIN.create_acl创建的 ACL 文件名必须全小写(如email_acl.xml),大写或驼峰命名会导致ORA-46059 - 调用
UTL_SMTP时,v_mailhost填的是域名就必须确保数据库服务器能nslookup解析成功;填 IP 更稳,但得确认防火墙放行了目标端口(不是所有邮件服务都走 25,企业微信/钉钉机器人常用 443) - 存储过程里所有
UTL_SMTP调用必须包裹在BEGIN...EXCEPTION WHEN OTHERS THEN ... END;中,且显式调用UTL_SMTP.quit(v_conn)—— 否则连接泄漏,跑几次就触发 ORA-29260











