DBMS_ALERT と DBMS_PIPE は、Oracle Database内のセッション間で通知やメッセージをやり取りするためのPL/SQL標準パッケージです。どちらも外部メッセージブローカーを用意せずに使えますが、性質はかなり違います。
DBMS_ALERT は「イベントが起きたこと」を待機セッションへ通知する仕組みです。一方、DBMS_PIPE は名前付きパイプへメッセージを詰めて送受信する仕組みです。ただし、どちらも永続キューではありません。重要なイベント履歴、再実行保証、障害後の復旧が必要な処理では、テーブル、DBMS_SCHEDULER、Advanced Queuingなどと組み合わせて設計します。全体像は DBMS_PIPE・DBMS_ALERT完全ガイド も参考にしてください。
DBMS_ALERTとDBMS_PIPEの違いSIGNAL/WAITONEの基本PACK_MESSAGE/SEND_MESSAGE/RECEIVE_MESSAGEの基本- コミット連動、タイムアウト、最新メッセージだけになる注意点
- テーブルとALERTを組み合わせる実務パターン
- PIPE名、private pipe、クリーンアップの注意点
DBMS_ALERTとDBMS_PIPEの違い
まず、両者の使い分けを押さえます。DBMS_ALERT は通知のブロードキャストに近く、登録して待っているセッションへ「何かが起きた」と知らせます。DBMS_PIPE は名前付きパイプへメッセージを送り、受信側がそれを取り出す形です。
DBMS_ALERTの基本形
DBMS_ALERT では、受信側が REGISTER でアラート名を登録し、WAITONE や WAITANY で通知を待ちます。送信側は SIGNAL を呼び出します。
DECLARE
v_message VARCHAR2(1800);
v_status PLS_INTEGER;
BEGIN
DBMS_ALERT.REGISTER('ORDER_CHANGED');
DBMS_ALERT.WAITONE(
name => 'ORDER_CHANGED',
message => v_message,
status => v_status,
timeout => 30
);
IF v_status = 0 THEN
DBMS_OUTPUT.PUT_LINE('message=' || v_message);
ELSE
DBMS_OUTPUT.PUT_LINE('timeout or no alert');
END IF;
DBMS_ALERT.REMOVE('ORDER_CHANGED');
END;
/
WAITONE の timeout は、待ち続ける時間を秒で指定します。本番処理では無限待ちにせず、タイムアウトしたらログを出す、再試行する、終了する、といった動きを決めておきます。
SIGNALはコミット後に通知される
DBMS_ALERT.SIGNAL の重要な特徴は、通知がトランザクションのコミットと連動することです。送信側が SIGNAL を呼んでも、コミットされるまでは待機側へ通知されません。ロールバックすれば通知も無効になります。
BEGIN
UPDATE orders
SET status = 'READY'
WHERE order_id = 1001;
DBMS_ALERT.SIGNAL(
name => 'ORDER_CHANGED',
message => 'order_id=1001'
);
COMMIT; -- この時点で待機側に通知される
END;
/
WAITONE / WAITANY で待機するセッションは、未コミットの作業を抱えたまま待たない設計にしてください。待機処理は専用セッションや専用ジョブに分けるほうが安全です。DBMS_ALERTはキューではない
DBMS_ALERT はイベント通知であり、メッセージキューではありません。同じアラート名に対して複数回 SIGNAL された場合、待機側が過去の通知をすべて順番に受け取れるとは考えないほうが安全です。重要な処理対象は、必ずテーブルに保存し、ALERTは「テーブルを見に行くきっかけ」として使います。
-- 重要なイベントはテーブルに保存する
INSERT INTO app_event_queue (
event_id,
event_type,
payload,
status,
created_at
) VALUES (
app_event_seq.NEXTVAL,
'ORDER_CHANGED',
'order_id=1001',
'READY',
SYSTIMESTAMP
);
DBMS_ALERT.SIGNAL('APP_EVENT_READY', 'ORDER_CHANGED');
COMMIT;
待機側はALERTを受けたら、メッセージ本文だけで処理を決めるのではなく、テーブルから READY のイベントを取得して処理します。これにより、通知漏れやセッション停止があっても、未処理データを後から回収できます。
待機側ワーカーの実装例
以下は、ALERTを受けたらイベントテーブルを処理するワーカーの簡略例です。本番ではロック、リトライ回数、エラー記録、終了条件をもう少し丁寧に設計します。
DECLARE
v_message VARCHAR2(1800);
v_status PLS_INTEGER;
BEGIN
DBMS_ALERT.REGISTER('APP_EVENT_READY');
LOOP
DBMS_ALERT.WAITONE('APP_EVENT_READY', v_message, v_status, 60);
IF v_status = 0 THEN
FOR r IN (
SELECT event_id, event_type, payload
FROM app_event_queue
WHERE status = 'READY'
ORDER BY event_id
) LOOP
UPDATE app_event_queue
SET status = 'DONE',
processed_at = SYSTIMESTAMP
WHERE event_id = r.event_id
AND status = 'READY';
END LOOP;
COMMIT;
ELSE
DBMS_OUTPUT.PUT_LINE('wait timeout');
END IF;
END LOOP;
END;
/
複数ワーカーで処理する場合は、二重処理を避けるための排他制御が必要です。Oracleのユーザー定義ロックは DBMS_LOCK完全ガイド、PL/SQLでの排他制御実装は DBMS_LOCKを使った排他制御 も参考になります。
DBMS_PIPEの基本形
DBMS_PIPE は、名前付きパイプへメッセージを送受信するパッケージです。送信側は RESET_BUFFER で送信バッファを初期化し、PACK_MESSAGE で値を詰め、SEND_MESSAGE でパイプへ送ります。受信側は RECEIVE_MESSAGE の後に UNPACK_MESSAGE で取り出します。
DECLARE
v_status PLS_INTEGER;
BEGIN
DBMS_PIPE.RESET_BUFFER;
DBMS_PIPE.PACK_MESSAGE('REFRESH_CACHE');
DBMS_PIPE.PACK_MESSAGE('product');
DBMS_PIPE.PACK_MESSAGE(1001);
v_status := DBMS_PIPE.SEND_MESSAGE(
pipename => 'APP_WORKER_PIPE',
timeout => 10
);
IF v_status <> 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'pipe send failed: ' || v_status);
END IF;
END;
/
DECLARE
v_status PLS_INTEGER;
v_command VARCHAR2(100);
v_target VARCHAR2(100);
v_id NUMBER;
BEGIN
v_status := DBMS_PIPE.RECEIVE_MESSAGE(
pipename => 'APP_WORKER_PIPE',
timeout => 30
);
IF v_status = 0 THEN
DBMS_PIPE.UNPACK_MESSAGE(v_command);
DBMS_PIPE.UNPACK_MESSAGE(v_target);
DBMS_PIPE.UNPACK_MESSAGE(v_id);
DBMS_OUTPUT.PUT_LINE(v_command || ':' || v_target || ':' || v_id);
ELSE
DBMS_OUTPUT.PUT_LINE('timeout or receive error: ' || v_status);
END IF;
END;
/
private pipeと名前衝突に注意する
DBMS_PIPE のパイプ名は設計上のインターフェースになります。単純な名前にすると他処理と衝突したり、意図しない送受信が起きたりします。用途、アプリ名、環境名、所有者を含めた名前にし、必要に応じてprivate pipeを使います。
DECLARE
v_status PLS_INTEGER;
BEGIN
v_status := DBMS_PIPE.CREATE_PIPE(
pipename => 'APP1.PROD.WORKER_CMD',
maxpipesize => 8192,
private => TRUE
);
IF v_status <> 0 THEN
RAISE_APPLICATION_ERROR(-20002, 'create pipe failed: ' || v_status);
END IF;
END;
/
不要になった明示的なパイプは REMOVE_PIPE で削除します。一時的な通知用途でも、名前を決めずに乱立させると運用時に追いづらくなります。
DECLARE
v_status PLS_INTEGER;
BEGIN
v_status := DBMS_PIPE.REMOVE_PIPE('APP1.PROD.WORKER_CMD');
DBMS_OUTPUT.PUT_LINE('remove status=' || v_status);
END;
/
DBMS_PIPEも永続キューではない
DBMS_PIPE のメッセージは、データベース内の一時的な通信バッファとして扱うべきです。ジョブの指示、重要な業務イベント、再実行が必要な処理をPIPEだけに載せると、受信側停止やインスタンス停止時に追跡できなくなります。確実に処理したい内容はテーブルに保存し、PIPEは起動合図や軽いコマンド通知として使います。
DBMS_ALERTとDBMS_PIPEの使い分け
どちらを使うかは、送るものが「イベント通知」なのか「メッセージ」なのかで考えます。
ジョブ運用や実行履歴まで含めたい場合は DBMS_SCHEDULER完全ガイド や ジョブ実行履歴の集中管理と異常検知 が近いテーマです。
タイムアウトと終了条件を必ず入れる
待機処理では、WAITONE や RECEIVE_MESSAGE にタイムアウトを入れます。無限待ちにすると、終了指示、メンテナンス、障害時の停止が難しくなります。運用しやすいワーカーは、定期的に制御テーブルを見て終了指示や設定変更を確認します。
DECLARE
v_message VARCHAR2(1800);
v_status PLS_INTEGER;
v_stop NUMBER;
BEGIN
DBMS_ALERT.REGISTER('APP_EVENT_READY');
LOOP
SELECT COUNT(*)
INTO v_stop
FROM app_control
WHERE control_name = 'WORKER_STOP'
AND control_value = 'Y';
EXIT WHEN v_stop > 0;
DBMS_ALERT.WAITONE('APP_EVENT_READY', v_message, v_status, 30);
IF v_status = 0 THEN
DBMS_OUTPUT.PUT_LINE('received: ' || v_message);
END IF;
END LOOP;
DBMS_ALERT.REMOVE('APP_EVENT_READY');
END;
/
トリガーから通知する場合の注意
テーブル変更をきっかけに通知したい場合、トリガーから DBMS_ALERT.SIGNAL を呼ぶ設計があります。ただし、行トリガーで毎行SIGNALすると通知が過剰になりやすく、更新件数が多い処理では負荷やログ量が増えます。多くの場合、処理対象をテーブルへ記録し、文単位またはバッチ単位で通知するほうが扱いやすいです。
CREATE OR REPLACE TRIGGER orders_notify_trg
AFTER INSERT OR UPDATE ON orders
BEGIN
DBMS_ALERT.SIGNAL(
name => 'ORDER_CHANGED',
message => 'orders changed'
);
END;
/
トリガーが無効になるとDML自体に影響するため、監視とコンパイル確認も必要です。トリガー無効化の典型例は ORA-04098の原因と解決方法 にまとめています。
運用で確認したいログ
通知やパイプ通信は、動いている間は見えづらい処理になりがちです。いつ送信したか、受信したか、処理したか、タイムアウトしたかをアプリケーションログや管理テーブルに残します。
CREATE TABLE app_ipc_log (
log_id NUMBER GENERATED BY DEFAULT AS IDENTITY,
channel VARCHAR2(100),
direction VARCHAR2(10),
status_code NUMBER,
message VARCHAR2(1800),
log_time TIMESTAMP DEFAULT SYSTIMESTAMP,
CONSTRAINT pk_app_ipc_log PRIMARY KEY (log_id)
);
INSERT INTO app_ipc_log(channel, direction, status_code, message)
VALUES ('APP_EVENT_READY', 'RECEIVE', v_status, v_message);
PL/SQLのロギング設計は、単にエラーを出すだけではなく、再実行判断や障害調査に使える粒度で残すことが重要です。関連する監査・ロギング設計は ジョブ実行履歴の集中管理と異常検知 も参考になります。
よくある質問
まとめ
DBMS_ALERT はコミット連動のイベント通知、DBMS_PIPE は名前付きパイプによる簡易メッセージ通信です。どちらもセッション間通信には便利ですが、永続キューではありません。重要なイベントや再実行が必要な処理はテーブルに保存し、ALERTやPIPEは処理開始の合図や軽いコマンド通知に限定するのが安全です。
実務では、タイムアウト、終了条件、ログ、二重起動防止、クリーンアップまで含めて設計します。単純な通知ならDBMS_ALERT、値を渡す簡易通信ならDBMS_PIPE、履歴やリトライを含めるならDBMS_SCHEDULERや永続キューを検討しましょう。
参考: Oracle Database PL/SQL Packages and Types Reference – DBMS_ALERT / Oracle Database PL/SQL Packages and Types Reference – DBMS_PIPE

