暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

oracle 等待事件 enq: TM – contention 处理

原创 黑獭 2025-01-19
745

先说结论:
enq: TM - contention 这个等待事件出现一般都有下面几种情况:
1 子表没有索引,这个最常见。
2 并行 DML
3 如果发生在 RAC 集群里,gc问题也可能造成相关等待事件
4 利用lock table 主动获取TM锁

第一部分:子表没有索引 测试

创建测试表:
CREATE TABLE T2001
( supplier_id number(10) not null,
supplier_name varchar2(50) not null,
contact_name varchar2(50),
CONSTRAINT supplier_pk PRIMARY KEY (supplier_id)
);

INSERT INTO T2001 VALUES (1, ‘Supplier 1’, ‘Contact 1’);
INSERT INTO T2001 VALUES (2, ‘Supplier 2’, ‘Contact 2’);
INSERT INTO T2001 VALUES (3, ‘Supplier 3’, ‘Contact 3’);
INSERT INTO T2001 VALUES (4, ‘Supplier 4’, ‘Contact 4’);
INSERT INTO T2001 VALUES (5, ‘Supplier 5’, ‘Contact 5’);

COMMIT;

CREATE TABLE T4001
( product_id number(10) not null,
product_name varchar2(50) not null,
supplier_id number(10) not null,
CONSTRAINT fk_supplier
FOREIGN KEY (supplier_id)
REFERENCES T2001(supplier_id)
ON DELETE CASCADE
);

INSERT INTO T4001 VALUES (1, ‘Product 1’, 1);
INSERT INTO T4001 VALUES (2, ‘Product 2’, 1);
INSERT INTO T4001 VALUES (3, ‘Product 3’, 2);
INSERT INTO T4001 VALUES (4, ‘Product 4’, 4);

COMMIT;

测试1
session 1: DELETE T2001 WHERE supplier_id = 1; --不提交
session 2: DELETE T2001 WHERE supplier_id = 2; – (现象HANG住)
session 3: DELETE T4001 WHERE product_id = 2; --(现象HANG住)

杀掉会话1 后先执行的是会话3,2等待会话3执行完后再执行。

查询锁和阻塞情况:
SQL>
select INST_ID,
SID,
TYPE,
ID1,
ID2,
LMODE,
REQUEST,
CTIME,
BLOCK,
DECODE(BLOCK, 0, ‘’, ‘blocker’) blocker,
DECODE(request, 0, ‘’, ‘waiter’) waiter
from gvlockwhere(ID1,ID2,TYPESQL>2345678910111213)in(selectID1,ID2,TYPEfromgvlock where (ID1, ID2, TYPESQL> 2 3 4 5 6 7 8 9 10 11 12 13 ) in (select ID1, ID2, TYPE from gvlock where request > 0)
order by blocker; 14 15

INST_ID SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK BLOCKER WAITER


 1	   11 TM   10316098	     0		3	   0	    108 	 1 blocker
 1	  382 TM   10316098	     0		0	   3	     82 	 0	   waiter
 1	  136 TM   10316098	     0		0	   5	     89 	 0	   waiter

SQL> col spid format a10
col PROCESS format a10
col machine format a25
col PROGRAM format a25
col type format a10

SELECT p.spid,s.sid,s.MACHINE,s.PROCESS,s.PROGRAM,s.type,s.BLOCKING_SESSION
from gvprocessp,gvprocess p, gvsession s
where p.ADDR = s.PADDR
AND s.sid in
(select c.sid
from (select distinct a.blocking_instance, a.BLOCKING_SESSION
from gvsessionawhereblockingsessionisnotnull)bjoingvsession a where blocking_session is not null) b join gvsession c
on b.blocking_instance = c.SQL> SQL> SQL> SQL> SQL> SQL> 2 3 4 5 6 7 8 9 10 INST_ID
and b.blocking_session = c.SID and c.BLOCKING_SESSION is null and c.BLOCKING_SESSION_STATUS != ‘VALID’
);
11 12
SPID SID MACHINE PROCESS PROGRAM TYPE BLOCKING_SESSION


27267 11 zc 27266 sqlplus@zc (TNS V1-V3) USER

测试2:
session 1: DELETE T4001 WHERE product_id = 2; --不提交
session 2: DELETE T2001 WHERE supplier_id = 2; (现象HANG住)

阻塞情况:
select INST_ID,
SID,
TYPE,
ID1,
ID2,
LMODE,
REQUEST,
CTIME,
BLOCK,
DECODE(BLOCK, 0, ‘’, ‘blocker’) blocker,
DECODE(request, 0, ‘’, ‘waiter’) waiter
from gvlockwhere(ID1,ID2,TYPE)in(selectID1,ID2,TYPEfromgvlock where (ID1, ID2, TYPE) in (select ID1, ID2, TYPE from gvlock where request > 0)
order by blocker;
SQL> 2 3 4 5 6 7 8 9 10 11 12 13 14 15
INST_ID SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK BLOCKER WAITER


 1	   11 TM	   10316098	     0		3	   0	     19 	 1 blocker
 1	  136 TM	   10316098	     0		0	   5	     12 	 0	   waiter

SQL>
SQL>
col spid format a10
col PROCESS format a10
col machine format a25
col PROGRAM format a25
col type format a10

SELECT p.spid,s.sid,s.MACHINE,s.PROCESS,s.PROGRAM,s.type,s.BLOCKING_SESSION
from gvprocessp,gvprocess p, gvsession s
where p.ADDR = s.PADDR
AND s.sSQL> SQL> SQL> SQL> SQL> SQL> SQL> 2 3 4 id in
(select c.sid
from (select distinct a.blocking_instance, a.BLOCKING_SESSION
from gvsessionawhereblockingsessionisnotnull)bjoingvsession a where blocking_session is not null) b join gvsession c
on b.blocking_instance = c 5 6 7 8 9 10 .INST_ID
and b.blocking_session = c.SID and c.BLOCKING_SESSION is null and c.BLOCKING_SESSION_STATUS != ‘VALID’
); 11 12

SPID SID MACHINE PROCESS PROGRAM TYPE BLOCKING_SESSION


27267 11 zc 27266 sqlplus@zc (TNS V1-V3) USER

测试3
create index T40011 on T4001(supplier_id);

session 1: DELETE T4001 WHERE product_id = 2; --不提交
session 2: DELETE T2001 WHERE supplier_id = 2; 正常删除

第二部分 并行insert

开启多个session执行下面的sql
SQL> insert /+append no logging/ into test select * from test ;

1162896 rows created.

查询阻塞情况:
SQL> select INST_ID,
SID,
TYPE,
ID1,
ID2,
LMODE,
REQUEST,
CTIME,
BLOCK,
DECODE(BLOCK, 0, ‘’, ‘blocker’) blocker,
DECODE(request, 0, ‘’, ‘waiter’) waiter
from gvlockwhere(ID1,ID2,TYPE)2345678910111213in(selectID1,ID2,TYPEfromgvlock where (ID1, ID2, TYPE) 2 3 4 5 6 7 8 9 10 11 12 13 in (select ID1, ID2, TYPE from gvlock where request > 0)
order by blocker;
14 15
INST_ID SID TYPE ID1 ID2 LMODE REQUEST CTIME BLOCK BLOCKER WAITER


 1	   11 TM	      73885	     0		6	   0	     33 	 1 blocker
 1	  382 TM	      73885	     0		0	   6	     18 	 0	   waiter
 1	  136 TM	      73885	     0		0	   6	     19 	 0	   waiter

SQL>
SQL> select sid,event from v$session where sid in (382,136);

   SID EVENT

   136 enq: TM - contention
   382 enq: TM - contention

第三部分:利用lock table 主动获取TM锁 或者异常的 DDL

利用lock table 主动获取TM锁

利用lock table…语句有意获取TM锁时,可能发生TM锁争用。

session 1: update test set object_id=1 where owner=‘ZC’;
session 2: lock table test in exclusive mode;

如上所示,会话1 上因update以sub-exclusive模式获得TM锁的状态下,会话2利用lock table命令试图以exclusive模式获得TM锁,如果发生争用,则等待enq:TM-contention事件。

SQL> select INST_ID,
SID,
TYPE,
ID1,
ID2,
LMODE,
REQUEST,
CTIME,
BLOCK,
DECODE(BLOCK, 0, ‘’, ‘blocker’) blocker,
DECODE(request, 0, ‘’, ‘waiter’) waiter
from gvlockwhere(ID1,ID2,TYPE)2345678910111213in(selectID1,ID2,TYPEfromgvlock where (ID1, ID2, TYPE) 2 3 4 5 6 7 8 9 10 11 12 13 in (select ID1, ID2, TYPE from gvlock where request > 0)
order by blocker;
14 15
INST_ID SID TY ID1 ID2 LMODE REQUEST CTIME BLOCK BLOCKER WAITER


 1	   11 TM      73885	     0		3	   0	     93 	 1 blocker
 1	  136 TM      73885	     0		0	   6	     86 	 0	   waiter

SQL> select sid,event from v$session where sid=136;

   SID EVENT

   136 enq: TM - contention

第三部分:解决方案

如果是第一种情况,根据下面的sql进行查询,查询出哪些外键上面没有索引,建立相应的索引

如果子表有多个父表,我们需要列出子表的所有父表的相关信息:
select
co.owner c_owner,
co.table_name c_table_name,
cc.column_name c_column_name,
co.constraint_name cons_constraint_name,
co.constraint_type,
cpc.table_name p_table,
cpc.column_name p_column,
co.r_constraint_name p_pk,
cc.position,
co.status,
co.validated
from
dba_constraints co,
dba_cons_columns cc,
DBA_CONS_COLUMNS cpc
where
co.owner = cc.owner
and co.table_name = cc.table_name
and co.constraint_name = cc.constraint_name
and co.constraint_type=‘R’
and co.r_constraint_name=cpc.constraint_name
and co.r_owner=cpc.owner
and co.TABLE_NAME = ‘T4001’;

列出某个用户下所有缺少索引的子表

SELECT *
FROM (SELECT c.table_name, cc.column_name, cc.position column_position
FROM dba_constraints c, dba_cons_columns cc
WHERE c.constraint_name = cc.constraint_name
and c.owner = cc.owner
AND c.constraint_type = ‘R’
AND c.owner = upper(‘ZC’)
and cc.owner = upper(‘ZC’)
MINUS
SELECT i.table_name, ic.column_name, ic.column_position
FROM dba_indexes i, dba_ind_columns ic
WHERE i.index_name = ic.index_name
AND I.owner = upper(‘ZC’)
and IC.table_owner = upper(‘ZC’))
ORDER BY table_name, column_position;

列出所有的缺失外键索引的表

select
acc.OWNER “Owner”,
acc.CONSTRAINT_NAME “Constraint”,
acc.table_name “Table”,
acc.COLUMN_NAME “Column”,
acc.POSITION “Position”
from
dba_cons_columns acc, dba_constraints ac
where
ac.CONSTRAINT_NAME = acc.CONSTRAINT_NAME and ac.CONSTRAINT_TYPE = ‘R’
and acc.OWNER not in ( ‘ANONYMOUS’, ‘AURORA$’, ‘AURORA’, ‘CTXSYS’, ‘DBSNMP’, ‘DIP’, ‘DMSYS’, ‘DVF’, ‘DVSYS’, ‘EXFSYS’, ‘HR’, ‘LBACSYS’, ‘MDDATA’, ‘MDSYS’, ‘MGMT_VIEW’, ‘ODM’, ‘ODM_MTR’, ‘OE’, ‘OLAPSYS’, ‘ORACLE_OCM’, ‘ORAWSM’, ‘ORDPLUGINS’, ‘ORDSYS’, ‘OSE’, ‘OUTLN’, ‘PERFSTAT’, ‘PM’, ‘QS’, ‘QS_ADM’, ‘QS_CB’, ‘QS_CBADM’, ‘QS_CS’, ‘QS_ES’, ‘QS_OS’, ‘QS_WS’, ‘REPADMIN’, ‘SCOTT’, ‘SH’, ‘SI_INFORMTN_SCHEMA’, ‘SYS’, ‘SYSMAN’, ‘SYSTEM’, ‘TRACESVR’, ‘TSMSYS’, ‘WKPROXY’, ‘WKSYS’, ‘WK_TEST’, ‘WKUSER’, ‘WMSYS’, ‘XDB’,‘APEX_030200’,‘GSMADMIN_INTERNAL’,‘ORDDATA’ )
and acc.OWNER = ac.OWNER
and not exists ( select ‘TRUE’ from dba_ind_columns b
where b.TABLE_OWNER = acc.OWNER
and b.TABLE_NAME = acc.TABLE_NAME
and b.COLUMN_NAME = acc.COLUMN_NAME
and b.COLUMN_POSITION = acc.POSITION)
union all
select ’ ',null,null,null,null from dual
order by 1 ;

第二种情况:
建议避免并行的insert 类型的dml

第三种情况:
建议关闭DRM或者分析私网是否存在问题

第四种情况:
避免相关操作

参考:
WAITEVENT: “enq: TM - contention” Reference Note (Doc ID 1980175.1)
Resolving Issues Where ‘enq: TM - contention’ Waits are Occurring (Doc ID 1905174.1)
enq: TX - contention when _lm_drm_disable is set to 2(Doc ID 2818792.1)
Script to Check for Foreign Key Locking Issues for a Specific User (Doc ID 1019527.6)
Script to check for Missing Indexes for Foreign Keys (Doc ID 16428.1)

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论