FK 外键上需要创建index 避免 主表update时 的lock
High 'enq: TM - contention' Waiting On Child Tables When Executing Update Against Primary Key Or Delete On Parent Table. (Doc ID 2924365.1)
如果外键有index, oracle会立即检查子表有没有符合要求的数据,如果没有index,会直接锁表
子表更新锁主表。
This is due to lack of index on foreign key column of child table.
In the case of child table with an unindexed foreign key, the update against primary key or delete on the parent table needs to check for child rows, then the DML needs to request an S DML lock on the child table.
If the transaction has already held an SX DML lock on the child table before, then this S request will result in an SSX DML lock request on child table. Both S and SSX DML lock request from parent table transactions will be blocked by the concurrent child table transaction holding SX DML lock and have to wait for 'enq: TM - contention' until the child table transaction commit or rollback.
Applies to:
Oracle Database Cloud Exadata Service - Version N/A and later
Oracle Cloud Infrastructure - Exadata Cloud Service - Version N/A and later
Generation 1 - Exadata Cloud at Customer (First Generation Cloud Machine) - Version N/A and later
Gen 2 Exadata Cloud at Customer - Version N/A and later
Oracle Database - Enterprise Edition - Version 11.2.0.4 to 20.0.0.0.0 Beta [Release 11.2 to 20.0]
Information in this document applies to any platform.
Symptoms
- High 'enq: TM - contention' waiting on child tables when executing DML on parent table:
*** 2023-01-16T17:42:47.686192+08:00
HM: Early Warning - Session ID 19734 serial# 52253 OS PID 286222 (FG)
is waiting on 'enq: TM - contention' for 32 seconds, wait id 597
p1: 'name|mode'=0x544d0005, p2: 'object #'=0x60fda3, p3: 'table/partition'=0x0 <<<<<<<<<<<< Request mode 5
Blocked by Session ID 10297 serial# 23236 on instance 4
which is waiting on 'enq: TM - contention' for 1314 seconds
p1: 'name|mode'=0x544d0004, p2: 'object #'=0x60fdd4, p3: 'table/partition'=0x0 <<<<<<<<<<<< Request mode 4
Final Blocker is Session ID 15737 serial# 40636 on instance 4
which is 'not in a wait' for 110 seconds - This problem can be simulated as the following simplified test case:
SQL> create table t1(t1_id int primary key);
Table created.
SQL> create table t2(t2_id int primary key, fk_t1_id int references t1(t1_id));
Table created.
SQL> create table t3(t3_id int primary key, fk_t2_id int references t2(t2_id));
Table created.
SQL> select object_name,object_id,UPPER(to_char(object_id,'xxxxxxxxx')) ID1 from user_objects;
OBJECT_NAME OBJECT_ID ID1
------------ ---------- ----------
T1 122854 1DFE6
SYS_C0010708 122855 1DFE7
T2 122856 1DFE8
SYS_C0010709 122857 1DFE9
T3 122858 1DFEA
SYS_C0010711 122859 1DFEB6 rows selected.
session 1:
SQL> select sid, serial# from v$session where sid=(select userenv('sid') from dual);
SID SERIAL#
---------- ----------
1239 47410SQL>
SQL> update t3 set fk_t2_id=1;0 rows updated.
session 2:
SQL> select sid, serial# from v$session where sid=(select userenv('sid') from dual);
SID SERIAL#
---------- ----------
1281 24479SQL> update t3 set fk_t2_id=1;
0 rows updated.
SQL> update t2 set t2_id=2; <<<<<<<<<<<< Hang
session 3:
SQL> select sid, serial# from v$session where sid=(select userenv('sid') from dual);
SID SERIAL#
---------- ----------
1276 33424SQL> update t1 set t1_id=2; <<<<<<<<<<<< Hang
session 4:
SQL> select sid, serial#,event,BLOCKING_SESSION,FINAL_BLOCKING_SESSION,P1TEXT,p1,P2TEXT,p2 from v$session where sid in ( 1239,1281,1276);
SID SERIAL# EVENT BLOCKING_SESSION FINAL_BLOCKING_SESSION P1TEXT P1 P2TEXT P2
---------- ---------- ------------------------------ ---------------- ---------------------- ------------------ ---------- ---------- ----------
1239 47410 SQL*Net message to client driver id 1650815232 #bytes 1
1276 33424 enq: TM - contention 1239 1239 name|mode 1414332420 object # 122856 <<<<<<<<< Request mode 4
1281 24479 enq: TM - contention 1239 1239 name|mode 1414332421 object # 122858 <<<<<<<<< Request mode 5SQL>
Changes
Cause
This is due to lack of index on foreign key column of child table.
In the case of child table with an unindexed foreign key, the update against primary key or delete on the parent table needs to check for child rows, then the DML needs to request an S DML lock on the child table.
If the transaction has already held an SX DML lock on the child table before, then this S request will result in an SSX DML lock request on child table. Both S and SSX DML lock request from parent table transactions will be blocked by the concurrent child table transaction holding SX DML lock and have to wait for 'enq: TM - contention' until the child table transaction commit or rollback.
Solution
Add index on the foreign key columns of child table:
session 1:
SQL> create index fk_t1_id on t2(fk_t1_id);
Index created.
SQL> create index fk_t2_id on t3(fk_t2_id);
Index created.
SQL> update t3 set fk_t2_id=1;
0 rows updated.
SQL>
session 2:
SQL> update t3 set fk_t2_id=1;
0 rows updated.
SQL> update t2 set t2_id=2;
0 rows updated.
SQL>
session 3:
SQL> update t1 set t1_id=2;
0 rows updated.
SQL>