Oracle テーブルロックの確認方法と基本を紹介
はじめに
データベースにおける「ロック」は、複数のユーザーやアプリケーションが同時にデータにアクセスする際の整合性や一貫性を保つために不可欠な仕組みです。特にOracleデータベースでは、非常にきめ細やかなロック制御が行われており、その中でも「テーブルロック」はDML(データ操作言語)やDDL(データ定義言語)の実行時に取得される重要なロックタイプの一つです。
しかし、ロックが適切に管理されない場合、パフォーマンスの低下、セッションのハング、さらにはデッドロックといった深刻な問題を引き起こす可能性があります。特に、予期しないテーブルロックは、アプリケーションの応答遅延やバッチ処理の長時間化の原因となることが多く、DBA(データベース管理者)や開発者にとってその発生状況を確認し、適切に対処するスキルは必須となります。
この記事では、Oracleデータベースにおけるテーブルロックの基本概念から始め、DMLロックとDDLロックといった主要なテーブルロックの種類について詳しく解説します。さらに、発生しているテーブルロックを特定するための具体的な確認方法、特に動的パフォーマンスビュー(V$ビュー)を活用したSQLクエリに焦点を当てて説明します。最後に、ロック問題が発生した場合の原因究明と解消方法、そして運用上の注意点についても触れます。
この記事を読むことで、Oracleデータベースでテーブルロックが発生した際に、その状況を正確に把握し、適切な対応を取るための知識を習得できるでしょう。
1. Oracleにおけるロックの基本
Oracleデータベースは、多人数が同時に利用する環境において、データの整合性と並行性を維持するために様々なレベルのロック機構を提供しています。
1.1. トランザクションとロックの関係
Oracleにおけるロックは、主に「トランザクション」に関連して機能します。トランザクションとは、一連のSQL文(SELECT
, INSERT
, UPDATE
, DELETE
, MERGE
など)を論理的な一つの単位として扱う仕組みです。トランザクション内のすべての操作が成功するか、あるいは失敗するかのいずれかになり、中途半端な状態は発生しません(ACID特性)。
トランザクションが開始されると(例えば、最初のDML文が実行されたとき)、そのトランザクションは、操作対象のデータに対して適切なロックを取得します。これらのロックは、他のトランザクションが同じデータに対して矛盾する操作を行うことを防ぎます。トランザクションがCOMMIT
またはROLLBACK
されるまで、取得したロックの大部分は保持されます。
1.2. ロックの目的
ロックの主な目的は以下の2点です。
- 一貫性の維持 (Consistency): あるトランザクションがデータを更新している最中に、他のトランザクションがその中途半端なデータを読み取ったり、さらに別の更新を行ったりするのを防ぎます。これにより、データベース全体のデータの論理的な一貫性が保たれます。
- 並行性の制御 (Concurrency): 複数のトランザクションが同時に実行される環境で、互いに干渉することなく、効率的に処理を進められるように調整します。ロックによって、必要な排他性を確保しつつ、可能な範囲で並列処理を許可します。
1.3. ロックの種類と粒度
Oracleには様々なロックタイプがありますが、大別すると以下のようになります。
- ラッチ (Latches): SGA(System Global Area)内の共有メモリ構造を保護するための軽量なシリアライゼーション機構。非常に短時間で保持・解放される。
- ミュート (Mutexes): ラッチよりもさらに軽量なシリアライゼーション機構。ラッチやミュートの競合はパフォーマンス問題の原因となるが、この記事の主題であるテーブルロック(エンキューロック)とは異なる。
- エンキューロック (Enqueue Locks): データベースオブジェクト(テーブル、行、パーティションなど)やリソース(システム、トランザクションなど)を保護するためのロック。このロックは、保持セッションが解放するまで待ち状態が発生しうる。テーブルロックはエンキューロックの一種です。
エンキューロックは、保護する対象の大きさによって「粒度」があります。
- 行レベルロック (Row-Level Lock): テーブル内の特定の行に対するロック。OracleのDML操作(
UPDATE
,DELETE
,INSERT
による重複キー違反など)では、デフォルトで行レベルロックが取得されます。これは最も粒度が細かく、並行性が高いロックです。 - ブロックレベルロック (Block-Level Lock): データブロック全体に対するロック。Oracleでは明示的なブロックレベルロックの概念はユーザーからは見えませんが、内部的にはデータブロックを保護する機構が存在します。
- パーティションレベルロック (Partition-Level Lock): パーティション化されたテーブルの特定のパーティションに対するロック。パーティション単位でのDMLやDDLで取得されうる。
- テーブルレベルロック (Table-Level Lock): テーブル全体に対するロック。DML操作やDDL操作の際に取得されます。この記事の主な対象です。
- オブジェクトレベルロック (Object-Level Lock): テーブル以外のスキーマオブジェクト(インデックス、ビュー、パッケージなど)に対するロック。主にDDL操作で取得されます。
- システムレベルロック (System-Level Lock): データベース全体、インスタンス、またはデータディクショナリ構造に対するロック。
2. Oracleにおけるテーブルロックの種類 (TM ロック)
テーブルロックは、特定のテーブル全体に対して取得されるロックです。主にDML操作とDDL操作によって取得されるロックに分類されます。
動的パフォーマンスビューV$LOCK
では、テーブルロックはTYPE = 'TM'
として識別されます。ID1
カラムにはロック対象のオブジェクトIDが格納されます。
2.1. DMLロック (TMロック)
DMLロックは、INSERT
, UPDATE
, DELETE
, MERGE
, SELECT ... FOR UPDATE
といったDML文を実行する際に、対象のテーブルに対して取得されるロックです。これらのDMLロックは、テーブル全体を完全にロックするわけではなく、行レベルロックと組み合わせて使用されることで、可能な限り並行性を維持しつつデータの整合性を保ちます。
DMLロックには、その排他性に応じていくつかのモードがあります。これらのモードは、他のトランザクションが同じテーブルに対してどのようなロックを取得できるか(またはできないか)を決定します。ロックモードは数値で表現されますが(0: None, 1: Null, 2: Row-S, 3: Row-X, 4: Share, 5: Share Row-X, 6: Exclusive)、通常は以下の名称で呼ばれます。
-
Row Share (RS) / Share Update (SRU) – モード2
- 取得される操作:
SELECT ... FOR UPDATE
文を発行したセッションは、選択された行に対してTXロック(行ロック)を取得すると同時に、対象のテーブルに対してRSロックを取得します。 - 互換性: このロックは比較的並行性が高いモードです。他のセッションは、同じテーブルに対してRS, RX, S, SRX(SSX)ロックを取得できます。ただし、EXロックは取得できません。他のセッションによるDML操作(
INSERT
,UPDATE
,DELETE
)は許可されます(これらの操作はRXロックを取得しますが、RSロックとは互換性があります)。DDL操作は制限される場合があります。 - 目的: 行レベルロックを取得しつつ、テーブル全体に対する一部の排他性を確保し、後続の
UPDATE
/DELETE
を他のセッションに妨げられないようにするため。
- 取得される操作:
-
Row Exclusive (RX) – モード3
- 取得される操作:
INSERT
,UPDATE
,DELETE
,MERGE
文を発行したセッションは、変更される行に対してTXロック(行ロック)を取得すると同時に、対象のテーブルに対してRXロックを取得します。 - 互換性: RSロックよりも排他性が高いモードです。他のセッションは、同じテーブルに対してRS, RX, S, SRX(SSX)ロックを取得できます。ただし、SX, EXロックは取得できません。他のセッションによるDML操作は許可されます。
- 目的: 行レベルロックを取得しつつ、テーブル全体に対する一部の排他性を確保し、他のセッションが競合するDDL操作やShare/Exclusiveモードでのロックを取得するのを防ぐため。最も一般的なDMLロックモードです。
- 取得される操作:
-
Share (S) – モード4
- 取得される操作:
LOCK TABLE table_name IN SHARE MODE;
文を発行することで明示的に取得できます。 - 互換性: このロックは、他のセッションが同じテーブルに対してSロックまたはRSロックを取得することを許可します。しかし、RX, SX, SRX(SSX), EXといった他のDMLロックモードは取得できません。つまり、このロックが取得されているテーブルに対して、他のセッションはDML操作(
INSERT
,UPDATE
,DELETE
,MERGE
)を実行できなくなります。 - 目的: テーブル全体に対する参照の一貫性を保ちつつ、他のセッションによるDML操作を完全にブロックしたい場合に利用されます。例えば、複雑な集計処理を行う際に、処理中にデータが変更されないようにしたい場合など。
- 取得される操作:
-
Share Row Exclusive (SRX) / Share Update Exclusive (SSX) – モード5
- 取得される操作:
LOCK TABLE table_name IN SHARE ROW EXCLUSIVE MODE;
文を発行することで明示的に取得できます。Oracle 11g R2まではIN SHARE UPDATE MODE
もこのモードでした。 - 互換性: RSロック以外の全てのDMLロックモード(RX, S, SX, SRX, EX)と互換性がありません。つまり、このロックが取得されているテーブルに対して、他のセッションはRSロックのみ取得できますが、それ以外のDMLロック(
INSERT
,UPDATE
,DELETE
などの通常DMLやLOCK TABLE
のS, EXモード)は取得できません。 - 目的: テーブルに対する参照の一貫性を保ちつつ、自分以外の他のセッションによるDML操作を完全にブロックし、かつ他のセッションによるSモードでの明示的なロックもブロックしたい場合に利用されます。
- 取得される操作:
-
Exclusive (EX) – モード6
- 取得される操作:
LOCK TABLE table_name IN EXCLUSIVE MODE;
文を発行することで明示的に取得できます。DROP TABLE
のような一部のDDL操作でも内部的に取得されます。 - 互換性: 最も排他性の高いモードです。他のセッションは、同じテーブルに対してどのDMLロックモードも取得できません。完全にテーブル全体へのアクセスを排他的に制御します。他のセッションは、このテーブルに対してDML操作も
SELECT
以外のDDL操作も実行できなくなります。 - 目的: テーブルに対する完全に排他的なアクセスを必要とする場合に利用されます。例えば、メンテナンス作業やバッチ処理などで、他のセッションが一切アクセスしない状態を確保したい場合など。
- 取得される操作:
これらのDMLロックモードの互換性をまとめた表(互換性マトリクス)は、Oracleのマニュアルに詳しく掲載されています。重要なのは、あるセッションが特定のロックモード(保持ロック)を取得している場合、他のセッションが要求しているロックモード(要求ロック)が保持ロックと互換性がない場合に待ち(待機)が発生するという点です。
2.2. DDLロック
DDL(データ定義言語)文(CREATE
, ALTER
, DROP
, TRUNCATE
など)を実行する際にも、対象のオブジェクトに対してロックが取得されます。DDLロックは、DMLロックとは異なる目的と挙動を持ちます。DDLロックは、DDL操作中のオブジェクト定義の一貫性を保つために使用され、通常はDDL操作が完了するまで保持されます。
DDLロックには主に以下の種類があります。
- 排他DDLロック (Exclusive DDL Lock):
DROP TABLE
,ALTER TABLE ... DROP COLUMN
などの操作で取得されます。このロックは、操作対象のオブジェクトに対する他のDDLやDML操作を完全にブロックします。オブジェクトの構造自体が大きく変更されるような、危険度の高い操作で使用されます。 - 共有DDLロック (Share DDL Lock):
ALTER TABLE ... ADD COLUMN
,CREATE INDEX
などの操作で取得されます。このロックは、他のDDL操作はブロックする場合がありますが、DML操作は通常許可します。ただし、DDL操作の種類によっては、マークDMLロックなどと組み合わせてDML操作にも影響を与える場合があります。 - 排他利用ロック (Exclusive Application Lock): 特定のDDL操作(例: インデックス作成)の準備段階などで内部的に取得されるロック。
- マークDMLロック (Mark DML Lock): DDL操作(例:
ALTER TABLE ... ADD COLUMN DEFAULT ... NOT NULL
)の実行中に、そのDDLが完了する前にDML操作が開始された場合に取得される内部的なロック。DDLは、マークDMLロックを取得したトランザクションが完了するまで待機します。
TRUNCATE TABLE
は特殊で、テーブルに対する排他DDLロックを取得しますが、内部的にはCOMMITに近い動作をし、既存のDMLロック(TMロック)や行ロック(TXロック)を解放します。
DDLロックはV$LOCK
ビューでは通常TYPE = 'DX'
(Exclusive DDL), TYPE = 'SX'
(Share DDL), TYPE = 'UL'
(User/Application Lock) などとして表示されることがありますが、DDLの種類や内部的な処理によって取得されるロックタイプは異なります。
3. テーブルロックの確認方法
Oracleデータベースで発生しているテーブルロックを確認する主な方法は、動的パフォーマンスビュー(V$ビュー)を問い合わせることです。これらのビューには、現在データベースで発生している様々なイベント、セッションの状態、ロック情報などがリアルタイムで格納されています。
テーブルロックの確認では、主に以下のV$ビューを使用します。
V$LOCK
: 現在データベース全体で保持または待機されているロックに関する詳細情報。テーブルロック(TMロック)の特定に最も基本となるビューです。TYPE
: ロックタイプ(例:TM
=テーブルロック,TX
=トランザクションロック/行ロック,CI
=カーソル無効化など)LMODE
: ロック保持モード(0: None, 1: Null, 2: Row-S, 3: Row-X, 4: Share, 5: Share Row-X, 6: Exclusive)REQUEST
: ロック要求モード(0: ロック待ちなし, >0: 要求モード、待ち状態)SID
: ロックを保持または要求しているセッションのSIDID1
: ロック対象のオブジェクトID(テーブルロックの場合)ID2
: ロックタイプに依存する追加情報(通常、テーブルロックの場合は0)
V$LOCKED_OBJECT
: 現在ロックされているオブジェクト(テーブルなど)とそのロック情報に関するサマリービュー。V$LOCK
のTM
ロック情報の一部を、オブジェクトIDとセッションIDで関連付けた形で見やすいように提供しています。SESSION_ID
: ロックを保持しているセッションのSIDOBJECT_ID
: ロックされているオブジェクトのIDORACLE_USERNAME
: ロックを保持しているセッションのデータベースユーザー名OS_USER_NAME
: ロックを保持しているセッションのOSユーザー名LOCKED_MODE
: ロックモード(数値、V$LOCK.LMODE
と同じ)
V$SESSION
: 現在アクティブなセッションに関する詳細情報。ロックを保持または待機しているセッションの特定、そのセッションが実行している処理、待機イベントなどを確認するために使用します。SID
: セッションIDSERIAL#
: セッションのシリアル番号(SIDとの組み合わせでセッションを一意に識別)USERNAME
: データベースユーザー名STATUS
: セッションの状態(例:ACTIVE
,INACTIVE
,KILLED
)PROGRAM
: セッションを実行しているプログラム名EVENT
: 現在待機しているイベント(ロック待ちの場合はenq: TM - contention
,enq: TX - row lock contention
など)WAIT_CLASS
: 待機イベントの分類(例:Application
,Concurrency
,User I/O
など)P1TEXT
,P1
,P2TEXT
,P2
,P3TEXT
,P3
: 待機イベントに関する詳細パラメータSQL_ID
: 現在実行中または最後に実行されたSQLのIDPREV_SQL_ID
: 直前に実行されたSQLのID
DBA_OBJECTS
/ALL_OBJECTS
/USER_OBJECTS
: オブジェクトIDからオブジェクト名(テーブル名、インデックス名など)を取得するために使用します。通常、V$LOCKED_OBJECT
やV$LOCK
のID1
カラムと結合して使用します。OBJECT_ID
: オブジェクトのIDOWNER
: オブジェクトの所有者スキーマOBJECT_NAME
: オブジェクトの名前OBJECT_TYPE
: オブジェクトのタイプ(例:TABLE
,INDEX
,VIEW
など)
V$SQLAREA
/V$SQL
: 実行されたSQL文のテキストや実行統計情報。ロックを引き起こしている可能性のあるSQL文を特定するために、V$SESSION
のSQL_ID
またはPREV_SQL_ID
と結合して使用します。
これらのビューを組み合わせて、発生しているテーブルロック、それを保持しているセッション、そのセッションが実行している処理、そしてそのロックを待っている他のセッションを特定します。
3.1. 代表的なSQLクエリ例
以下に、テーブルロックの確認に役立つ代表的なSQLクエリ例をいくつか紹介します。これらのクエリは、適切な権限(例: SELECT ANY DICTIONARY
または SELECT
on V$
ビューとDBA_OBJECTS
)を持つユーザー(通常はDBA)で実行する必要があります。
クエリ1:現在ロックされているオブジェクトとロックモードを確認する
このクエリは、V$LOCKED_OBJECT
とDBA_OBJECTS
を結合し、どのオブジェクトが、どのセッションによって、どのモードでロックされているかを表示します。
sql
SELECT
lo.SESSION_ID,
s.SERIAL#,
s.USERNAME,
s.OSUSER,
s.PROGRAM,
s.MACHINE,
s.STATUS,
s.EVENT,
s.WAIT_CLASS,
o.OWNER,
o.OBJECT_NAME,
o.OBJECT_TYPE,
lo.LOCKED_MODE,
CASE lo.LOCKED_MODE
WHEN 0 THEN 'None'
WHEN 1 THEN 'Null'
WHEN 2 THEN 'Row-S (SS)'
WHEN 3 THEN 'Row-X (SX)'
WHEN 4 THEN 'Share (S)'
WHEN 5 THEN 'Share Row-X (SSX)'
WHEN 6 THEN 'Exclusive (EX)'
ELSE TO_CHAR(lo.LOCKED_MODE)
END AS LOCKED_MODE_DESC,
s.SQL_ID,
s.PREV_SQL_ID
FROM
V$LOCKED_OBJECT lo
JOIN
DBA_OBJECTS o ON lo.OBJECT_ID = o.OBJECT_ID
JOIN
V$SESSION s ON lo.SESSION_ID = s.SID
WHERE
o.OBJECT_TYPE IN ('TABLE', 'VIEW', 'MATERIALIZED VIEW') -- テーブル、ビュー、マテビューに絞る場合
ORDER BY
lo.LOCKED_MODE DESC, o.OWNER, o.OBJECT_NAME;
lo.SESSION_ID
: ロックを保持しているセッションID。s.*
:V$SESSION
からセッションの詳細情報。o.OWNER
,o.OBJECT_NAME
,o.OBJECT_TYPE
: ロックされているオブジェクトの情報。lo.LOCKED_MODE
,LOCKED_MODE_DESC
: ロックモードの数値とその意味。s.SQL_ID
,s.PREV_SQL_ID
: ロックを取得した、またはロックを引き起こしている可能性のあるSQLのID。
このクエリを実行することで、どのテーブルに対してどのようなロックがかかっているか、そしてそれをどのセッションが保持しているかを一覧できます。LOCKED_MODE_DESC
を見て、Exclusive (EX)
やShare (S)
といった排他性の高いロックがかかっていないかを確認するのが第一歩です。
クエリ2:特定のテーブルに対するロックを確認する
特定のテーブルに対してロックがかかっているか知りたい場合に便利なクエリです。上記のクエリにWHERE
句でテーブル名を指定します。
sql
SELECT
lo.SESSION_ID,
s.SERIAL#,
s.USERNAME,
s.OSUSER,
s.PROGRAM,
s.MACHINE,
s.STATUS,
s.EVENT,
s.WAIT_CLASS,
o.OWNER,
o.OBJECT_NAME,
o.OBJECT_TYPE,
lo.LOCKED_MODE,
CASE lo.LOCKED_MODE
WHEN 0 THEN 'None'
WHEN 1 THEN 'Null'
WHEN 2 THEN 'Row-S (SS)'
WHEN 3 THEN 'Row-X (SX)'
WHEN 4 THEN 'Share (S)'
WHEN 5 THEN 'Share Row-X (SSX)'
WHEN 6 THEN 'Exclusive (EX)'
ELSE TO_CHAR(lo.LOCKED_MODE)
END AS LOCKED_MODE_DESC,
s.SQL_ID,
s.PREV_SQL_ID
FROM
V$LOCKED_OBJECT lo
JOIN
DBA_OBJECTS o ON lo.OBJECT_ID = o.OBJECT_ID
JOIN
V$SESSION s ON lo.SESSION_ID = s.SID
WHERE
o.OBJECT_NAME = 'TARGET_TABLE_NAME' -- ここに確認したいテーブル名を指定
AND o.OWNER = 'TARGET_OWNER' -- ここにテーブルの所有者スキーマを指定
AND o.OBJECT_TYPE = 'TABLE'
ORDER BY
lo.LOCKED_MODE DESC;
'TARGET_TABLE_NAME'
と'TARGET_OWNER'
を実際のテーブル名とスキーマ名に置き換えて実行してください。
クエリ3:ロック待ちが発生しているセッションとロック保持セッションを確認する
このクエリは、V$LOCK
ビューを使用して、どのセッションがどのロックを待ち、どのセッションがそのロックを保持しているか(ブロッキングセッション)を特定します。テーブルロック(TYPE = 'TM'
)に絞り込んでいます。
“`sql
SELECT
waiting_l.SID AS waiting_sid,
waiting_s.SERIAL# AS waiting_serial#,
waiting_s.USERNAME AS waiting_user,
waiting_s.MACHINE AS waiting_machine,
waiting_s.PROGRAM AS waiting_program,
waiting_s.STATUS AS waiting_status,
waiting_s.EVENT AS waiting_event,
waiting_s.WAIT_CLASS AS waiting_wait_class,
waiting_s.SQL_ID AS waiting_sql_id,
waiting_s.PREV_SQL_ID AS waiting_prev_sql_id,
holding_l.SID AS holding_sid,
holding_s.SERIAL# AS holding_serial#,
holding_s.USERNAME AS holding_user,
holding_s.MACHINE AS holding_machine,
holding_s.PROGRAM AS holding_program,
holding_s.STATUS AS holding_status,
holding_s.EVENT AS holding_event, — holding_s.EVENTが’enq: TM – contention’なら、そのセッションも何かを待っている可能性あり
holding_s.WAIT_CLASS AS holding_wait_class,
holding_s.SQL_ID AS holding_sql_id,
holding_s.PREV_SQL_ID AS holding_prev_sql_id,
holding_l.TYPE, — ロックタイプ (e.g., TM)
holding_l.ID1, — オブジェクトID (TMロックの場合)
holding_l.ID2, — TMロックの場合は0
holding_l.LMODE, — 保持しているロックモード
holding_l.REQUEST, — 保持セッションは通常0
waiting_l.REQUEST, — 待っているロックモード
CASE holding_l.LMODE
WHEN 0 THEN ‘None’
WHEN 1 THEN ‘Null’
WHEN 2 THEN ‘Row-S (SS)’
WHEN 3 THEN ‘Row-X (SX)’
WHEN 4 THEN ‘Share (S)’
WHEN 5 THEN ‘Share Row-X (SSX)’
WHEN 6 THEN ‘Exclusive (EX)’
ELSE TO_CHAR(holding_l.LMODE)
END AS holding_lock_mode_desc,
CASE waiting_l.REQUEST
WHEN 0 THEN ‘None’
WHEN 1 THEN ‘Null’
WHEN 2 THEN ‘Row-S (SS)’
WHEN 3 THEN ‘Row-X (SX)’
WHEN 4 THEN ‘Share (S)’
WHEN 5 THEN ‘Share Row-X (SSX)’
WHEN 6 THEN ‘Exclusive (EX)’
ELSE TO_CHAR(waiting_l.REQUEST)
END AS waiting_lock_mode_desc
FROM
V$LOCK holding_l
JOIN
V$LOCK waiting_l
ON holding_l.TYPE = waiting_l.TYPE
AND holding_l.ID1 = waiting_l.ID1
AND holding_l.ID2 = waiting_l.ID2
AND holding_l.LMODE != 0 — Holding lock mode > 0 (actually holds a lock)
AND waiting_l.REQUEST != 0 — Waiting lock mode > 0 (requests a lock)
JOIN
V$SESSION holding_s ON holding_l.SID = holding_s.SID
JOIN
V$SESSION waiting_s ON waiting_l.SID = waiting_s.SID
WHERE
holding_l.BLOCK = 1 — ロックをブロックしているセッション
AND waiting_l.REQUEST > 0 — ロックを要求しているセッション (待ち状態)
AND holding_l.TYPE IN (‘TM’, ‘TX’) — テーブルロック(TM)または行ロック(TX)に絞る
ORDER BY
holding_l.TYPE, holding_l.ID1;
“`
このクエリは少し複雑ですが、ロック待ちが発生している場合に、その待ち状態にあるセッション(waiting_sid
など)と、そのロックを保持しているセッション(holding_sid
など)の関係を特定できます。
特に重要なのは、
waiting_s.EVENT
: 待機イベントがenq: TM - contention
であれば、テーブルロック待ちです。holding_l.LMODE
: 保持されているロックモード。このモードとwaiting_l.REQUEST
(要求されているモード)の間に互換性がないため、待ちが発生しています。holding_s.SQL_ID
,holding_s.PREV_SQL_ID
: ブロッキングセッションが実行している、または直前に実行したSQL。これがロックの原因である可能性が高いです。
必要に応じて、holding_l.TYPE = 'TM'
のみを対象とするなど、WHERE句を調整してください。また、ID1
がオブジェクトIDなので、DBA_OBJECTS
と結合してオブジェクト名を表示させると、より分かりやすくなります。
クエリ4:ロックされているオブジェクト名、保持セッション、待ちセッションをまとめて確認する
上記のクエリを改良し、ロック対象のオブジェクト名と、そのロックを保持しているセッション、そしてそのロックを待っているセッションの関係を一覧で表示するクエリです。
“`sql
SELECT
s.SID,
s.SERIAL#,
s.USERNAME,
s.OSUSER,
s.MACHINE,
s.PROGRAM,
s.STATUS,
s.WAIT_CLASS,
s.EVENT,
s.SQL_ID,
s.PREV_SQL_ID,
CASE
WHEN l.LMODE = 0 THEN ‘None’
WHEN l.LMODE = 1 THEN ‘Null’
WHEN l.LMODE = 2 THEN ‘Row-S (SS)’
WHEN l.LMODE = 3 THEN ‘Row-X (SX)’
WHEN l.LMODE = 4 THEN ‘Share (S)’
WHEN l.LMODE = 5 THEN ‘Share Row-X (SSX)’
WHEN l.LMODE = 6 THEN ‘Exclusive (EX)’
ELSE TO_CHAR(l.LMODE)
END AS LMODE_DESC, — 保持しているロックモード
CASE
WHEN l.REQUEST = 0 THEN ‘None’
WHEN l.REQUEST = 1 THEN ‘Null’
WHEN l.REQUEST = 2 THEN ‘Row-S (SS)’
WHEN l.REQUEST = 3 THEN ‘Row-X (SX)’
WHEN l.REQUEST = 4 THEN ‘Share (S)’
WHEN l.REQUEST = 5 THEN ‘Share Row-X (SSX)’
WHEN l.REQUEST = 6 THEN ‘Exclusive (EX)’
ELSE TO_CHAR(l.REQUEST)
END AS REQUEST_DESC, — 要求しているロックモード (待ちの場合)
l.BLOCK, — 1: 他をブロックしている, 0: ブロックしていない, 2: グローバルロックをブロックしている(RAC)
o.OWNER,
o.OBJECT_NAME,
o.OBJECT_TYPE
FROM
V$SESSION s,
V$LOCK l,
DBA_OBJECTS o
WHERE
s.SID = l.SID
AND l.TYPE = ‘TM’ — テーブルロック
AND l.ID1 = o.OBJECT_ID (+) — (+)は外部結合。ロック対象がオブジェクトでない場合も表示するため
AND o.OBJECT_TYPE (+) IN (‘TABLE’, ‘VIEW’, ‘MATERIALIZED VIEW’) — オブジェクトタイプを絞る場合
ORDER BY
l.BLOCK DESC, l.LMODE DESC, o.OWNER, o.OBJECT_NAME, s.SID;
``
LMODE_DESC
このクエリは、全てのTMロックを表示し、それぞれのセッションがロックを保持しているか()、ロックを待っているか(
REQUEST_DESC)、そして他のセッションをブロックしているか(
BLOCK=1)を一覧で確認できます。
BLOCK=1の行が、他のセッションを待たせているロック保持セッションです。
REQUEST_DESC`が’None’以外になっている行が、ロック待ち状態のセッションです。
クエリ5:ロックを引き起こしているSQL文を確認する
ロック保持セッションが実行しているSQL文を確認したい場合に、上記のクエリで得られたSQL_ID
またはPREV_SQL_ID
を使用してV$SQLAREA
またはV$SQL
を問い合わせます。
sql
SELECT sql_fulltext
FROM V$SQLAREA
WHERE SQL_ID = 'SQL_ID_FROM_V_SESSION'; -- V$SESSIONで取得したSQL_IDを指定
または、長時間実行されているSQLを確認するクエリと組み合わせて使用することも有効です。
3.2. ロック待ちチェーン (V$WAIT_CHAINS)
Oracle 11g R2以降では、V$WAIT_CHAINS
ビューを使用すると、複雑なロック待ち(デッドロックを含む)の関係性をより分かりやすく確認できます。このビューは、どのセッションがどのセッションを待っているか、そして最終的にどのセッションが待たずにリソースを保持しているか(チェーンの根っこ)を示してくれます。
V$WAIT_CHAINS
はテキスト形式でロック待ちの「鎖」を表示します。
sql
SELECT * FROM V$WAIT_CHAINS;
出力例(一部を簡略化):
IN_WAIT_SESSION WAITING_FOR_SESSION_ID LOCK_TYPE MODE_HELD MODE_REQUESTED LOCK_ID1 LOCK_ID2 ...
---------------- ---------------------- ------------ ---------- -------------- -------- -------- ...
(SID 100) (SID 200) TM 3 (RX) 6 (EX) 12345 0 ... -- SID 100 は SID 200 のロック待ち
(SID 200) (SID 300) TX 6 (EX) 6 (EX) ...
(SID 300) (null) TX 6 (EX) 0 (None) ... -- SID 300 は何も待っていない (ロック保持者)
この例では、SID 100はSID 200が保持しているロック(TMロック、ID1=12345, モード3 RX)の解放を待っています(モード6 EXを要求)。そしてSID 200はSID 300が保持しているロック(TXロック)を待っています。SID 300は待機しておらず、ロックチェーンの根っこ(ブロッキングセッション)です。
V$WAIT_CHAINS
はデッドロック(お互いのロックを待ち合う状態)が発生している場合にも、その関係性を表示してくれます。Oracleはデッドロックを自動検出し、通常はORA-00060
エラーを発生させて片方のセッションを強制終了(ロールバック)しますが、その原因究明に役立ちます。
3.3. Enterprise Manager (EM) を利用した確認
Oracle Enterprise Manager (EM) Cloud Control または EM Express といったGUIツールを利用している場合、これらのツールにもセッション情報やロック情報を確認できる機能が搭載されています。
- パフォーマンスハブ: アクティブセッション、待機イベントなどをグラフィカルに表示。ロックに関連する待機イベント(
enq: TM - contention
,enq: TX - row lock contention
など)が多い場合、問題が発生していることを視覚的に把握できます。 - セッションブラウザ/セッション詳細: 個々のセッションの詳細情報(状態、実行中のSQL、待機イベントなど)を確認できます。ブロッキングセッションやロック待ちセッションを特定し、詳細をドリルダウンできます。
- ブロックセッションレポート: ロック待ちの関係性をツリー構造やリストで表示し、ブロッキングセッションと待機セッションの関係を分かりやすく示します。
GUIツールは直感的に情報を把握できる利点がありますが、詳細な分析や履歴情報の取得にはSQLクエリの方が柔軟性が高い場合があります。状況に応じて使い分けると良いでしょう。
4. テーブルロックの原因と解消方法
テーブルロックが問題となるのは、通常、排他性の高いロックが長時間保持されたり、多くのセッションが同じロックを待機したりする場合です。ロック問題を解決するには、まずその原因を特定し、状況に応じて適切な対応を取る必要があります。
4.1. テーブルロックの一般的な原因
テーブルロックが長時間保持されたり、ロック待ちが発生したりする主な原因は以下の通りです。
- 長時間実行されているトランザクション:
- DML文(
INSERT
,UPDATE
,DELETE
,MERGE
)を実行したセッションが、長時間コミットまたはロールバックせずにいる場合、そのセッションが取得した行ロック(TXロック)とテーブルロック(TMロック – RXモードなど)は解放されません。他のセッションが同じデータやテーブルに対して競合する操作を行おうとすると、ロック待ちが発生します。 - 明示的に
LOCK TABLE
文でSモードやEXモードのテーブルロックを取得し、そのトランザクションを長時間コミット/ロールバックしない場合。
- DML文(
- 不適切なアプリケーション設計:
- アプリケーションがユーザーの入力を待っている間や、外部システムとの連携処理中に、データベーストランザクションを開きっぱなしにし、ロックを保持し続けてしまう設計。
- トランザクション処理の途中でエラーが発生したが、適切にエラーハンドリングされず、トランザクションが終了しないまま放置される。
- バッチ処理などが、排他性の高いロック(例:
LOCK TABLE IN EXCLUSIVE MODE
)を取得したまま、長時間の処理を行っている。
- デッドロック:
- 2つ以上のセッションがお互いに相手が保持しているロックを待ち合い、どのセッションも処理を進められなくなる状態。Oracleはデッドロックを自動検出し、片方のセッションに
ORA-00060
エラーを発生させて強制終了(ロールバック)させます。
- 2つ以上のセッションがお互いに相手が保持しているロックを待ち合い、どのセッションも処理を進められなくなる状態。Oracleはデッドロックを自動検出し、片方のセッションに
- バッチ処理とオンライン処理の衝突:
- バッチ処理が大量のデータを更新したり、テーブル全体をロックしたりする際に、同時に実行されているオンライン処理がそのロック待ちとなる。
- DDL操作の実行:
ALTER TABLE
,DROP TABLE
,CREATE INDEX
などのDDL文は、実行中に排他DDLロックなどを取得し、その間はテーブルに対するDMLや他のDDLがブロックされる場合があります。特にデータ量の多いテーブルに対するDDLは実行時間が長くなり、影響が大きくなります。- DDL実行中に開始されたDMLが、DDLの完了を待つマークDMLロックを取得し、逆にDDLがDMLの完了を待つという待ちが発生する場合もあります。
4.2. テーブルロックの解消方法
ロック問題を解消するための基本的な手順は以下の通りです。
- 問題の特定: どのテーブルでロックが発生しているか、どのセッションがロックを保持しているか(ブロッキングセッション)、そしてどのセッションがロック待ちになっているかを特定します。これには、前述のSQLクエリ(特にクエリ3, 4)が役立ちます。
- 原因の究明: ロックを保持しているセッションが何を実行しているか(
V$SESSION.SQL_ID
,PREV_SQL_ID
からSQLテキストを確認)、セッションの状態(V$SESSION.STATUS
)、待機イベント(V$SESSION.EVENT
,WAIT_CLASS
)、プログラム名(V$SESSION.PROGRAM
)などを確認し、なぜそのロックが取得され、なぜ解放されないのか(トランザクションが終了しないのか)を究明します。アプリケーションの問題か、特定のSQLの問題か、ユーザー操作の問題かなどを判断します。 -
対処: 原因に応じて適切な対応を行います。
-
推奨される対応:原因セッションでのトランザクション終了 (COMMIT / ROLLBACK)
- ロックを保持しているセッションが、アプリケーションからの通常の操作で実行されている場合、そのアプリケーションでトランザクションを終了させる(コミットまたはロールバックする)のが最も安全な方法です。例えば、ユーザーに処理を完了させる、アプリケーションを正常終了させるなどです。これにより、そのセッションが保持しているロックは適切に解放され、待機していた他のセッションが処理を再開できます。
- 原因となっているSQLを特定できたら、そのSQLがなぜ長時間実行されているのか(パフォーマンス問題、大量処理など)を調査し、SQLチューニングを行うことも根本的な解決策になります。
-
緊急時の対応:セッションの切断または強制終了 (KILL SESSION)
- 原因となっているセッションがアプリケーション側から操作できない場合や、応答しない場合など、緊急を要する場合には、データベース側からセッションを切断または強制終了することができます。
- 切断 (Disconnect):
ALTER SYSTEM DISCONNECT SESSION 'SID,SERIAL#' IMMEDIATE;
- このコマンドは、セッションのネットワーク接続を切断します。セッションはまだサーバープロセスとして存在し、現在実行中のSQLやトランザクションは完了しようと試みます。トランザクションがコミット/ロールバックされずに終了した場合、データベースは自動的にそのトランザクションをロールバックします。ロックはトランザクションの終了に伴って解放されます。
IMMEDIATE
を指定すると、アクティブなコールが完了するのを待たずに切断を試みます。
- このコマンドは、セッションのネットワーク接続を切断します。セッションはまだサーバープロセスとして存在し、現在実行中のSQLやトランザクションは完了しようと試みます。トランザクションがコミット/ロールバックされずに終了した場合、データベースは自動的にそのトランザクションをロールバックします。ロックはトランザクションの終了に伴って解放されます。
- 強制終了 (Kill):
ALTER SYSTEM KILL SESSION 'SID,SERIAL#' IMMEDIATE;
- このコマンドは、サーバープロセス自体に終了要求を送ります。プロセスは速やかに終了処理を開始し、現在のトランザクションをロールバックします。
IMMEDIATE
を指定すると、トランザクションの完了を待たずに、即座にプロセスを終了させようとします。この方法は最も強力ですが、データベースプロセスを突然終了させるため、システムの負荷を一時的に高めたり、内部リソースのクリーンアップに時間がかかったりする可能性があります。特にIMMEDIATE
オプションは強力で、予期しない副作用(例えば、SGA内のキャッシュされた情報の破棄やリカバリ処理の発生)を引き起こす可能性もあるため、慎重に使用する必要があります。 通常はALTER SYSTEM DISCONNECT SESSION ... IMMEDIATE;
の方が安全性が高いとされます。
- このコマンドは、サーバープロセス自体に終了要求を送ります。プロセスは速やかに終了処理を開始し、現在のトランザクションをロールバックします。
- 強制終了の注意点:
KILL SESSION
(特にIMMEDIATE
)は、データベース内部のプロセスを直接操作するため、最後の手段と考えるべきです。実行する前に、対象セッションが保持しているロックの影響範囲(他の多くのセッションが待っているかなど)や、強制終了によってロールバックされる処理の内容(データ整合性への影響)を十分に評価する必要があります。
-
デッドロックの場合:
- Oracleはデッドロックを自動的に検出して解消するため、通常、DBAが手動で介入する必要はありません。解消されたセッションには
ORA-00060
エラーが返されます。 - 繰り返しデッドロックが発生する場合は、アプリケーションのトランザクション設計やSQLのロック取得順序に問題がある可能性が高いです。前述の
V$WAIT_CHAINS
やトレースファイル(ORA-00060
エラー発生時に自動的に出力されることが多い)を解析して原因のSQLや処理フローを特定し、アプリケーション側で修正する必要があります。
- Oracleはデッドロックを自動的に検出して解消するため、通常、DBAが手動で介入する必要はありません。解消されたセッションには
-
DDLロックの場合:
- DDL操作によるロック待ちは、そのDDL操作が完了するまで待つのが基本です。
- DDL操作が非常に長い時間かかっている場合は、そのDDL操作自体がパフォーマンスボトルネックになっている可能性や、他のセッションとの競合(マークDMLロックなど)によって待たされている可能性があります。
- やむを得ずDDL操作を中断したい場合は、そのセッションを特定して強制終了することを検討しますが、DDLの途中でプロセスを終了させると、オブジェクトが不整合な状態になるリスクもあるため、非常に慎重な判断が必要です。可能であれば、実行中のDDLを正常に完了させるか、キャンセル処理(もしあれば)を利用するのが望ましいです。
-
-
恒久的な対策: ロック問題が頻繁に発生する場合は、その根本原因を取り除くための恒久的な対策を講じる必要があります。
- トランザクションの短縮: アプリケーション設計を見直し、データベーストランザクションを可能な限り短くする。特にユーザーの入力待ちや外部連携など、データベース処理以外の時間を含むような長いトランザクションを避ける。
- ロックレベルの見直し: 必要以上に強いロックを取得していないか(例: 不要な
SELECT ... FOR UPDATE
やLOCK TABLE
)。可能であれば、より並行性の高いロック(行ロック主体)になるようにSQLや処理フローを変更する。 - SQLチューニング: 長時間実行されているDML文や、意図しないロックを引き起こしている可能性のあるSQL文を特定し、パフォーマンスチューニングを行う。不適切なインデックスやオプティマイザの挙動が原因となることもあります。
- 競合する処理の実行タイミング調整: バッチ処理とオンライン処理など、同じテーブルにアクセスしてロック競合を引き起こしやすい処理は、実行タイミングをずらす、処理対象を分けるなどの考慮を行う。
- アプリケーションによるリトライ処理: ロック待ちが発生した場合やデッドロックでエラーになった場合に、アプリケーション側で適切にエラーを捕捉し、少し待ってから処理をリトライするなどの仕組みを導入する。
5. テーブルロックに関する運用上の注意点
- ロック待ちはボトルネックの兆候:
V$SESSION
でWAIT_CLASS
がConcurrency
でEVENT
がenq: TM - contention
などとなっているセッションが多く見られる場合、これはデータベースの並行性に問題が発生している、つまりロック競合がボトルネックになっている兆候です。積極的に原因を調査する必要があります。 KILL SESSION IMMEDIATE
は最終手段: 前述の通り、ALTER SYSTEM KILL SESSION ... IMMEDIATE;
コマンドは非常に強力でリスクを伴います。安易に使用せず、他の方法(トランザクションを正常に終了させる、セッションを切断する)を優先的に検討してください。使用する際は、影響範囲とリスクを十分に理解した上で、DBA責任において実施する必要があります。- データ整合性への影響: ロック問題を解決するためにセッションを強制終了することは、そのセッションが実行していたトランザクションをロールバックさせます。これにより、一部の処理結果が失われたり、関連する処理との間でデータ不整合が生じたりするリスクがないか、事前に評価することが重要です。
- デッドロックは自動解消される: デッドロック(
ORA-00060
)が発生しても、Oracleが自動的に検出し解消するため、即時の手動介入は不要です。ただし、繰り返し発生する場合は根本原因の調査と対策が必要です。ORA-00060
エラー発生時のトレースファイルを分析することが原因特定に役立ちます。 - DDL実行時の注意: 大規模なテーブルに対するDDL(特に構造変更を伴うもの)は、長時間ロックを保持し、DMLや他のDDLと競合しやすい特性があります。システム負荷の低い時間帯に実行する、オンラインでのDDL機能(エディションベース再定義など)を利用する、影響範囲を事前にテスト環境で評価するといった対応が必要です。
- 監視: ロック競合は、データベースのパフォーマンスを監視する上で重要な指標の一つです。AWR (Automatic Workload Repository) や Statspack レポートなどを定期的に確認し、待機イベントの上位にロック関連のイベントが出ていないか確認することで、問題の早期発見に繋がります。
まとめ
Oracleデータベースにおけるテーブルロックは、データの整合性と並行性を維持するための重要な制御機構です。DML操作では、行レベルロックと組み合わせてTMロック(Row Share, Row Exclusiveなど)が取得され、並行性を確保しつつ変更対象のデータを保護します。DDL操作では、オブジェクトの構造変更を安全に行うためにDDLロック(排他DDL、共有DDLなど)が取得されます。
しかし、トランザクションの長時間実行、不適切なアプリケーション設計、デッドロック、DDLとDMLの衝突などにより、テーブルロックが問題を引き起こすことがあります。具体的には、必要なロックを取得できずに他のセッションが待機状態となり、システム全体のパフォーマンスが低下します。
テーブルロックの発生状況を確認するには、V$LOCK
, V$LOCKED_OBJECT
, V$SESSION
, DBA_OBJECTS
といった動的パフォーマンスビューを組み合わせたSQLクエリが非常に有効です。これらのビューを問い合わせることで、どのテーブルで、どのようなモードのロックが、どのセッションによって保持または待機されているかを詳細に把握できます。特に、ロックをブロックしているセッション(ブロッキングセッション)を特定し、そのセッションが実行している処理や待機イベントを確認することが、原因究明の鍵となります。また、Oracle 11g R2以降のV$WAIT_CHAINS
はロック待ちの関係性を分かりやすく表示してくれます。
ロック問題が発生した場合の解消方法は、原因となっているトランザクションを正常に終了させる(コミットまたはロールバック)ことが最も推奨される安全な方法です。これが難しい場合や緊急を要する場合には、対象セッションをデータベース側から切断または強制終了することも可能ですが、特にALTER SYSTEM KILL SESSION ... IMMEDIATE;
の使用は最後の手段であり、慎重な判断とリスク評価が必要です。
ロック問題の根本的な解決には、アプリケーションのトランザクション設計の見直し、SQLのチューニング、競合する処理の実行タイミング調整など、システム全体での対応が必要となります。定期的なパフォーマンス監視を通じて、ロック競合の兆候を早期に発見し、 proactivelyに対策を講じることが、安定したデータベース運用には不可欠です。
この記事で紹介したテーブルロックの基本、確認方法、そして解消手順を理解し実践することで、Oracleデータベースで発生するロック問題に自信を持って対応できるようになるでしょう。