Enq: TM – contention

基本情報

 

DMLが実行される間、DMLに関連するオブジェクトへの変更を防止するためにDMLを実行するプロセスは、必ず、そのテーブルに対してTMロックを獲得しなければなりません。TMロックを獲得する過程で競合が発生した場合enq:TM – contentionイベントを待機することになります。

オラクルのConceptマニュアルとReferenceマニュアルでは、ロックの分類を次のように説明している。

・DMLロック:Data lock。 DMLの実行時にデータを保護するためのロックです。ローロック(TX)は、特定のローを保護し、
      テーブルロック(TM)は、テーブル全体を保護します。 dba_dml_locksビューを介して観察可能です。
・DDLロック:Data dictionary lock。 User / Table / View / Procedureなどの定義を保護します。
      dba_ddl_locksビューを介して観察可能です。
・Internal locks and latches

上記の説明は、DMLロックとDDLロックという別個のロックが存在するかのように混乱を与える恐れがあります。実際にDMLロックとDDLロックはロックを適切に分類するために付けた名前のことですので、この点に留意してください。

DMLロックは、実際にはTMロックと一致します。 DMLロックはDBA_DML_LOCKSビューで観察可能ですが、このビューは、V$LOCKビューでロックタイプがTMであることだけを抽出して示す役割をします。データベースで許容TMロックの数はDML_LOCKSパラメータで指定することができます。もしDML_LOCKSパラメータの値を0した場合テーブルに対してTMロック獲得されません。この場合、Oracleは、テーブル定義が保護されることを保証するために、すでに存在しているテーブルのDDLを基本的に許可していません。したがってTMロックを獲得していなくても、何の心配もなく、テーブルの特定の行を変更することが可能になります。OPSのような環境でTMロックをグローバルに獲得するために発生するオーバーヘッドを減らすためにDML_LOCKSの値を0に変更したりします。

DDLロックは、実際にはlibrary cache lockと一致します。DDLロックはDBA_DDL_LOCKSビューで観察可能ですが、このビューは、実際には、X$KGLLKビューを適切に処理して表示役割をします。 DDLロックはDBA_DDL_LOCKSビューのほか、X$KGLLK、DBA_KGLLOCKのようなビューを介して観察可能です。

一般的なDMLステートメントは、テーブルに対してTMロックをSub-Exclusive(SX)モードで獲得します。Sub-Exclusiveモード間の相互互換性があるため、複数のセッションが同一のテーブルに対して同時にDMLを実行することが可能です。DMLを実行したセッションは、テーブルの上には、TMロックをSub-Exclusiveモードで獲得し、変更されたデータには、TXロックをExclusiveモードで獲得します。

以下のようにV$LOCKビューでDMLを実行したセッションがどのようなロックをどのモードで獲得しているかを観察することができます。

SQL> update test set id = 1 where rownum = 1;
...
SQL> exec print_table('select * from v$lock where sid = 148');

ADDR                           : C0000000ED2663D0
KADDR                         : C0000000ED2663F8
SID                              : 148
TYPE                            : TM
ID1                              : 54679
ID2                              : 0
LMODE                         : 3
REQUEST                      : 0
CTIME                          : 67
BLOCK                         : 0
-----------------
ADDR                           : C0000000ED2DB7F8
KADDR                         : C0000000ED2DB980
SID                              : 148
TYPE                            : TX
ID1                              : 4718594
ID2                              : 439
LMODE                         : 6
REQUEST                      : 0
CTIME                          : 67
BLOCK                         : 0

オブジェクト名54679(ID1)に対応するオブジェクトに対してTMロックをSub-Exclusive(3)モードで獲得していることを確認することができます。TMロックのID1は、テーブルのオブジェクトID(Object ID)に該当します。したがってDBA_OBJECTSビューと結合する任意のテーブルのTMロックかどうかを判断することができます。Sub-Exclusiveモード間での互換性があるため、DMLの間TMロックをめぐる競合が発生しません。したがってenq:TM – contention待機も発生しないのです。TM Lock競合が発生する場合は、通常、次のとおりです。

・インデックスがないForeign keyの親キーを変更する場合
・DMLとDDLの間TMロック競合
・Lock table..によるTMロック競合
・Direct load作業によるTMロック競合

 

パラメータと待機時間

 

待機パラメータ

 

1.P1:Enqueue情報
2.P2:Object#
3.P3:Table/ Partition情報

 

待機時間

 

enqueue待機イベントと同一です。最大3秒まで待ちます。もしTMロックを獲得する場合は獲得するまで待機します。

 

チェックポイントと解決策

 

インデックスがない外部キー

Oracle 9iの前には、インデックスがない外部キーカラムがTMロック競合の主な原因でした。Oracle 9iの以前のバージョンには、子テーブルの外部キーカラムにインデックスがない状態で、親テーブルのKeyが変更されると、子テーブルに対してSharedモードやShared-Sub-ExclusiveモードでTMロックを獲得しなければなりませんでした。獲得されたTMロックは、親Keyを変更したトランザクションが終了(commitまたはrollback)されるまで維持されるため、これによる競合が深刻なパフォーマンスの低下現象を起こしたりしていました。しかし、幸いなことに、Oracle9iからのアルゴリズムが改善され、このような競合は発生しません。Oracle 9iの以前のバージョンを使用している場合は、foreign keyカラムに必ずインデックスを作成する習慣を持つ必要があります。

不適切なDDLによるTMロック競合

トランザクションが進行中のテーブルには、基本的にDDLが不可能です。したがって、この場合には、競合によるパフォーマンスの問題は発生しません。次の例を見てみて見ましょう。

セッションA:
SQL> update test set id = 1 where rownum = 1;
セッションB:
SQL> alter table test add ID2 number;
alter table test add ID2 number
*
ERROR at line 1:
ORA-00054: resource busy and acquire with NOWAIT specified

 

Updateが行われて、まだコミットされていないテーブルには、DDLを実行することは不可能です。一方、DDLが実行されているテーブルに対してDMLを実行する場合には、TMロック競合が発生することが可能となります。特定のプロセスがテーブルに対してインデックスを作成する場合は、インデックスが作成されている間、テーブルに対してTMロックをSharedモードで獲得することになります。

SQL> create index big_objects_idx_test 
on big_objects(owner,object_name,object_id);
SQL> exec print_table('select * from v$lock where sid = 148');
...
ADDR                          : C0000000ED2663D0
KADDR                        : C0000000ED2663F8
SID                             : 148
TYPE                           : TM
ID1                             : 52318
ID2                             : 0
LMODE                        : 4

 

TMロックのID1を利用すれば、BIG_OBJECTSというテーブル名を得ることができます。LMODEの値は4であり、Sharedモードを意味します。つまり、create index文はbig_objectsテーブルに対してSharedモードでTMロックを獲得することになります。問題は、このテーブルに対してDMLを実行するセッションはSub-ExclusiveモードでTMロックを獲得しなければならないというものです。SharedモードとSub-Exclusiveモードでは、互換性がないため、DMLを実行するセッションはDDL実行が完了するまで待機することになり、この時enq:TM – contention待機イベントが発生するようになります。DDLによるTMロック競合を減らすことができる方法としては、次のようなものがあります。

・データ量が多く、テーブルの不適切なDDLを実行すると、そのテーブルをアクセスするすべてのDMLセッションが待機状態になり、
 障害の状況にまで発展することがあります。適切な管理を通じて根本的に防止することが良い結果を生みます。
・DDLの実行時に可能な限り、オンライン(Online)]オプションを使用します。Oracleのバージョンアップされるほど、オンラ
 インで実行可能なDDLはますます増加する傾向にあります。ほとんどの一般的なDDLでオンラインオプションを使用することが可能
 です。例えば、create indexコマンドをオンラインオプションで実行する場合は、テーブルに対してSharedモードではなく、
 Sub-Shared(SS)モードでTMロックを獲得します。Sub-Sharedモードでは、Sub-Exclusiveモードと互換性があるため、
 インデックスを作成する途中でDMLを実行することが可能です。つまりenq:TM - contention待機が発生しません。 

次の例を見てみましょう。

セッションA:(SID=148)
SQL> create index big_objects_idx_test111 on 
big_objects(secondary,generated,temporary,status,timestamp,last_ddl_time) 
online;
セッションB:
SQL> exec print_table('select * from v$lock where sid = 148');
...
ADDR                          : C0000000ED2663D0
KADDR                        : C0000000ED2663F8
SID                             : 148
TYPE                           : TM
ID1                             : 52318
ID2                             : 0
LMODE                        : 2
・Parallel DDLを使用して、DDLの実行速度を最大化する。大容量データのDDLの実行時にParallelオプションを
 適切に使用すると、DDL自体の性能を最大限に引き出すことができます。
 加えてNologgingオプションを一緒に使用してのよいのです。 DDLの実行速度が向上しされると、
 その比例してTMロックによる待機時間も減ることになります。

 

Lock table..を利用して意図的にTMロックを獲得した場合

 

Lock table …を利用して意図的にTMロックを獲得する場合にはTMロック競合が発生することがあります。

セッションA:(SID=148)
SQL> update test set id = 1 where rownum = 1;
セッションB:(SID=150)
.. Wait ...

上記のように、セッションAからupdateによってTMロックがSub-Exclusiveモードで獲得された状態では、セッションBがlock tableコマンドを利用してTMロックをExclusiveに獲得しようとした場合、競合が発生することになりenq:TM – contentionイベントを待機することになります。

TMロックによる競合が発生する場合には、ロックホルダー(Lock Holder)セッションで実行されたSQL文を収集することが非常に重要です。TMロックの場合に発生することがある場合が非常に多様であるため、SQLステートメントがなければ、正確な判断が難しい場合が多く存在します。問題が誘発されたSQL文がLock table…構文であり、これにより、TMロック競合が広く行われる場合、アプリケーションの適切な修正で解決することができます。同期のためにテーブル全体にロックをかけることなく、DMBS_LOCKパッケージを使用するか、select…for updateなどを使用してロックの範囲を減らす方法を検討します。

 

Direct/Parallel Load処理を実行した場合

 

INSERT / * + APPEND * / INTO …またはSQL * Loaderのdirect path loadなどの一部の機能は、そのテーブルに対してTMロックをExclusiveに獲得します。Direct loadタスクは、SGAを経由せずにデータファイルに直接書き込みを行うので、処理が実行されている間、テーブルにどのような変化も許容してはいけません。したがってTMロックをExclusiveに獲得することにより、テーブルのどのような変更も許可されないことを保証受けなければ作業が可能です。 Direct load処理が実行されている間は、テーブルの任意のDDLやDMLも許されません。したがって、トランザクションが多い時点でDirect loadタスクを実行するときは、TMロック競合による問題が発生する恐れがあることを確認しなければなりません。 SQL*Loaderをparallelモードで実行する場合には、テーブルのTMロックをSharedモードで獲得します。したがって、この場合にも、他のセッションでのDDLやDMLは許されません。