Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】enq: TX — allocate ITL entry

2025年09月24日に「Dev.to」が公開したITニュース「enq: TX — allocate ITL entry」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Oracle DBのITLは、データブロック内でトランザクション情報を管理し、データ一貫性確保の仕組みだ。トランザクションが行を更新する際、ITLスロットを利用する。スロット不足は「enq: TX – allocate ITL entry」待機やデッドロックを招く。PCTFREEやINITRANSで対策できる。

出典: enq: TX — allocate ITL entry | Dev.to公開日:

ITニュース解説

データベースシステムでは、複数のユーザーが同時にデータを操作しても、それぞれが矛盾のない一貫したデータを見ることができ、かつ互いの処理が滞りなく進むようにするための複雑な仕組みが内部で動作している。Oracleデータベースにおいて、この「データの一貫性」と「並行性」を実現する上で非常に重要な役割を果たすのが「ITL(Interested Transaction List)」という概念である。

ITLは、データが実際に格納されている「データブロック」という記憶領域の先頭部分、いわゆるヘッダー領域に設けられた特別なリストである。このITLには、現在そのデータブロック内のデータを変更しようとしている、または変更しているトランザクションに関する情報が記録される。ITLは複数の「スロット」から構成され、各スロットが1つのトランザクション情報を保持する。

データを更新したり削除したりするトランザクションは、まず対象のデータが含まれるデータブロックのITL内で1つの空きスロットを確保しなければならない。スロットが確保されると、データベースはその中にいくつかの重要な情報を記録する。具体的には、そのトランザクションを一意に識別するための「トランザクションID(XID)」、データの変更を元に戻すための情報(UNDO情報)がどこに格納されているかを示す「Undo Block Address(UBA)」、そしてそのトランザクションがこのブロック内で何行のデータをロック(占有)しているかを示す「Lck」などの情報だ。各スロットは一度に1つのトランザクションにのみ属し、1つのトランザクションが同じデータブロック内で複数のスロットを占有することはない。トランザクションが正常に完了(コミット)するか、あるいは中断(ロールバック)されると、そのトランザクションが使用していたITLスロットは解放され、他の新しいトランザクションが再利用できる状態となる。ITLスロット一つあたりのサイズは約24バイト程度と小さい。

データブロックに最初から予約しておくITLスロットの数は、「INITRANS」という属性で設定できる。テーブルの場合、このINITRANSのデフォルト値は1だが、Oracle 9i以降では実際には最低2つのスロットが予約されるようになっている。インデックスのデフォルトは2だ。もし、初期に設定された数以上のITLスロットが必要になった場合、Oracleは自動的に追加のスロットをデータブロック内に確保しようとする。ただし、これには上限があり、「MAXTRANS」という属性で設定された最大値に達するか、あるいはデータブロック内に新たなスロットを格納するための空き容量が全くない場合には、それ以上スロットを増やすことはできない。MAXTRANSのデフォルト値は255で、Oracle 10gR1以降ではユーザーがこの値を変更することはできず、事実上255が使われる。理論上は1つのデータブロックで最大255個のトランザクションが同時に活動できることになるが、実際には1ブロックあたりの行数が少ないため、この上限に達することは稀だ。

実際の動きを観察してみよう。例えば、INITRANSを2に設定したテーブルを作成し、データブロックの内容をSQLトレースで確認すると、初期状態では2つのITLスロットが空き状態として表示される。次に、このテーブルに1行のデータを挿入するトランザクションを実行すると、トレース結果では最初のITLスロット(0x01)がこのトランザクションによって占有されたことが確認できる。スロットにはトランザクションIDが記録され、Lck(ロックした行数)の値が0から1に変化する。このトランザクションをコミットせずに、さらに同じテーブルにデータを挿入していくと、同じITLスロットが引き続き使用され、Lckの値は挿入した行数に応じて増加していく。そして、トランザクションをコミットすると、ITLスロットの「Flag」という状態表示が「-U-」に変わり、そのスロットがコミット済みのトランザクションによって使用され、再利用可能になったことを示す。

複数のトランザクションが同時に活動する場合の例も見てみよう。もし、いくつかのセッションから同時に、同じデータブロック内の異なる行に対して削除操作(DELETE)を実行すると、それぞれの削除トランザクションがITLの空きスロットを順次占有していく。例えば、3つのセッションがそれぞれ1行ずつ削除すると、データブロック内には3つの異なるITLスロットが確保され、それぞれに異なるトランザクションIDと、1行をロックしていることを示すLck値が記録されることがトレースから確認できる。さらに削除操作が続けば、ITLスロットは順次増えていく。

このようにITLスロットは通常自動的に割り当てられるが、まれに問題が発生することがある。トランザクションがITLスロットを確保できない場合、そのトランザクションを実行しているセッションは「enq: TX — allocate ITL entry」という名の待機イベントでブロックされてしまう。この待機イベントが発生する原因は主に二つある。一つは、前述のMAXTRANSによって設定されたITLスロットの最大数に既に達している場合。もう一つは、データブロック内に新しいITLスロットを格納するための十分な空き容量が残っていない場合だ。

この待機イベントが発生する状況を再現するシナリオがある。例えば、PCTFREEを0(データブロック内の更新用空き領域を予約しない設定)にし、INITRANSを1に設定したテーブルを作成する。そして、非常に大きなサイズの行データを何行か挿入して、データブロックをほぼ満杯にする。この状態で、ブロック内の既存の行に対して複数のセッションから同時に削除操作を実行しようとすると、最初の二つの削除トランザクションはスロットを確保できるが、三つ目の削除トランザクションは新しいITLスロットを確保できず、上記の待機イベントでブロックされてしまう。これは、データブロックが満杯であるために、INITRANSで設定された初期スロット数(この場合は1だが実質2)を超えて、新しいITLスロットを自動拡張する余地がないためだ。先行するトランザクションのいずれかがコミットまたはロールバックされ、ITLスロットが解放されると、待機していたセッションの処理が再開される。

このようなITL関連の待機イベントの発生を軽減するための対策は二つ考えられる。一つは、テーブルやインデックスの「PCTFREE」属性の値を増やすことだ。これにより、データブロック内に将来の更新やITLスロットの追加に備えてより多くの空き領域が確保される。もう一つは、「INITRANS」属性の値を増やすことである。これにより、データブロックが作成される際に、最初からより多くのITLスロットが予約される。ただし、これらの属性を変更するには、対象のテーブルやインデックスを再構築する必要がある点に注意が必要だ。

さらに複雑な問題として、ITLスロットの不足が原因で「デッドロック」が発生することもある。デッドロックとは、複数のトランザクションが互いに相手が保持しているリソースの解放を待ち続け、永遠に処理が進まなくなる状態を指す。例えば、セッション1がテーブルAのブロックの最後のITLスロットを占有し、セッション2がテーブルBのブロックの最後のITLスロットを占有しているとする。この状態で、セッション1がテーブルBのブロックでITLスロットを要求し、同時にセッション2がテーブルAのブロックでITLスロットを要求した場合、互いに相手のリソース解放を待つ状態(循環待機)が発生し、Oracleはこれをデッドロックとして検出し、いずれかのトランザクションを強制的に終了させる。デッドロック発生時には、Oracleのアラートログに詳細なトレースファイルの情報が出力され、どのセッションがどのリソースを保持し、何を待っていたかといった状況を確認できる。

最後に、ITL待機イベントはUPDATEやDELETE操作で発生する可能性があるが、INSERT操作では発生しないという点に留意する必要がある。なぜなら、INSERTの場合、もし現在のデータブロックにITLスロットの空きがないか、あるいはデータブロック自体が満杯で新しい行を追加できない場合でも、Oracleは現在のブロックで待機することなく、別の空いているデータブロックを探して新しい行を挿入するからだ。

関連コンテンツ

関連IT用語