Oracle のトランザクションを特定して安全に KILL する手順まとめ
本番環境でアプリが固まったり、「待機セッションが大量発生している」といったとき、
原因になっているトランザクション(セッション)を特定して KILL したくなる場面があります。
しかし、やみくもに KILL するとアプリ側でエラーが多発したり、最悪データ不整合の原因になります。
この記事では、Oracle Databaseで
- 問題となっているトランザクション/セッションを特定する
- 影響を確認したうえで
- セッションを適切に KILL する
という一連の流れを、実務で使える SQL 付きでまとめます。
前提
-
対象:Oracle Database
-
必要権限:
-
SELECT_CATALOG_ROLEもしくは同等の権限(V$SESSION,V$TRANSACTION,V$LOCK参照) - セッション KILL 用に
ALTER SYSTEM権限
-
-
実行環境:
SQL*Plus,SQL Developer,sqlclなど
⚠️ 注意
- 本番環境で実行する場合は、必ずアプリ担当者・運用担当者と連携したうえで実施してください。
- 「KILL=最終手段」という前提で、まずは原因調査を優先しましょう。
全体の流れ
この記事で紹介する流れは以下のとおりです。
- 問題の症状を確認する(アプリ/監視側)
- 長時間トランザクションやロックを持っているセッションを洗い出す
- ブロッキングセッション(ロックの元凶)を特定する
- 実行中の SQL・ログインユーザ・アプリ名を確認する
- 影響範囲を確認したうえで KILL する
- ロックが解放されたか確認する
1. 長時間トランザクションを持っているセッションを探す
まずは、長時間コミットされていないトランザクションを持っているセッションを洗い出します。
-- 長時間トランザクションを持っているセッション一覧
-- used_ublk / used_urec が大きいほど、UNDO をたくさん使用している
SELECT
s.sid,
s.serial#,
s.username,
s.osuser,
s.machine,
s.program,
s.status,
t.xidusn,
t.xidslot,
t.xidsqn,
t.start_time,
t.used_ublk,
t.used_urec,
s.logon_time
FROM
v$transaction t
JOIN v$session s ON t.ses_addr = s.saddr
ORDER BY
t.start_time;
ポイント:
-
v$transactionに載っている = 現在進行中のトランザクションがある - 特に
used_ublk/used_urecが大きいもの、start_timeが古いものは要注意 -
s.programやs.machineから、どのアプリ・どのサーバかを把握できます
2. ロック待ちになっているセッションを洗い出す
アプリ側で「固まっている」「タイムアウトが頻発」といった場合、
ロック待ち(誰かがロックを持っていて進めない状態) が発生していることがよくあります。
-- ロック待ちセッション(WAIT)をざっくり確認
SELECT
s.sid,
s.serial#,
s.username,
s.osuser,
s.machine,
s.program,
s.status,
s.event,
s.wait_class,
s.seconds_in_wait
FROM
v$session s
WHERE
s.wait_class <> 'Idle'
ORDER BY
s.seconds_in_wait DESC;
event や wait_class に注目します。
- 例:
enq: TX - row lock contentionなどが出ていれば、行ロック競合の可能性大
3. ブロッキングセッション(ロックの元凶)を特定する
次に、他のセッションをブロックしている “犯人” セッション を特定します。
-- ブロックしている側 / ブロックされている側の対応を取得
SELECT
s1.sid AS blocker_sid,
s1.serial# AS blocker_serial,
s1.username AS blocker_user,
s1.machine AS blocker_machine,
s1.program AS blocker_program,
s2.sid AS blocked_sid,
s2.serial# AS blocked_serial,
s2.username AS blocked_user,
s2.machine AS blocked_machine,
s2.program AS blocked_program,
l1.id1,
l1.id2,
l1.lmode,
l2.request
FROM
v$lock l1
JOIN v$session s1 ON l1.sid = s1.sid
JOIN v$lock l2 ON l1.id1 = l2.id1 AND l1.id2 = l2.id2
JOIN v$session s2 ON l2.sid = s2.sid
WHERE
l1.block = 1 -- ブロックしている側
AND l2.request > 0; -- ブロックされている側
-
blocker_*が ロックの元凶 となっているセッション -
blocked_*が 待たされている側 のセッション
通常は、blocker の方を優先して調査 / KILL 候補にすることが多いです。
4. 実行中の SQL を確認する
候補となるセッションが絞れたら、実際にどんな SQL を実行しているか を確認します。
-- 特定の SID の実行中 SQL を確認
-- :sid には確認したいセッション ID を指定
SELECT
s.sid,
s.serial#,
q.sql_id,
q.sql_text
FROM
v$session s
JOIN v$sql q ON s.sql_id = q.sql_id
WHERE
s.sid = :sid;
ポイント:
-
重要なのは「本当に KILL していい処理か?」を判断すること
-
例えば、
- 大量データの一括更新バッチの途中
- 決済・売上計上など、ビジネス的に重要なトランザクション
などは、関係者と相談せずに KILL しない方が安全です。
5. セッションを KILL する(ALTER SYSTEM KILL SESSION)
調査の結果、KILL してもよいと判断できたセッション に対してのみ、
以下のように ALTER SYSTEM KILL SESSION を実行します。
-- 基本形
ALTER SYSTEM KILL SESSION 'SID,SERIAL#';
-- 例: SID = 123, SERIAL# = 45678 の場合
ALTER SYSTEM KILL SESSION '123,45678';
-- すぐに切りたい場合(IMMEDIATE オプション)
ALTER SYSTEM KILL SESSION '123,45678' IMMEDIATE;
IMMEDIATE あり/なしの違い
-
なし:
- セッションは「KILLED」状態になり、PMON プロセスがクリーンアップを行う
- 状況によってはしばらく残り続けることがある
-
あり(
IMMEDIATE):- 強制的に切断
- ロールバック処理も並行して行われるが、アプリ側には即エラーが返る
実務では、多くの場合
IMMEDIATEを付けて KILL するケースが多いです。
ただし、アプリ側への影響(例外発生)を前提にしたうえで使用しましょう。
6. OS レベルで KILL するケース(参考)
まれに ALTER SYSTEM KILL SESSION でもなかなか消えないセッションがあります(SNIPED や KILLED 状態のまま長時間残る等)。
その場合、以下のように v$session の paddr → v$process の spid から OS のプロセス ID を取得し、
OS コマンド(kill -9 など)で強制終了するケースもあります。
-- OS プロセス ID (spid) を取得
SELECT
s.sid,
s.serial#,
p.spid
FROM
v$session s
JOIN v$process p ON s.paddr = p.addr
WHERE
s.sid = :sid;
⚠️ OS レベル KILL はかなり強い手段なので、
- 本番では DBA チームの運用ルールに従う
- 勝手にやらない
ことを強くおすすめします。
7. KILL 後に確認しておくポイント
セッションを KILL したあと、以下を確認すると安心です。
7.1 ロックが解放されたか?
-- まだロック待ちが残っていないかざっくり確認
SELECT
s.sid,
s.serial#,
s.username,
s.event,
s.wait_class,
s.state,
s.seconds_in_wait
FROM
v$session s
WHERE
s.wait_class <> 'Idle'
ORDER BY
s.seconds_in_wait DESC;
ブロックされていたセッションが再度動き出しているか、アプリ側の動作も併せて確認します。
7.2 ログ・監視の記録
- いつ
- 誰が
- どの SID / SERIAL# を
- なぜ KILL したのか
を運用ログ(Redmine / JIRA / Slack / Wiki など)に残しておくと、
後からトラブルになったときに原因追跡しやすくなります。
8. セッション KILL の運用ポリシー例
実務で混乱しないよう、あらかじめチーム内でルールを決めておくのがおすすめです。
例:
-
影響度がわからないセッションは 即 KILL しない
-
SYSユーザ / バックグラウンドプロセスは原則 KILL しない -
以下の条件を満たす場合のみ KILL してよい
- ロック待ちセッションが多数発生している
- ブロッキングセッションの処理内容が明確で、再実行可能
- 関係者(アプリ担当, 運用責任者)が KILL を承認済み
-
KILL 実施後は、必ず
- アプリの動作確認
- ログの記録
を行う
まとめ
Oracle でトランザクションを KILL する手順は、ざっくり言うと以下の流れになります。
-
v$transaction/v$sessionで長時間トランザクションを確認 -
v$lockでブロッキングセッションを特定 -
v$sqlで実行中 SQL・アプリ情報を確認 - 影響を理解したうえで
ALTER SYSTEM KILL SESSION 'SID,SERIAL#' IMMEDIATE; - ロック解放・アプリ動作を確認し、運用ログに記録
「何となく怪しいから KILL」ではなく、
「どのセッションが」「どんなトランザクションで」「誰をブロックしていて」「切るとどうなるか」
を理解したうえで、落ち着いて対処することが重要です。
おまけ:よく使う SQL 一覧(コピペ用)
-- 1. 長時間トランザクション一覧
SELECT
s.sid,
s.serial#,
s.username,
s.osuser,
s.machine,
s.program,
s.status,
t.xidusn,
t.xidslot,
t.xidsqn,
t.start_time,
t.used_ublk,
t.used_urec,
s.logon_time
FROM
v$transaction t
JOIN v$session s ON t.ses_addr = s.saddr
ORDER BY
t.start_time;
-- 2. ロック待ちセッション(WAIT)
SELECT
s.sid,
s.serial#,
s.username,
s.osuser,
s.machine,
s.program,
s.status,
s.event,
s.wait_class,
s.seconds_in_wait
FROM
v$session s
WHERE
s.wait_class <> 'Idle'
ORDER BY
s.seconds_in_wait DESC;
-- 3. ブロッキングセッション特定
SELECT
s1.sid AS blocker_sid,
s1.serial# AS blocker_serial,
s1.username AS blocker_user,
s1.machine AS blocker_machine,
s1.program AS blocker_program,
s2.sid AS blocked_sid,
s2.serial# AS blocked_serial,
s2.username AS blocked_user,
s2.machine AS blocked_machine,
s2.program AS blocked_program,
l1.id1,
l1.id2,
l1.lmode,
l2.request
FROM
v$lock l1
JOIN v$session s1 ON l1.sid = s1.sid
JOIN v$lock l2 ON l1.id1 = l2.id1 AND l1.id2 = l2.id2
JOIN v$session s2 ON l2.sid = s2.sid
WHERE
l1.block = 1
AND l2.request > 0;
-- 4. 特定セッションの実行中 SQL を見る
SELECT
s.sid,
s.serial#,
q.sql_id,
q.sql_text
FROM
v$session s
JOIN v$sql q ON s.sql_id = q.sql_id
WHERE
s.sid = :sid;
-- 5. セッション KILL
ALTER SYSTEM KILL SESSION '123,45678' IMMEDIATE;