OracleのV$LOCKED_OBJECTで現在ロックされているオブジェクトを確認する方法|セッション・テーブル名・ロックモードの調べ方

Oracle

Oracle Databaseで「どのテーブルが現在ロックされているのか」「どのセッションがDMLロックを保持しているのか」を調べる場合、動的パフォーマンスビューのV$LOCKED_OBJECTを利用できます。

Oracle公式ドキュメントでは、V$LOCKED_OBJECTはシステム上のトランザクションが取得したロックを示し、特にどのセッションが、どのオブジェクトに、どのモードでDMLロック(TMタイプのエンキュー)を保持しているかを確認できるビューとして説明されています。

ただし、V$LOCKED_OBJECTだけをSELECTしても、通常はテーブル名がそのまま表示されるわけではありません。OBJECT_IDが表示されるため、実務ではDBA_OBJECTSV$SESSIONとJOINして、オブジェクト名やセッション情報まで確認するのが一般的です。

[参照] Oracle Database公式「V$LOCKED_OBJECT」

V$LOCKED_OBJECTで確認できる情報

V$LOCKED_OBJECTには、ロック対象のオブジェクトID、ロックを保持しているセッションID、Oracleユーザー名、OSユーザー名、ロックモードなどが記録されています。

まず内容だけを確認するなら、次のようにSELECTできます。

SELECT * FROM V$LOCKED_OBJECT;

代表的な列は次のとおりです。

意味
OBJECT_ID ロック対象オブジェクトのID
SESSION_ID ロックを保持しているセッションID
ORACLE_USERNAME Oracleユーザー名
OS_USER_NAME OSユーザー名
PROCESS OSプロセスID
LOCKED_MODE ロックモード
CON_ID マルチテナント環境におけるコンテナID

そのため、「現在DMLロックを保持しているセッションが存在するか」を確認する入口として非常に便利なビューです。

テーブル名まで確認するならDBA_OBJECTSとJOINする

V$LOCKED_OBJECTOBJECT_IDは数値なので、そのままでは「どのテーブルなのか」が分かりにくいことがあります。そこでDBA_OBJECTSとJOINします。

DBA_OBJECTSにはデータベース内のオブジェクト情報があり、OBJECT_IDOWNEROBJECT_NAMEOBJECT_TYPEなどを取得できます。

SELECT lo.session_id, lo.oracle_username, o.owner, o.object_name, o.object_type, lo.locked_mode FROM v$locked_object lo JOIN dba_objects o ON o.object_id = lo.object_id ORDER BY lo.session_id, o.owner, o.object_name;

これにより、たとえば次のような情報として確認できます。

SESSION_ID  ORACLE_USERNAME  OWNER  OBJECT_NAME  OBJECT_TYPE  LOCKED_MODE
----------  ---------------  -----  -----------  -----------  -----------
123         APP_USER         SALES  ORDERS       TABLE        3

この例なら、セッション123のAPP_USERSALES.ORDERSに対してロックモード3のDMLロックを保持していることが分かります。

[参照] Oracle Database公式「DBA_OBJECTS」

どのセッションがロックしているかV$SESSIONとJOINする

障害調査ではオブジェクト名だけでなく、「どの端末・アプリケーション・セッションがロックしているのか」を知る必要があります。その場合はV$SESSIONもJOINすると便利です。

SELECT s.sid, s.serial#, s.username, s.status, s.machine, s.program, o.owner, o.object_name, o.object_type, lo.locked_mode FROM v$locked_object lo JOIN dba_objects o ON o.object_id = lo.object_id JOIN v$session s ON s.sid = lo.session_id ORDER BY s.sid, o.owner, o.object_name;

このSQLでは、SIDだけでなくSERIAL#、接続ユーザー、セッション状態、接続元マシン、プログラム名まで確認できます。

たとえば「ORDERSテーブルがロックされている」という事実だけでは原因は分かりませんが、MACHINEPROGRAMまで確認すれば、「バッチサーバーから接続しているJavaアプリケーションのセッションが保持している」といった切り分けが可能になります。

[参照] Oracle Database公式「V$SESSION」

LOCKED_MODEの数字は何を意味する?

V$LOCKED_OBJECT.LOCKED_MODEには、ロックの種類が数値で格納されています。Oracle公式ドキュメントでは、テーブルロックについて次のように対応付けられています。

LOCKED_MODE 名称 概要
0 NONE 要求したがまだ取得していない
1 NULL Nullロック
2 ROWS_S(SS) Row Share
3 ROW_X(SX) Row Exclusive
4 SHARE(S) Share
5 S/ROW-X(SSX) Share Row Exclusive
6 Exclusive(X) Exclusive

通常のINSERTUPDATEDELETEなどのDMLを実行して未コミットのトランザクションがあると、対象テーブルについてRow Exclusive系のロックが確認されることがあります。

ただし、LOCKED_MODEの数値が大きければ単純に「悪いロック」という意味ではありません。処理内容に応じて必要なロックモードが取得されるため、ロックの存在だけで障害と判断しないことが重要です。

V$LOCKED_OBJECTに行がある=問題が起きている、ではない

Oracle Databaseでは、データの整合性を守るために通常のDML処理でもロックが使用されます。そのため、V$LOCKED_OBJECTに結果が表示されたからといって、必ずデッドロックや異常が起きているわけではありません。

たとえばセッションAがORDERSテーブルの1行をUPDATEし、まだCOMMITしていない場合、そのトランザクションに伴うDMLロックが確認できます。しかし、他の処理がそのロックと競合していなければ、アプリケーションは問題なく動作している可能性があります。

重要なのは「ロックが存在するか」だけではなく、「そのロックによって別セッションが待たされているか」です。

したがって、処理停止やSQL待機を調査する場合はV$LOCKED_OBJECTだけで完結させず、V$SESSIONの待機情報やブロッキングセッションも合わせて確認します。

「ロック保持」と「ロック待ち」は区別する

V$LOCKED_OBJECTは、どのセッションがどのオブジェクトにDMLロックを保持しているかを見るのに適しています。しかし、「誰が誰を待たせているか」を知りたい場合は別の情報も必要です。

V$SESSIONにはBLOCKING_SESSION_STATUSBLOCKING_INSTANCEBLOCKING_SESSIONなどの列があります。Oracle公式ドキュメントでは、BLOCKING_SESSION_STATUS='VALID'の場合、BLOCKING_SESSIONによってブロッキングセッションを識別できるとされています。

SELECT sid, serial#, username, status, event, blocking_session_status, blocking_session, seconds_in_wait FROM v$session WHERE blocking_session_status = 'VALID';

このSQLに結果が出れば、そのセッションが別のセッションによって待たされている可能性を調査できます。

「どのオブジェクトにDMLロックがあるか」はV$LOCKED_OBJECT、「現在どのセッションが誰にブロックされているか」はV$SESSIONというように目的を分けると分かりやすくなります。

ブロッキングセッションと最終ブロッカーを確認する

複数のセッションが連鎖的に待機していると、直接のブロッカーだけ確認しても根本原因にたどり着けない場合があります。

V$SESSIONにはFINAL_BLOCKING_SESSIONもあり、待機チェーンをたどった最終的なブロッキングセッションを確認できます。

SELECT sid, serial#, username, blocking_session, final_blocking_session, event, seconds_in_wait FROM v$session WHERE blocking_session_status = 'VALID';

たとえばセッション300がセッション200を待ち、セッション200がセッション100を待っている場合、直接のブロッカーと最終的なブロッカーが異なる可能性があります。

Oracle公式ドキュメントでは、FINAL_BLOCKING_SESSIONはセッション同士の待機チェーンを追跡した最終要素として説明されています。

[参照] Oracle Database公式「V$SESSION」

実務で使いやすいロック確認SQL

単純な確認であれば、オブジェクト、ロック保持セッション、接続元をまとめて表示するSQLを用意しておくと便利です。

SELECT s.sid, s.serial#, s.username, s.status, s.machine, s.program, lo.oracle_username, o.owner, o.object_name, o.object_type, lo.locked_mode FROM v$locked_object lo JOIN dba_objects o ON o.object_id = lo.object_id JOIN v$session s ON s.sid = lo.session_id ORDER BY o.owner, o.object_name, s.sid;

結果を確認するときは、特にSIDSERIAL#USERNAMEMACHINEPROGRAMOBJECT_NAMELOCKED_MODEを見ます。

このSQLだけで「障害原因が確定する」わけではありませんが、ロック調査の第一段階として、誰がどのオブジェクトにDMLロックを持っているのかを把握できます。

特定のテーブルがロックされているか確認するSQL

対象テーブルが分かっているなら、DBA_OBJECTS側で絞り込むと確認しやすくなります。

SELECT s.sid, s.serial#, s.username, s.machine, s.program, o.owner, o.object_name, lo.locked_mode FROM v$locked_object lo JOIN dba_objects o ON o.object_id = lo.object_id JOIN v$session s ON s.sid = lo.session_id WHERE o.owner = 'SALES' AND o.object_name = 'ORDERS';

この例ではSALES.ORDERSにDMLロックを保持しているセッションを調べます。

Oracleでは引用符を使わずに作成された一般的なオブジェクト名は大文字で格納されるため、条件も'SALES''ORDERS'のように大文字で指定することが多くなります。

行ロックの「具体的な1行」までV$LOCKED_OBJECTで分かるわけではない

V$LOCKED_OBJECTの名前から、「UPDATEによってロックされている具体的な行をすべて一覧表示できる」と考えてしまうことがあります。しかし、このビューはオブジェクトに対するDMLロック情報を見るためのもので、ロック中の各行を一覧表示するビューではありません。

行ロック競合で待機しているセッションについては、V$SESSIONROW_WAIT_OBJ#ROW_WAIT_FILE#ROW_WAIT_BLOCK#ROW_WAIT_ROW#などがあります。

Oracle公式によると、これらの列はセッションが別トランザクションのCOMMITを現在待っており、ROW_WAIT_OBJ#-1ではない場合などに有効です。

したがって、V$LOCKED_OBJECTでオブジェクトレベルのロック保持状況を確認し、必要に応じてV$SESSIONで行待機情報まで掘り下げるのが適切です。

V$LOCKED_OBJECTは「すべての種類のOracleロック」を一覧表示するビューではない

ここも重要な注意点です。Oracle公式はV$LOCKED_OBJECTについて、どのセッションがどのオブジェクトにどのモードでDMLロック、つまりTMタイプのエンキューを保持しているかを示すビューと説明しています。

したがって、「Oracle内部に存在するあらゆるロック・ラッチ・エンキュー・待機をこのビューだけですべて確認できる」と考えるのは正確ではありません。

より低レベルのロック情報を確認する場合にはV$LOCKなど、目的に応じて別の動的パフォーマンスビューを利用します。

ロック調査では、まず何を知りたいのかを明確にすることが重要です。「オブジェクトをDMLロックしているセッションを知りたい」のか、「ブロックされているセッションを知りたい」のか、「特定の待機イベントの原因を知りたい」のかによって、見るビューが変わります。

UPDATEしただけでV$LOCKED_OBJECTに表示される例

たとえばセッションAで次のような更新を実行し、COMMITせずにそのままにしたとします。

UPDATE SALES.ORDERS SET STATUS = 'PROCESSING' WHERE ORDER_ID = 1001;

このトランザクションが継続している間、SALES.ORDERSに関連するDMLロックをV$LOCKED_OBJECTから確認できることがあります。

ここで別セッションBがまったく別の行を更新できる場合もあります。Oracleの行レベルロックでは、「テーブルにロック情報がある=テーブル全体が誰にも更新できない」とは限りません。

さらにセッションBが同じORDER_ID=1001を更新しようとすれば、セッションAのトランザクション終了を待つ状況になる可能性があります。このとき、ロック保持状況と待機状況を組み合わせて調査します。

COMMITやROLLBACKでロックが解放される

DMLによって取得されたトランザクションロックは、通常、そのトランザクションがCOMMITまたはROLLBACKされるまで保持されます。

アプリケーション障害で「更新処理をしたままCOMMITされず、セッションが長時間残っている」といった場合には、それが後続処理をブロックする原因になることがあります。

そのためロック問題を見つけたときは、単にセッションを強制終了する前に、「正常な長時間トランザクションなのか」「アプリケーションがCOMMITを忘れているのか」「利用者が画面を放置しているのか」「バッチ処理中なのか」を確認することが重要です。

ロックしているセッションを見つけてもすぐKILLしない

ロック調査をすると、セッションIDが分かるためALTER SYSTEM KILL SESSIONを実行したくなることがあります。しかし、本番環境で原因を確認せずセッションを強制終了するのは危険です。

対象セッションが重要なバッチ処理中だった場合、大量の変更がROLLBACKされ、かえって長時間の処理や業務影響が発生する可能性があります。

まずV$SESSIONで接続ユーザー、MACHINEPROGRAM、状態などを確認し、そのセッションが本当に不要なのかを判断します。可能であればアプリケーション担当者や利用者に正常終了・COMMIT・ROLLBACKしてもらうほうが安全です。

「ロックを持っているセッション」と「悪いセッション」は同義ではありません。ロックはOracleの正常なトランザクション制御でも使用されるためです。

セッションを特定するときはSIDだけでなくSERIAL#も確認する

V$LOCKED_OBJECTにはSESSION_IDがありますが、セッションを管理操作の対象として特定する場合にはV$SESSION.SERIAL#も一緒に取得するのが重要です。

SIDは将来別の接続で再利用される可能性があります。そのため、セッションを厳密に識別する際にはSIDSERIAL#を組み合わせます。

SELECT s.sid, s.serial#, s.username, s.machine, s.program, o.owner, o.object_name, lo.locked_mode FROM v$locked_object lo JOIN v$session s ON s.sid = lo.session_id JOIN dba_objects o ON o.object_id = lo.object_id;

特に本番環境で管理操作を検討する場合には、SQL結果を見た直後でも対象セッションが同一であることを慎重に確認する必要があります。

権限不足でV$LOCKED_OBJECTを参照できない場合

V$LOCKED_OBJECTV$SESSIONDBA_OBJECTSといったビューは、一般アプリケーションユーザーから自由に参照できるとは限りません。

必要な権限がない場合、SQL実行時に権限不足のエラーになることがあります。その場合はDBAへ確認し、組織のセキュリティポリシーに従って必要な参照権限を設定します。

ロック確認だけを目的に、一般ユーザーへ過剰な管理権限を与えるのは避けるべきです。特に本番データベースでは最小権限の原則に従うことが重要です。

CDB・PDB環境ではCON_IDにも注意する

マルチテナント構成のOracle Databaseでは、V$LOCKED_OBJECTCON_IDがあります。これは表示された情報がどのコンテナに属するかを示すための列です。

Oracle公式ドキュメントでは、CON_ID=0はCDB全体に関係する情報または非CDB、1はルート、その他の値は該当するコンテナIDを示すと説明されています。

複数PDBを運用している環境では、「同じ名前のテーブルが別PDBにも存在する」といった可能性があります。そのため、必要に応じてCON_IDも取得し、どのコンテナの情報なのか確認します。

SELECT con_id, session_id, oracle_username, object_id, locked_mode FROM v$locked_object ORDER BY con_id, session_id;

RAC環境ではインスタンスも意識する

Oracle Real Application Clusters(RAC)では、複数インスタンスが同じデータベースを構成しています。このため単一インスタンスだけを前提としたV$ビューだけでは、調査対象によっては情報が不足する可能性があります。

RAC全体の動的パフォーマンス情報を調査する場合には、GV$系ビューを使用してINST_IDを含め、どのインスタンスのセッションなのかを識別する方法が一般的です。

特にV$SESSIONBLOCKING_INSTANCEはブロッキングセッションのインスタンス識別子を示すため、RACでのロック待ち調査ではSIDだけでなくインスタンス番号も重要になります。

「現在」しか見えないことにも注意

V$LOCKED_OBJECTは現在の動的な状態を確認するためのビューです。そのため、「10分前にロック待ちが発生したが現在は解消している」という場合、あとから同じ状態をそのまま確認できるとは限りません。

障害が発生している最中なら非常に有用ですが、事後調査では監視ログや診断情報、利用可能な環境ではASHなど、履歴を扱える情報源が必要になることがあります。

したがって、頻繁にロック障害が発生するシステムでは、問題発生時にSID、SERIAL#、SQL、待機イベント、ブロッカー、オブジェクトなどを収集できる監視設計も重要です。

V$LOCKED_OBJECT・V$SESSION・DBA_OBJECTSの使い分け

ロック調査でよく使うビューを整理すると、それぞれの役割が分かりやすくなります。

確認したいこと 主なビュー
どのオブジェクトにDMLロックが保持されているか V$LOCKED_OBJECT
OBJECT_IDからテーブル名などを調べる DBA_OBJECTS
ロック保持セッションの詳細を調べる V$SESSION
誰にブロックされているか V$SESSION.BLOCKING_SESSION
最終的なブロッカーを調べる V$SESSION.FINAL_BLOCKING_SESSION
より低レベルなロック情報を調査する V$LOCK

V$LOCKED_OBJECTだけですべてを解決しようとせず、オブジェクト情報・セッション情報・待機情報を組み合わせることがOracleのロック調査では重要です。

ロック障害を調査するときのおすすめ手順

実際に「SQLが終わらない」「更新処理が止まった」という問題が発生した場合は、順番に確認すると切り分けやすくなります。

  1. V$SESSIONで待機中のセッションとBLOCKING_SESSIONを確認する
  2. V$LOCKED_OBJECTでDMLロックを保持しているセッションとオブジェクトIDを確認する
  3. DBA_OBJECTSとJOINしてテーブル名・所有者を特定する
  4. V$SESSIONからSIDSERIAL#、ユーザー、端末、プログラムを確認する
  5. 正常な長時間処理か、異常な未コミット処理かを判断する
  6. アプリケーション側でCOMMIT・ROLLBACK可能か確認する
  7. どうしても必要な場合だけDBA判断でセッション管理を検討する

この流れなら、「ロックがあるから即KILL」という危険な判断を避けつつ、実際に誰が誰を待たせているのかを調査できます。

まとめ:V$LOCKED_OBJECTでDMLロック中のオブジェクトを確認できる

Oracle DatabaseのV$LOCKED_OBJECTを使えば、どのセッションが、どのオブジェクトに、どのモードでDMLロック(TMエンキュー)を保持しているのかを確認できます。

ただし、表示される対象はOBJECT_IDなので、人間が分かりやすいテーブル名やオブジェクト名を確認するにはDBA_OBJECTSとのJOINが便利です。

SELECT s.sid, s.serial#, s.username, o.owner, o.object_name, o.object_type, lo.locked_mode FROM v$locked_object lo JOIN dba_objects o ON o.object_id = lo.object_id JOIN v$session s ON s.sid = lo.session_id ORDER BY s.sid, o.owner, o.object_name;

また、V$LOCKED_OBJECTに行が存在すること自体は異常ではありません。通常のDMLトランザクションでもロックは使用されます。問題なのは、そのロックによって別セッションが長時間待機するなど、業務処理へ影響している場合です。

そのため実際のトラブルシューティングでは、V$LOCKED_OBJECTでロック対象を確認し、DBA_OBJECTSでオブジェクト名を特定し、V$SESSIONBLOCKING_SESSIONやセッション情報を使ってブロッカーを調べる、という組み合わせが有効です。

[参照] Oracle Database公式「V$LOCKED_OBJECT」

コメント

タイトルとURLをコピーしました