【Oracle】ORA-04021の原因と解決方法|timeout occurred while waiting to lock object・DDLやコンパイルが待たされる時の確認ポイント

【Oracle】ORA-04021の原因と解決方法|timeout occurred while waiting to lock object・DDLやコンパイルが待たされる時の確認ポイント Oracle

ORA-04021: timeout occurred while waiting to lock object は、Oracleが対象オブジェクトをロックしようとしたものの、待機時間内にロックを取得できなかった時に発生するエラーです。特に ALTER TABLECREATE OR REPLACE VIEW、パッケージやプロシージャのコンパイル、トリガーの再作成、パッチ適用やリリース作業中に出やすいです。

ポイントは、単なる行ロックではなく、DDLやPL/SQLオブジェクトの再コンパイルで必要になるライブラリキャッシュやDDLロック待ちとして現れることが多い点です。待っている側だけを見ても解決しにくいため、対象オブジェクトを使用中のセッション、コンパイル中のセッション、長時間実行SQL、デプロイ作業の重複を順番に確認します。

先に結論
ORA-04021が出たら、まず対象オブジェクト名を確認し、v$sessiondba_ddl_locksv$accessv$locked_object で誰が何を使っているかを見ます。業務SQLが実行中なら待つ、デプロイやコンパイルが競合しているなら片方を止める、明らかな放置セッションなら影響確認後に切断する、という順で判断します。
スポンサーリンク

状況別の最短確認ルート

ORA-04021は発生場面によって見る場所が少し変わります。まず自分の状況に近い入口から確認すると、ロック元にたどり着きやすくなります。

パッケージやプロシージャのコンパイルで出た

v$accessdba_ddl_locks で、対象PL/SQLオブジェクトを実行中または参照中のセッションを探します。

ビューやトリガーのCREATE OR REPLACEで出た

依存オブジェクトの使用中セッション、無効オブジェクト、別のデプロイ処理が同時に動いていないかを確認します。

ALTER TABLEやDROP TABLEで出た

v$locked_object で対象テーブルを触っているセッションを確認し、実行中SQLとトランザクション有無を見ます。

リリース作業中に何度も出る

作業重複、常駐アプリ、夜間バッチ、監視ジョブの接続を確認し、DDL実行前の停止順序を見直します。

ORA-04021とは

Oracle公式のエラー説明では、ORA-04021は指定されたオブジェクトのロック待ちがタイムアウトした状態です。つまり、対象オブジェクトに対して必要なロックを取りたい処理があり、別のセッションが使用中またはロック保持中だったため、一定時間待っても進めなかったという意味です。

よくある発生場面は、利用中のパッケージをコンパイルする、参照されているビューを再作成する、実行中SQLがあるテーブルにDDLを実行する、デプロイ処理が同じオブジェクトを同時に触る、といったケースです。無効オブジェクトやコンパイルエラーが絡む場合は ORA-04063PLS-00905ORA-06508 と合わせて確認すると原因を絞りやすくなります。

まず対象オブジェクトと実行中セッションを確認する

エラーメッセージにオブジェクト名が出ている場合は、その名前を手がかりにします。まずは現在のセッションで、待機イベントやブロッキング情報を確認します。

check-waiting-sessions.sql
SELECT sid,
       serial#,
       username,
       machine,
       program,
       module,
       status,
       event,
       blocking_session,
       seconds_in_wait
FROM v$session
WHERE event LIKE 'library cache%'
   OR event LIKE 'enq: DDL%'
   OR blocking_session IS NOT NULL
ORDER BY seconds_in_wait DESC;

eventlibrary cache locklibrary cache pinenq: DDL 系の待機が出ている場合、DDLやコンパイルに必要なロック待ちが疑えます。セッション確認の基本は Oracleのセッション確認・強制切断方法 でも詳しく整理しています。

DDLロックを確認する

DDLやコンパイルが待たされている場合は、DBA_DDL_LOCKS で対象オブジェクトのロック状況を確認します。権限がない環境ではDBAに依頼するか、参照可能なビューで代替します。

check-ddl-locks.sql
SELECT session_id,
       owner,
       name,
       type,
       mode_held,
       mode_requested
FROM dba_ddl_locks
WHERE owner = 'APP_SCHEMA'
  AND name = 'TARGET_OBJECT'
ORDER BY session_id;
check-ddl-locks-with-session.sql
SELECT l.session_id AS sid,
       s.serial#,
       s.username,
       s.machine,
       s.program,
       s.module,
       l.owner,
       l.name,
       l.type,
       l.mode_held,
       l.mode_requested
FROM dba_ddl_locks l
JOIN v$session s
  ON s.sid = l.session_id
WHERE l.owner = 'APP_SCHEMA'
  AND l.name = 'TARGET_OBJECT'
ORDER BY l.session_id;

MODE_HELD が出ているセッションは何らかのロックを保持しています。MODE_REQUESTED が出ているセッションは、そのロックを取りたい側です。デプロイ中に複数の作業が同じオブジェクトを触っている場合は、まず作業重複を止めます。

PL/SQLオブジェクトを使用中のセッションを確認する

パッケージ、プロシージャ、ファンクション、ビューなどでORA-04021が出る場合は、v$access で対象オブジェクトを現在参照しているセッションを確認できます。DDLロックだけでは見えにくい、実行中・参照中のセッションを探す時に役立ちます。

check-object-access.sql
SELECT a.sid,
       s.serial#,
       s.username,
       s.machine,
       s.program,
       s.module,
       a.owner,
       a.object,
       a.type
FROM v$access a
JOIN v$session s
  ON s.sid = a.sid
WHERE a.owner = 'APP_SCHEMA'
  AND a.object = 'TARGET_OBJECT'
ORDER BY a.sid;

ここで対象オブジェクトを参照しているセッションが見つかったら、そのセッションの programmodule、実行中SQLを確認します。アプリケーションが常時パッケージを呼び続けている場合、コンパイル作業は業務停止時間や接続停止後に実行したほうが安全です。

テーブルへのDDL待ちならv$locked_objectも見る

ALTER TABLEDROP TABLE、インデックス作成などで待っている場合は、v$locked_object とオブジェクト情報を結合して、どのセッションが対象テーブルを使っているか確認します。

check-locked-object.sql
SELECT s.sid,
       s.serial#,
       s.username,
       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
WHERE o.owner = 'APP_SCHEMA'
  AND o.object_name = 'TARGET_TABLE'
ORDER BY s.sid;

DDLのロック待ちは ORA-00054 と近い領域です。ORA-00054はNOWAIT指定やDDL_LOCK_TIMEOUTとの関係で出ることが多く、ORA-04021はオブジェクトロック待ちがタイムアウトした時に出ます。どちらも、ロック元セッションの特定が最初の作業になります。

実行中SQLを確認する

ロックを持っているセッションが業務処理中なのか、放置された接続なのかを判断するには、現在または直近のSQLを確認します。実行中SQLが重要処理なら、強制切断ではなく完了を待つ判断になります。

check-current-sql.sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.status,
       s.machine,
       s.program,
       s.sql_id,
       q.sql_text
FROM v$session s
LEFT JOIN v$sql q
  ON q.sql_id = s.sql_id
WHERE s.sid = 123;
check-long-running-sql.sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.machine,
       s.program,
       s.status,
       s.last_call_et,
       s.sql_id
FROM v$session s
WHERE s.type = 'USER'
ORDER BY s.last_call_et DESC;

切断してよいか判断する

ORA-04021の復旧では、ロック元セッションを切断すれば進むことがあります。ただし、未コミット更新、長時間バッチ、オンライン処理中のセッションを切ると業務影響が出ます。切断前に、接続元、プログラム、実行SQL、トランザクション有無を確認します。

check-active-transaction.sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.machine,
       s.program,
       t.start_time,
       t.used_ublk,
       t.used_urec
FROM v$session s
JOIN v$transaction t
  ON t.ses_addr = s.saddr
WHERE s.sid = 123;
kill-session-after-check.sql
ALTER SYSTEM KILL SESSION '123,456' IMMEDIATE;
切断は影響確認後に行う
ロック元が業務更新中の場合、強制切断によってロールバックや処理失敗が発生します。リリース作業の重複や放置された開発ツール接続など、切断してよい根拠がある場合に対象を絞って実行します。

切断前に、アプリケーション側で該当接続を止められるなら、DB側から強制切断するよりも安全です。特に接続プール経由のセッションは、DB側で切断してもアプリケーションがすぐ再接続することがあります。先にジョブ停止、アプリ停止、接続プールの一時停止を検討します。

未コミット処理やロック待ちが絡む場合は、OracleトランザクションORA-00060ORA-08177 も合わせて見ると、競合の種類を整理しやすくなります。

パッケージ・ビュー・トリガーのコンパイルで出る場合

PL/SQLオブジェクトのコンパイル時にORA-04021が出る場合、対象オブジェクトを実行中のセッションや、依存オブジェクトを触っている別作業が原因になることがあります。まず依存関係と無効状態を確認します。

check-invalid-objects.sql
SELECT owner,
       object_name,
       object_type,
       status
FROM dba_objects
WHERE owner = 'APP_SCHEMA'
  AND object_name IN ('TARGET_PACKAGE', 'TARGET_VIEW', 'TARGET_TRIGGER')
ORDER BY object_type, object_name;
check-object-dependencies.sql
SELECT owner,
       name,
       type,
       referenced_owner,
       referenced_name,
       referenced_type
FROM dba_dependencies
WHERE owner = 'APP_SCHEMA'
  AND name = 'TARGET_PACKAGE'
ORDER BY referenced_owner, referenced_name;

トリガーが無効な場合は ORA-04098、パッケージやビューのコンパイルエラーは ORA-04063、無効オブジェクト全般は PLS-00905 の記事が参考になります。

デプロイ時の再発防止

ORA-04021は、リリース作業と通常業務が重なる時に起きやすいです。デプロイ手順では、同じオブジェクトを同時に触らない、実行中ジョブを止める、DDL実行前にセッションを確認する、コンパイル順を決める、といった準備が有効です。

DDL前に対象セッションを確認する

対象テーブルやパッケージを使っているセッションがないか、事前にv$sessionやDBA_DDL_LOCKSで確認します。

リリース作業を直列化する

複数担当者や複数ジョブが同じパッケージ、ビュー、トリガーを同時に再作成しないようにします。

長時間バッチを止めてからDDLを流す

対象オブジェクトを使うバッチや常駐処理を止め、完了確認後にDDLやコンパイルを実行します。

失敗時の再実行手順を決める

ORA-04021後に同じSQLを連打すると待機が増えます。原因確認、停止、再実行の順番を決めておきます。

pre-release-lock-check.sql
SELECT s.sid,
       s.serial#,
       s.username,
       s.machine,
       s.program,
       s.status,
       s.sql_id
FROM v$session s
WHERE s.type = 'USER'
  AND (
      UPPER(s.program) LIKE '%SQL%'
   OR UPPER(s.module)  LIKE '%BATCH%'
   OR s.blocking_session IS NOT NULL
  )
ORDER BY s.last_call_et DESC;

DDL_LOCK_TIMEOUTを増やせば解決するか

DDL_LOCK_TIMEOUT は、DDLがロックを取得できない時にどれくらい待つかに関係するパラメータです。短時間の業務SQLが終わるのを待てばよいケースでは、待機時間を設けることで失敗を避けられる場合があります。

check-ddl-lock-timeout.sql
SHOW PARAMETER ddl_lock_timeout;

-- セッション単位で待機秒数を設定する例
ALTER SESSION SET ddl_lock_timeout = 60;
待機時間を延ばすだけでは原因は消えない
DDL_LOCK_TIMEOUT を増やしても、放置セッションや長時間バッチが対象オブジェクトを使い続けている場合は、単に失敗までの時間が延びるだけです。まずロック元の特定と作業重複の解消を優先します。隠しパラメータの変更は自己判断で行わず、必要ならOracle Supportの指示に従います。

ORA-00054・ORA-00060との違い

ORA-04021

オブジェクトのロック待ちがタイムアウトしたエラーです。DDL、コンパイル、ライブラリキャッシュ待ちで出やすいです。

ORA-00054

resource busy and acquire with NOWAIT のエラーです。NOWAIT指定やDDL_LOCK_TIMEOUT設定と関係するDDL待ちでよく見ます。

ORA-00060

デッドロックです。複数セッションがお互いのロック解放を待つ状態で、Oracleが検出して片方を失敗させます。

ORA-04063 / PLS-00905

オブジェクト自体がコンパイルエラーや無効状態になっている時のエラーです。ORA-04021の後に再コンパイル失敗として見えることがあります。

対応手順のまとめ

  1. エラーメッセージから対象オブジェクト名を確認する
  2. v$session で待機イベント、接続元、ブロッキング情報を確認する
  3. dba_ddl_locks でDDLロックの保持側と要求側を確認する
  4. PL/SQLやビューなら v$access で使用中セッションを確認する
  5. テーブルDDLなら v$locked_object でロック元セッションを確認する
  6. 実行中SQLとトランザクション有無を見て、待つか止めるか判断する
  7. 放置セッションや作業重複が原因なら、影響確認後に停止または切断する
  8. デプロイ手順、ジョブ停止、作業直列化で再発を防ぐ

よくある質問

ORA-04021は行ロックのエラーですか?

行ロックだけのエラーではありません。DDLやPL/SQLコンパイル時に必要なオブジェクトロック、ライブラリキャッシュロック、DDLロック待ちとして出ることが多いです。

待てば自然に解消しますか?

ロック元の処理が正常に終われば解消することがあります。ただし、放置セッション、止まったリリース作業、長時間バッチが原因なら、原因を取り除かない限り再発します。

すぐALTER SYSTEM KILL SESSIONしてよいですか?

すぐには実行しません。接続元、実行SQL、トランザクション有無を確認し、業務影響とロールバック影響を見てから対象を絞ります。

DDL_LOCK_TIMEOUTを大きくすれば再発防止になりますか?

短いロック待ちを吸収する効果はありますが、根本対策ではありません。対象オブジェクトを使う処理を止める、作業を直列化する、長時間バッチとリリースを重ねない、といった運用面の見直しが必要です。

デプロイ作業で毎回出る場合は何を見直しますか?

作業時間帯、同時実行ジョブ、常駐アプリの接続、コンパイル順、失敗時の再実行手順を見直します。特に同じパッケージやビューを複数処理が同時に触っていないかを確認します。

参考