Oracle包死锁排查请求:定位定时批处理失败的锁定对象
这种生产环境下频繁触发的死锁问题真的让人头大,尤其是还影响到定时批处理的稳定性。我给你几个在生产环境安全可行的排查步骤,帮你精准定位到被其他进程锁定的具体对象:
1. 立刻抓取死锁发生时的实时锁信息
Oracle会自动把死锁记录到alert日志和跟踪文件,但直接看这些文件效率不高。建议在死锁发生后马上执行下面的查询,拿到最直观的锁关联数据:
- 查询当前所有锁等待的会话,同时关联锁定的对象:
SELECT s.sid, s.serial#, s.username, s.program, l.type, l.id1, l.id2, l.lmode, l.request, o.object_name, o.object_type FROM v$lock l JOIN v$session s ON l.sid = s.sid LEFT JOIN dba_objects o ON l.id1 = o.object_id WHERE l.request > 0 -- 只筛选正在等待锁的会话 OR (l.lmode IN (6,7) AND l.id1 IN (SELECT id1 FROM v$lock WHERE request>0)); -- 同时找出持有排他/共享锁的阻塞会话
这里要重点关注:*l.lmode=6*代表排他锁(X锁),是最容易引发死锁的锁类型;*l.lmode=7*是共享锁(S锁);通过dba_objects关联就能直接拿到被锁定的表或对象名称。
- 查看最近发生的死锁记录(11g及以上版本可用):
SELECT * FROM v$deadlock;
如果是老版本Oracle,可以通过AWR视图查询死锁时间范围内的会话:
SELECT ash.session_id, ash.session_serial#, ash.sql_id, ash.blocking_session, o.object_name, o.object_type FROM dba_hist_active_sess_history ash LEFT JOIN dba_objects o ON ash.current_obj# = o.object_id WHERE ash.event = 'enq: TX - row lock contention' -- 死锁最常见的等待事件 AND ash.sample_time BETWEEN SYSDATE - 1/24 AND SYSDATE; -- 查询最近1小时的记录
2. 先定位你的批处理会话
因为你的批处理是每10分钟定时触发的,先找到它的会话标识会让排查更精准:
SELECT sid, serial#, username, program, sql_id FROM v$session WHERE program LIKE '%你的批处理程序标识%' -- 替换成实际的程序名(比如Job名称) OR username = '批处理使用的数据库用户'; -- 如果知道用户名可以直接筛选
拿到批处理会话的sid后,回到第一步的锁查询里,就能快速找到它被哪个会话阻塞,以及对应的锁定对象。
3. 分析锁定对象的访问模式
当你拿到具体的锁定对象后,需要进一步分析它的使用情况:
- 检查这个对象是否被其他定时任务、业务应用频繁读写?比如有没有其他批量更新/删除任务和你的批处理时间重叠?
- 查看你的批处理SQL是否有优化空间?比如批量操作时有没有走索引?如果是行级锁冲突,大概率是批量操作扫描到的行被其他会话持有锁。
- 用AWR视图查看对象的锁历史(需要开启AWR):
SELECT lock_type, mode_held, mode_requested, object_name, sample_time, session_id, blocking_session FROM dba_hist_lock l JOIN dba_objects o ON l.object_id = o.object_id WHERE o.object_name = '锁定的对象名' -- 替换成实际对象名称 ORDER BY sample_time DESC;
4. 生产环境排查的注意事项
- 绝对不要随便在生产环境执行
ALTER SYSTEM KILL SESSION,除非你确认阻塞会话是无害的,或者已经获得运维团队的许可。 - 尽量在死锁发生的高峰期(比如批处理触发后的3-5分钟内)执行查询,这样拿到的信息最准确。
- 如果你的批处理是用
DBMS_SCHEDULER创建的,可以查看它的运行日志获取更多上下文:
SELECT log_id, log_date, status, error#, additional_info FROM dba_scheduler_job_run_details WHERE job_name = '你的批处理Job名称' ORDER BY log_date DESC;
内容的提问来源于stack exchange,提问作者Nandy
相关产品推荐
相关产品推荐

