Oracleデータベースを運用していると、「バッチ処理が終わらない」「画面が固まってロールバックもできない」といったトラブルに直面することがあります。こうした状況の多くは、ロック待ちやゾンビセッションが原因です。本記事では、セッション情報を確認するV$SESSIONの使い方から、ロックを保持するセッションの特定、そしてALTER SYSTEM KILL SESSIONによる強制切断まで、実務で使える手順を体系的に解説します。
- V$SESSIONを使った現在のセッション一覧の確認方法
- ロック待ちセッションの特定(V$LOCK・DBA_BLOCKERS・DBA_WAITERS)
- ALTER SYSTEM KILL SESSIONによる強制切断の手順
- IMMEDIATE オプションとの使い分け
- 接続数の上限確認・変更方法
- デッドロック(ORA-00060)の検出と対処
V$SESSIONとは
V$SESSIONはOracleの動的パフォーマンスビュー(V$ビュー)の一つで、現在データベースに接続しているすべてのセッション情報を保持しています。SELECT権限さえあれば一般ユーザーでも参照できますが、全ユーザーのセッションを見るにはDBAロールまたはSELECT ANY DICTIONARYシステム権限が必要です。
主要な列は次のとおりです。
| 列名 | 説明 |
|---|---|
| SID | セッションID(KILLコマンドに使用) |
| SERIAL# | 同一SIDの再利用を区別するシリアル番号(KILLコマンドに使用) |
| USERNAME | ログインしているDBユーザー名 |
| STATUS | セッション状態(ACTIVE / INACTIVE / KILLED / CACHED / SNIPED) |
| OSUSER | OSユーザー名 |
| MACHINE | クライアントのマシン名 |
| PROGRAM | 接続に使用しているプログラム名 |
| SQL_ID | 現在実行中のSQL識別子 |
| WAIT_CLASS | 待機クラス(Idle / Application / Concurrency 等) |
| SECONDS_IN_WAIT | 現在の待機時間(秒) |
| LOGON_TIME | セッションのログイン時刻 |
| LAST_CALL_ET | 最後のSQL実行からの経過時間(秒) |
全セッション一覧を確認する
まず基本的なセッション一覧取得から始めます。
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.osuser,
s.machine,
s.program,
s.logon_time,
s.last_call_et AS last_call_sec
FROM
v$session s
ORDER BY
s.logon_time;
SYS や SYSTEMのバックグラウンドプロセスも含まれるため、ユーザー接続だけを確認したい場合は WHERE username IS NOT NULL を追加します。
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.machine,
s.program,
TO_CHAR(s.logon_time, 'YYYY-MM-DD HH24:MI:SS') AS logon_time,
s.last_call_et AS last_call_sec
FROM
v$session s
WHERE
s.username IS NOT NULL
ORDER BY
s.logon_time;
アクティブセッションだけを絞り込む
障害対応時は STATUS = ‘ACTIVE’ のセッションだけに絞ると情報が整理しやすくなります。ACTIVE はSQLを実行中(CPUを使っているか、何かを待っている)の状態を指します。
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.machine,
s.wait_class,
s.seconds_in_wait,
q.sql_text
FROM
v$session s
LEFT JOIN v$sql q ON s.sql_id = q.sql_id
AND s.sql_child_number = q.child_number
WHERE
s.status = 'ACTIVE'
AND s.username IS NOT NULL
ORDER BY
s.seconds_in_wait DESC;
V$SQLを結合することで、そのセッションが現在実行しているSQLテキストも合わせて確認できます。V$SQLの詳しい使い方は【Oracle】V$SQLビューを使用して直前に発行されたSQLを確認する方法を参照してください。
長時間実行中のセッションを特定する
バッチ処理が長時間止まっている場合、LAST_CALL_ET(最終SQL呼び出しからの経過秒数)を使ってフィルタリングします。
SELECT
s.sid,
s.serial#,
s.username,
s.status,
s.machine,
ROUND(s.last_call_et / 60, 1) AS elapsed_min,
s.wait_class,
s.event,
s.seconds_in_wait
FROM
v$session s
WHERE
s.username IS NOT NULL
AND s.last_call_et > 1800 -- 1800秒 = 30分
ORDER BY
s.last_call_et DESC;
LAST_CALL_ET は「最後のSQLが呼ばれてからの経過時間」であり、INACTIVEセッションの場合はSQLが終了してからのアイドル時間を示します。ACTIVEセッションの場合は現在のSQLが開始してからの実行時間を意味します。
ロック待ちセッションを特定する
ロック競合は最もよく遭遇するトラブルの一つです。段階的に調査する方法を解説します。
V$LOCK でロック状況の全体像を把握する
SELECT
l.sid,
l.type,
l.id1,
l.id2,
l.lmode, -- 保持しているロックモード(2=RS, 3=RX, 4=S, 5=SRX, 6=X)
l.request, -- 要求しているロックモード(0=なし、>0=待機中)
l.block, -- 1=他のセッションをブロックしている
l.ctime -- このロック状態になってからの秒数
FROM
v$lock l
WHERE
l.type IN ('TM', 'TX') -- TM=テーブルロック, TX=トランザクションロック
ORDER BY
l.block DESC, l.ctime DESC;
BLOCK = 1 のセッションが「加害者」(ロックを保持して他をブロックしている側)です。REQUEST > 0 のセッションが「被害者」(ロック待ちしている側)です。
DBA_BLOCKERS / DBA_WAITERS でブロック関係を可視化する
Oracle 10g以降はDBA_BLOCKERSとDBA_WAITERSビューを使うと、ブロック関係をシンプルに把握できます。
SELECT
s.sid,
s.serial#,
s.username,
s.machine,
s.program,
s.status,
s.last_call_et AS elapsed_sec
FROM
v$session s
WHERE
s.sid IN (SELECT blocking_session FROM v$session WHERE blocking_session IS NOT NULL);
SELECT
s.sid,
s.serial#,
s.username,
s.machine,
s.program,
s.blocking_session, -- このセッションをブロックしているSID
s.seconds_in_wait,
s.wait_class,
s.event
FROM
v$session s
WHERE
s.blocking_session IS NOT NULL;
どのオブジェクトがロックされているか確認する
ロックされているテーブル名を特定するには、V$LOCKED_OBJECTとDBA_OBJECTSを結合します。
SELECT
lo.session_id AS sid,
s.serial#,
s.username,
s.machine,
o.owner,
o.object_name,
o.object_type,
lo.locked_mode, -- 2=RS, 3=RX, 4=S, 5=SRX, 6=X
s.status
FROM
v$locked_object lo
JOIN v$session s ON lo.session_id = s.sid
JOIN dba_objects o ON lo.object_id = o.object_id
ORDER BY
o.object_name;
ブロック連鎖を一覧表示する(総合クエリ)
実務では「誰が誰をブロックし、どのテーブルをロックしているか」を一画面で把握したい場面が多くあります。次のクエリがそれを実現します。
SELECT
b.sid AS blocker_sid,
b.serial# AS blocker_serial,
b.username AS blocker_user,
b.machine AS blocker_machine,
b.program AS blocker_program,
b.last_call_et AS blocker_elapsed_sec,
w.sid AS waiter_sid,
w.serial# AS waiter_serial,
w.username AS waiter_user,
w.seconds_in_wait AS waiter_wait_sec,
o.owner || '.' || o.object_name AS locked_object
FROM
v$session w
JOIN v$session b ON w.blocking_session = b.sid
JOIN v$locked_object lo ON lo.session_id = b.sid
JOIN dba_objects o ON lo.object_id = o.object_id
ORDER BY
waiter_wait_sec DESC;
BLOCKER_SIDがロックを保持している「加害者」セッションです。WAITER_SIDが待ち続けている「被害者」セッションです。KILLするのはBLOCKER側であり、WAITER側をKILLしても根本解決になりません。
セッションを強制切断する(ALTER SYSTEM KILL SESSION)
ロックしているセッションが特定できたら、ALTER SYSTEM KILL SESSION で強制切断します。実行にはALTER SYSTEMシステム権限(DBAロール等)が必要です。
-- 構文: ALTER SYSTEM KILL SESSION 'SID,SERIAL#'; ALTER SYSTEM KILL SESSION '123,456';
SIDとSERIAL#はV$SESSIONから取得した値を使います。セッションが切断されると、そのセッションのトランザクションはロールバックされ、保持していたロックは解放されます。
KILL SESSION の動作とステータス遷移
KILL SESSION を実行しても、セッションがすぐに消えるとは限りません。状態は次のように遷移します。
| STATUSの値 | 説明 |
|---|---|
| ACTIVE | 通常実行中(またはKILL前) |
| KILLED | KILLコマンドを受け付けた。ロールバック中または次のSQL発行時に切断される |
| SNIPED | プロファイルの接続時間制限により切断対象になったがまだ残っている状態 |
STATUSが「KILLED」のまま長時間消えない場合は、大きなトランザクションのロールバックが進行中であることが多く、通常は待つのが正しい対応です。無理に操作するとデータ不整合の原因になります。
IMMEDIATE オプション(即時切断)
通常のKILL SESSIONはロールバックが完了してから切断しますが、IMMEDIATEオプションを付けると強制的に即時切断します。ただし、OSレベルのプロセスを強制終了するため、アーカイブログが大量に発生することがあり、本番環境では慎重に使用してください。
-- 通常(ロールバック後に切断) ALTER SYSTEM KILL SESSION '123,456'; -- 即時切断(ロールバック中でも強制終了) ALTER SYSTEM KILL SESSION '123,456' IMMEDIATE; -- DISCONNECT SESSION(ネットワーク接続も即時切断) ALTER SYSTEM DISCONNECT SESSION '123,456' IMMEDIATE;
IMMEDIATE は長時間のロールバックを待てない緊急時に使います。ただし Oracleプロセスが異常終了することがあり、その場合は PMON(プロセスモニター)が後処理を引き継ぎます。本番環境では影響を十分確認してから実行してください。
複数セッションをまとめてKILLするスクリプト生成
多数のセッションを一括切断したい場合、KILLコマンドを動的に生成すると便利です。
-- 特定ユーザーの全セッションに対するKILLコマンドを生成
-- CHR(39)は単一引用符(')を表す
SELECT
'ALTER SYSTEM KILL SESSION ' || CHR(39) || sid || ',' || serial# || CHR(39) || ' IMMEDIATE;'
AS kill_command
FROM
v$session
WHERE
username = 'APPUSER' -- KILLしたいユーザー名に変更
AND status != 'KILLED'; -- すでにKILL済みは除外
生成されたKILLコマンドを確認してから手動で実行します。WHERE句をカスタマイズすることで、特定のMACHINEからの接続や、一定時間以上アイドルのセッションなども絞り込めます。
接続数(セッション数)の確認と上限変更
「ORA-00018: セッション数の上限に達しました」エラーが出た場合、現在の接続数上限を確認して必要に応じて変更します。
現在の接続数と上限を確認する
-- 初期化パラメータの確認
SELECT name, value, description
FROM v$parameter
WHERE name IN ('processes', 'sessions', 'transactions');
-- 現在の接続セッション数(ユーザーセッションのみ)
SELECT COUNT(*) AS current_sessions
FROM v$session
WHERE username IS NOT NULL;
-- ステータス別の内訳
SELECT status, COUNT(*) AS cnt
FROM v$session
WHERE username IS NOT NULL
GROUP BY status
ORDER BY cnt DESC;
PROCESSES はOSプロセス数の上限で、SESSIONS は論理セッション数の上限です。通常 SESSIONS = PROCESSES * 1.1 + 5 として自動計算されますが、明示的に設定することもできます。
接続上限を変更する(SPFILEが必要)
-- 現在の値を確認 SHOW PARAMETER processes; SHOW PARAMETER sessions; -- SPFILEを使って変更(再起動後に有効) ALTER SYSTEM SET processes = 500 SCOPE = SPFILE; ALTER SYSTEM SET sessions = 555 SCOPE = SPFILE; -- 変更後は再起動が必要 SHUTDOWN IMMEDIATE; STARTUP; -- 反映確認 SHOW PARAMETER processes; SHOW PARAMETER sessions;
PROCESSESとSESSIONSは静的パラメータのため、SCOPE=SPFILEを指定し、データベースを再起動しないと反映されません。本番環境では計画メンテナンスウィンドウで実施してください。
アイドルセッションを検出する
長時間アイドルのセッションが大量に残ると接続枠を圧迫します。定期的に確認してKILLするか、プロファイルでIDLE_TIME(接続時間制限)を設定することを検討します。
SELECT
s.sid,
s.serial#,
s.username,
s.machine,
s.program,
ROUND(s.last_call_et / 3600, 1) AS idle_hours,
TO_CHAR(s.logon_time, 'YYYY-MM-DD HH24:MI:SS') AS logon_time
FROM
v$session s
WHERE
s.username IS NOT NULL
AND s.status = 'INACTIVE'
AND s.last_call_et > 3600 -- 3600秒 = 1時間以上アイドル
ORDER BY
s.last_call_et DESC;
デッドロック(ORA-00060)の検出と対処
2つのセッションがお互いのロックを待つ「デッドロック」が発生すると、Oracleは自動的に一方のセッションにORA-00060エラーを返して解消します。ただし、エラーを受けたセッションのトランザクションは自動的にはロールバックされないため、アプリ側でのハンドリングが必要です。
デッドロックの発生をアラートログで確認する
-- アラートログのパスを確認 SELECT value FROM v$parameter WHERE name = 'background_dump_dest'; -- または診断ディレクトリから確認 SELECT value FROM v$diag_info WHERE name = 'Diag Trace';
アラートログ(alert_<SID>.log)に「ORA-00060: deadlock detected」というメッセージとトレースファイル名が記録されます。トレースファイルを確認することで、どのセッションがどのオブジェクトに対してデッドロックを起こしたかを特定できます。
デッドロックが発生しやすいパターンと対策
-- セッションA セッションB
-- ─────────────────── ────────────────────
UPDATE tbl SET col=1 WHERE id=1; UPDATE tbl SET col=2 WHERE id=2;
UPDATE tbl SET col=2 WHERE id=1; -- Aが保持するidのロックを待つ
UPDATE tbl SET col=1 WHERE id=2; -- Bが保持するidのロックを待つ → デッドロック
デッドロック対策の基本は「複数テーブル・複数行を更新する場合は常に同じ順序でアクセスする」ことです。また、トランザクションをできるだけ短くし、必要な行だけを最初にロック(SELECT FOR UPDATE)してから処理を進める設計も有効です。
よくある質問と対処
ALTER SYSTEM KILL SESSIONで「ORA-27143」が発生する
OSプロセスがすでに存在しない(クライアントとの接続が物理的に切れているが、サーバー側のセッション情報が残っている)場合に発生します。IMMEDIATE オプションを付けて再度実行するか、数分待ってから再確認してください。
KILL後もSTATUS=KILLEDが続く
トランザクションのロールバック中です。変更量が大きいほど時間がかかります。V$SESSION から消えるまで待つのが原則です。ただし、OracleのPMONプロセスがロールバックを代行しているため、手動介入は不要です。
自分のセッションのSIDを確認する
SELECT sid, serial#, username, status
FROM v$mystat
WHERE statistic# = 0;
-- または
SELECT sys_context('USERENV', 'SID') AS my_sid FROM dual;
別のセッションが保持するトランザクションの内容を確認する
SELECT
t.addr,
t.xidusn,
t.xidslot,
t.xidsqn,
t.status,
t.used_ublk * 8192 / 1024 AS undo_kb,
t.used_urec AS undo_records,
s.sid,
s.serial#,
s.username,
ROUND(t.start_date - SYSDATE, 4) AS start_offset_day
FROM
v$transaction t
JOIN v$session s ON t.addr = s.taddr
ORDER BY
undo_kb DESC;
UNDO_KBが大きいほどロールバックに時間がかかります。遅いSQLのさらに詳しい特定方法は【Oracle】遅いSQLを特定する方法|V$SQLやAWRレポートの活用法も参考にしてください。
セッション管理の実務チェックリスト
| 状況 | 使うビュー・コマンド |
|---|---|
| セッション全体の把握 | V$SESSION |
| 実行中SQLを確認したい | V$SESSION + V$SQL(SQL_ID結合) |
| ロックの全体像確認 | V$LOCK(BLOCK=1 がブロッカー) |
| ブロッカー特定 | V$SESSION.BLOCKING_SESSION カラム |
| ロックされているテーブル名 | V$LOCKED_OBJECT + DBA_OBJECTS |
| セッション強制切断 | ALTER SYSTEM KILL SESSION ‘SID,SERIAL#’ |
| 即時切断 | ALTER SYSTEM KILL SESSION ‘…’ IMMEDIATE |
| 接続数上限確認 | V$PARAMETER(processes / sessions) |
| デッドロック発生確認 | アラートログ(alert_SID.log) |
まとめ
Oracleのセッション管理はV$SESSIONを起点に、V$LOCK・V$LOCKED_OBJECT・DBA_OBJECTSといったビューを組み合わせることで体系的に調査できます。
- ブロッカーの特定には V$SESSION.BLOCKING_SESSION カラムが最も手早い
- ロックされているオブジェクトはV$LOCKED_OBJECTとDBA_OBJECTSの結合で特定する
- 強制切断にはALTER SYSTEM KILL SESSIONを使い、緊急時のみIMMEDIATEを付ける
- KILL後のKILLED状態はロールバック中なので、完了を待つのが原則
- 接続数上限の変更はPROCESSESパラメータをSPFILEで変更して再起動が必要
ユーザー権限の確認方法については【Oracle】ユーザ権限を確認する方法完全ガイド、データベースへの接続ができない場合の原因調査は【Oracle】ORA-12560: TNS プロトコル・アダプタ・エラーの原因と解決方法も合わせて参照してください。

