提到enq: TX - row lock contention,你会想到什么

1160阅读 0评论2021-05-29 brjl
分类:Oracle


行锁,更新冲突,事务没提交。。。

来自真实世界的情况是

  1. --查被阻塞会话
  2. set lin 200 pages 1000
  3. col USERNAME for a10
  4. col PROGRAM for a40
  5. col EVENT for a30
  6. col WAITING_SESSION for a20

  7. WITH tkf_block_info AS
  8.  (SELECT a.inst_id || '_' || a.sid waiting_session,
  9.          a.username, a.program, a.event, a.sql_id, a.last_call_et,
  10.          DECODE(a.blocking_instance || '_' || a.blocking_session,
  11.                 '_', NULL, a.blocking_instance || '_' || a.blocking_session) holding_session
  12.     FROM gv$session a,
  13.          (SELECT inst_id, sid
  14.             FROM gv$session
  15.            WHERE blocking_session IS NOT NULL
  16.           UNION
  17.           SELECT blocking_instance, blocking_session
  18.             FROM gv$session
  19.            WHERE blocking_session IS NOT NULL) b
  20.    WHERE a.inst_id = b.inst_id
  21.      AND a.SID = b.sid)
  22. SELECT LPAD(' ', 3 * (LEVEL - 1)) || waiting_session waiting_session,
  23.        username, program, event, sql_id, last_call_et
  24.   FROM tkf_block_info
  25. CONNECT BY PRIOR waiting_session = holding_session
  26.  START WITH holding_session IS NULL;



这是一个阻塞树(先给自己下个拌儿),最上面的实例2 的2235阻塞了其他几个会话,而且这个会话已经空闲状态,等客户端指令。

应该有未结束的事务,可以先搂一眼(繁忙的系统就不要看了)。

  1. --未提交的事务
  2. col username for a8
  3. col MACHINE for a20
  4. col PROGRAM for a25 trunc
  5.  
  6. SELECT t.start_time , s.sid, s.serial#, s.username, s.status, s.program,sql_id
  7.   -- s.machine, s.module,s.osuser,s.terminal,t.UBABLK,t.UBAREC,t.STATUS
  8.    , to_char(s.logon_time,'yy/mm/dd HH24:MI:SS') logon_time
  9. FROM v$transaction t, v$session s
  10. WHERE s.saddr = t.ses_addr
  11. ORDER BY start_time;

首要的是判定大家都在争抢啥

  1. --看这些session在等什么对象

  2. col owner for a12
  3. col object_name for a30
  4. col object_type for a15

  5. select owner,object_name,object_type from dba_objects where object_id in(
  6. select ROW_WAIT_OBJ# /*,ROW_WAIT_FILE#,ROW_WAIT_BLOCK#,ROW_WAIT_ROW# */ from v$session
  7. where event='enq: TX - row lock contention');

用什么语句抢

  1. --等待执行的sql
  2. col sql_text for a60

  3. select sid,sql_text from v$session a,v$sql b where event='enq: TX - row lock contention'
  4. and (b.sql_id=a.sql_id ) order by 1,2;

如果没判定出原因,那就就从头梳理,先看看锁类型

  1. select sid,
  2.        chr(bitand(p1, -16777216) / 16777215) ||
  3.        chr(bitand(p1, 16711680) / 65535) "Name",
  4.        (bitand(p1, 65535)) "Mode"
  5.   from v$session_wait
  6.  where event 'enq: TX - row lock contention';
再看关联的事务
  1. col tx for a30

  2. SELECT
  3.    sid, seq#, state, seconds_in_wait,
  4.    'TX-'||lpad(ltrim(p2raw,'0'),8,'0')||'-'||lpad(ltrim(p3raw,'0'),8,'0') TX,
  5.    trunc(p2/65536) XIDUSN,
  6.    trunc(mod(p2,65536)) XIDSLOT,
  7.    p3 XIDSQN
  8.   FROM v$session_wait
  9.  WHERE event='enq: TX - row lock contention'



WAITEVENT:“enq:TX - 行锁争用”参考说明(文档 ID 1966048.1)

回到问题开头,为什么有个空闲”会话阻塞了一堆会话呢?
而且进一步跟踪 v$active_session_history中sql_mode<>3(排除掉查询),也没发现相关操作,根据客户描述是a公司开发的模块阻塞了b公司的更新操作。

为什么 ash中没有发现相关sql ?
不难解释,可能是事务太小,每秒一次的会话信息也没有采样到。

a公司的到底更新了哪些数据呢?
也不用解释,直接查行锁争用事件对应的object即可,详细的可以再查事务。

最关键的疑问:为什么a公司的会话没有提交事务呢?
这个就不好查了,也许防火墙、也许网络中断、也许sql性能差超出连接池的阈值...
应该不是GC收集,因为长时间不释放锁,目前都是 kill 才释放。

检查侦听日志、检查网卡丢包情况、请a公司程序员吃饭...

跟进结果是阻塞源头的事务是一个应用事务处理,处理中包含有web服务调用,而web服务处理出现故障,一直处于阻塞等待状态,导致事务长时间未提交。

解决思路是:在web服务调用中加入了超时失败事务回滚机制。

上一篇:io不好,oracle先看这里
下一篇:半导体、半决赛以及半联结 exists