本站文章除注明转载外,均为本站原创:转载自 love wife & love life —Roger 的Oracle技术博客
本文链接地址: 关于enq: TX – row lock contention的测试和案例分析
1、主键或唯一index
---session 1 SQL> select sid from v$mystat where rownum=1; SID ---------- 130 SQL> create table t1_tx(id number primary key,name varchar2(20)); Table created. SQL> insert into t1_tx values(1,'roger'); 1 row created. SQL> commit; Commit complete. SQL> insert into t1_tx values(2,'xxoo'); 1 row created. ---session 2 SQL> conn roger/roger Connected. SQL> select sid from v$mystat where rownum=1; SID ---------- 69 SQL> insert into t1_tx values(2,'xxoo'); ---一直处于等待状态 ---session 3 SQL> select sid, 2 chr(bitand(p1, -16777216) / 16777215) || 3 chr(bitand(p1, 16711680) / 65535) "Name", 4 (bitand(p1, 65535)) "Mode",event,sql_id,FINAL_BLOCKING_SESSION 5 from v$session 6 where event like 'enq%'; SID Name Mode EVENT SQL_ID FINAL_BLOCKING_SESSION ---------- ---- ----- ------------------------------ ------------- ---------------------- 69 TX 4 enq: TX - row lock contention b775wqk86zc6k 130 SQL> select sid,serial#,username,sql_id from v$session where sid=130; SID SERIAL# USERNAME SQL_ID ---------- ---------- ------------------------------ ------------- 130 185 ROGER SQL> select * from v$Lock where block=1; ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------- 2A4F2AAC 2A4F2AEC 130 TX 196612 895 6 0 736 1
可以看出,对于表存在主键或者 unique index 时,一个会话操作主键不提交时,其他会话如果也操作相同的主键时,那么必须进行等待,而其持有的mode=4;而阻塞blocker的持有mode=6.
2、Bitmap INDEX
--session 1 SQL> select * from t1_tx; ID NAME ---------- -------------------- 1 roger 2 roger 3 aa 4 aa SQL> create bitmap index idx_bitmap_name on t1_tx(name); Index created. SQL> select sid from v$mystat where rownum=1; SID ---------- 130 SQL> update t1_tx set name='tx' where id=3; 1 row updated. SQL> ---session 2 SQL> select sid from v$mystat where rownum=1; SID ---------- 69 SQL> update t1_tx set name='bitmap' where id=4; ---一直处于等待状态 ---session 3 SQL> select sid, 2 chr(bitand(p1, -16777216) / 16777215) || 3 chr(bitand(p1, 16711680) / 65535) "Name", 4 (bitand(p1, 65535)) "Mode",event,sql_id,FINAL_BLOCKING_SESSION 5 from v$session 6 where event like 'enq%'; SID Name Mode EVENT SQL_ID FINAL_BLOCKING_SESSION ---------- ---- ---------- ------------------------------- ------------- ---------------------- 69 TX 4 enq: TX - row lock contention 7wanaturqndn1 130 SQL> SQL> set lines 200 pagesize 200 SQL> select * from table(dbms_xplan.display_cursor('&sql_id', NULL, 'ALL')); Enter value for sql_id: 7wanaturqndn1 old 1: select * from table(dbms_xplan.display_cursor('&sql_id', NULL, 'ALL')) new 1: select * from table(dbms_xplan.display_cursor('7wanaturqndn1', NULL, 'ALL')) PLAN_TABLE_OUTPUT -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- SQL_ID 7wanaturqndn1, child number 0 ------------------------------------- update t1_tx set name='bitmap' where id=4 Plan hash value: 1842098942 ----------------------------------------------------------------------------------- | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ----------------------------------------------------------------------------------- | 0 | UPDATE STATEMENT | | | | 1 (100)| | | 1 | UPDATE | T1_TX | | | | | |* 2 | INDEX UNIQUE SCAN| SYS_C0010951 | 1 | 25 | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------- SQL> select * from v$Lock where block=1; ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------- 2A4F3100 2A4F3140 130 TX 262144 563 6 0 209 1 SQL> l 1* select * from v$Lock where block=1 SQL> / ADDR KADDR SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK -------- -------- ---------- -- ---------- ---------- ---------- ---------- ---------- ---------- 2A4F3100 2A4F3140 130 TX 262144 563 6 0 215 1 SQL> select sid,serial#,username,sql_id,event from v$session where sid=130; SID SERIAL# USERNAME SQL_ID EVENT ---------- ---------- ------------------------------ ------------- ----------------------------------------------------------------- 130 185 ROGER SQL*Net message from client SQL> select owner,index_name,index_type from dba_indexes where table_name='T1_TX'; OWNER INDEX_NAME INDEX_TYPE ------------------------------ ------------------------------ --------------------------- ROGER IDX_BITMAP_NAME BITMAP ROGER SYS_C0010951 NORMAL
我们可以看到,如果表上存在位图index,那么在update时,多个会话同时进行更新,必然出现tx 等待。此时waiter申请持有的tx 锁mode=4,而blocker持有的mode=6,而且通过v$session试图还无法查询到blocker会话到sql_id.
3、数据位于同一block
---session 1 SQL> select dbms_rowid.rowid_object(rowid) obj#, 2 dbms_rowid.rowid_relative_fno(rowid) rfile#, 3 dbms_rowid.rowid_block_number(rowid) block#, 4 dbms_rowid.rowid_row_number(rowid) row# 5 from t1_tx order by 4; OBJ# RFILE# BLOCK# ROW# ---------- ---------- ---------- ---------- 74762 4 30141 0 74762 4 30141 1 74762 4 30141 2 74762 4 30141 3 SQL> update t1_tx set name='enmotech' where id=2; 1 row updated. SQL> commit; Commit complete. ---session 2 SQL> update t1_tx set name='zhenxu' where id=4; 1 row updated. SQL> commit; Commit complete. SQL> 即使我分别开2个会话执行100w次,也不会出现tx锁 --session 1 SQL> declare 2 c number; 3 begin 4 for i in 1 .. 1000000 loop 5 update t1_tx set name='shit1' where id=2; 6 end loop; 7 end; 8 / PL/SQL procedure successfully completed. Elapsed: 00:00:26.58 SQL> ---session 2 SQL> declare 2 c number; 3 begin 4 for i in 1 .. 1000000 loop 5 update t1_tx set name='t-shit' where id=3; 6 end loop; 7 end; 8 / PL/SQL procedure successfully completed. --session 3 SQL> select inst_id,event,count(1) from gv$session where wait_class#<>6 group by inst_id,event order by 1,3; INST_ID EVENT COUNT(1) ---------- ----------------------------------------------------------------- ---------- 1 asynch descriptor resize 1 1 Log archive I/O 1 1 buffer busy waits 2
我们可以看到,不同会话更新同一block中到不同行,不会存在等待,假设更新同一行,那么不提交到情况执行,必然存在等待,这里不再累述。
4、外键
SQL> create table t1 (id number ,name varchar2(20),product_id number); Table created. SQL> create table t2 (id number primary key,name varchar2(20)); Table created. SQL> alter table t1 add constraint FK_PRODUCTID foreign key (PRODUCT_id) references t2 (ID); Table altered. SQL> SQL> select index_name,table_name from user_indexes where table_name='T1'; no rows selected SQL> SQL> insert into t2 values(1,'aa'); 1 row created. SQL> insert into t2 values(2,'dd'); 1 row created. SQL> insert into t2 values(3,'cc'); 1 row created. SQL> commit; Commit complete. SQL> insert into t2 values(5,'cc'); 1 row created. SQL> ---session 2 SQL> insert into t1 values(1,'xx',5); --子表操作会一直挂起
实际上我们可以发现,无论子表有没有主键约束,都会存在这种情况,只有主表操作不提交.
实际上还有一种更特殊到情况,也会出现,当然原理上来讲,也上主外键的问题,如下测试:
---session 1 SQL> conn roger/roger Connected. SQL> create table t3_ref (id number primary key,name varchar2(20),obj_id NUMBER); Table created. SQL> alter table t3_ref add constraint fk_id foreign key (obj_id) references t3_ref (id); Table altered. SQL> insert into t3_ref values(1,'roger',1); 1 row created. SQL> insert into t3_ref values(2,'roger',1); 1 row created. ---session 2 SQL> conn roger/roger Connected. SQL> insert into t3_ref values(3,'roger',2); ---一直处于等待 ---session 3 SQL> l 1 select sid, 2 chr(bitand(p1, -16777216) / 16777215) || 3 chr(bitand(p1, 16711680) / 65535) "Name", 4 (bitand(p1, 65535)) "Mode",event,sql_id,blocking_session,FINAL_BLOCKING_SESSION 5 from v$session 6* where event like 'enq%' SQL> / SID Name Mode EVENT SQL_ID BLOCKING_SESSION FINAL_BLOCKING_SESSION ---------- ---- ---------- -------------------------------- ------------- ---------------- ---------------------- 199 TX 4 enq: TX - row lock contention 8cj5awv9djrby 139 139
1. 其原因一般有如下几种:
1) 表上存在主键或唯一性约束,多个会话操作同一条记录
3) 表上存在位图Index,这跟uniqeue index中存在重复值是一样的道理,其中一个会话操作,其他会话必须等待.
2. 对于网上说的enq: TX – row lock contention也有可能是在等待index block分裂的情况,我没有进行测试, 从理论上来讲,如果是在等待index block分裂,那么应该还伴有enq: TX – index contention等待事件产生.
3. 对于enq: TX – row lock contention,通过v$session视图查询时,等待会话带lock mode通常为4,而blocker 会话带lock mode通常为6,并且一般查询blocker会话的sql_id都为空。这是正常现象,v$session显示是当前状态, 而非历史数据.
SQL> select inst_id,event,count(1) from gv$session where wait_class#<>6 group by inst_id,event order by 1,3; INST_ID EVENT COUNT(1) ---------- ----------------------------------------------------------------- ---------- 1 SQL*Net message to client 1 1 SQL*Net message from dblink 2 1 db file sequential read 4 1 library cache: mutex X 4 1 enq: TX - row lock contention 18 2 library cache: mutex X 1 2 db file sequential read 1 7 rows selected. SQL> select sid, chr(bitand(p1, -16777216) / 16777215) || 2 3 chr(bitand(p1, 16711680) / 65535) "Name", 4 (bitand(p1, 65535)) "Mode",event,sql_id,FINAL_BLOCKING_SESSION 5 from v$session 6 where event like 'enq%'; SID Name Mode EVENT SQL_ID FINAL_BLOCKING_SESSION ---------- ---- ---------- ----------------------------------- ------------- ---------------------- 207 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 1008 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 1168 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 1451 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 5286 1652 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 5286 2129 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 2207 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 5286 2723 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 5286 3095 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 4807 TX 6 enq: TX - row lock contention djbvcr351s0mh 1690 5015 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 5047 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 5213 TX 6 enq: TX - row lock contention djbvcr351s0mh 1690 5372 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 5286 5374 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 5732 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 6721 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 7608 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 7609 TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2810 19 rows selected.
这里通过dump 这几个process,然后过滤insert into 并没有发现针对party表的insert 操作。
于是尝试换一种思路,通过logminer 来分析进程的操作进程,如下:
SQL> select member from v$logfile where group#=1; MEMBER -------------------------------------------------- /crm/oradata01/redo01a.log /crm/oradata02/redo01b.log SQL> select sid, username, 2 chr(bitand(p1, -16777216) / 16777215) || 3 chr(bitand(p1, 16711680) / 65535) "Name", 4 (bitand(p1, 65535)) "Mode",event,sql_id,blocking_session,FINAL_BLOCKING_SESSION 5 from v$session 6 where event like 'enq%'; SID USERNAME Name Mode EVENT SQL_ID BLOCKING_SESSION FINAL_BLOCKING_SESSION ---------- ---------- ---- ---------- -------------------------------- ------------- ---------------- ---------------------- 123 CRM_APP TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2802 2802 689 CRM_APP TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2802 2802 934 CRM_APP TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 3128 3128 4128 CRM_APP TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2802 2802 4449 CRM_APP TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2802 2802 6324 CRM_APP TX 4 enq: TX - row lock contention 4fpb7rfm3fb3b 2802 2802 6 rows selected.
SQL> SELECT t.xidusn, t.xidslot, t.xidsqn, t.start_time, t.start_scn 2 FROM v$transaction t JOIN v$session s ON t.addr = s.taddr 3 WHERE s.sid in (123,689,934,4128,4449,6324) 4 / XIDUSN XIDSLOT XIDSQN START_TIME START_SCN ---------- ---------- ---------- -------------------- ---------- 743 28 61461 07/11/15 22:09:12 1.4484E+13 828 26 61559 07/11/15 22:07:22 1.4484E+13 918 5 57068 07/11/15 22:08:12 1.4484E+13 820 1 64176 07/11/15 22:07:11 1.4484E+13 1060 16 54417 07/11/15 22:08:12 1.4484E+13 816 3 62830 07/11/15 22:07:11 1.4484E+13 6 rows selected.
SQL> SELECT t.xidusn, t.xidslot, t.xidsqn, t.start_time, t.start_scn 2 FROM v$transaction t JOIN v$session s ON t.addr = s.taddr 3 WHERE s.sid in (2802,3128) 4 / XIDUSN XIDSLOT XIDSQN START_TIME START_SCN ---------- ---------- ---------- -------------------- ---------- 949 31 79938 07/11/15 22:06:22 1.4484E+13 1061 20 57965 07/11/15 22:06:11 1.4484E+13
SQL> EXECUTE dbms_logmnr.add_logfile(logfilename=>'/crm/oradata01/redo01a.log'); PL/SQL procedure successfully completed. SQL> EXECUTE dbms_logmnr.start_logmnr(options=>dbms_logmnr.dict_from_online_catalog); PL/SQL procedure successfully completed. SQL> create table tmp_logmnr_contents as select * from v$logmnr_contents; Table created. SQL> EXECUTE dbms_logmnr.end_logmnr ; PL/SQL procedure successfully completed.
最后查询发现blocker和waiter执行的SQL都类似,因此这就很容易说明问题了. 由于logminer抓取的SQL涉及到客户信息,因此这里不便贴出来。这里只是给大家提供一种思路,对于TX锁的分析,也是可以利用logminer来做的。
最后分析发现,本质上来讲,就是因为前后会话操作相同的数据导致,而表上有存在主键,这必然导致出现TX锁等待。
PS:或许有人会说,为什么不直接查v$试图抓取sql的绑定变量,实际上我这里已经查过,没有查到,而且通过dump processstate也没有发现,因此才想到利用logminer来分析问题,找到根本原因。