ヘッダーをスキップ

Oracle Database アプリケーション開発者ガイド-基礎編
10gリリース2(10.2)

B19248-02
目次
目次
索引
索引

戻る 次へ

13 データベース変更通知を使用したアプリケーション開発

この項の内容は次のとおりです。

データベース変更通知の概要

データベース変更通知とは、クライアント・アプリケーションで問合せをデータベースに登録し、その問合せに関連付けられたオブジェクトのDML変更またはDDL変更に応答して通知を受信できるようにする機能です。通知は、DMLまたはDDLトランザクションのコミット時にデータベースから公開されます。

登録時に、アプリケーションでは通知ハンドラを指定して、重要な問合せのセットを通知ハンドラに関連付けます。通知ハンドラには、サーバー側PL/SQLプロシージャまたはクライアント側Cコールバックを使用できます。登録は、問合せの実行中に参照されるオブジェクトすべてに作成されます。通知ハンドラは、以降にトランザクションにより登録済オブジェクトのいずれかが変更され、コミットされる時点で起動されます。

アプリケーションが、HR.EMPLOYEES表の問合せに対する結果セットの変更について通知を受け取る必要があるとします。 アプリケーションでは、変更通知機能を使用してhr.employees表の問合せをデータベースに登録できます。ユーザーが従業員を追加する場合、アプリケーションでは表に新規行が追加されるときにデータベース変更通知を受信できます。hr.employeesの新規問合せでは、変更後の結果セットが戻されます。

データベースでは、変更通知の発行時に次の一部またはすべての情報を含めることができます。

通知には、変更があったデータ自体ではなく、変更があった行またはオブジェクトのメタデータのみが含まれます。たとえば、5000から6000への月給の増額があっても、データベースからクライアントには通知されません。変更があったオブジェクトまたは行の最新の値を取得するには、クライアントは通知に含まれていた情報に基づいてデータベースを問い合せる必要があります。

データベース変更通知が有用なのは、データベースへのネットワーク・ラウンドトリップを回避するために、中間層にあってほとんど読取り専用のオブジェクトに対する問合せ結果セットをキャッシュするアプリケーションの場合です。この種のアプリケーションでは、変更通知サービスを使用して、キャッシュ内の重要な問合せについて登録を作成できます。登録した問合せ内で参照されるオブジェクトに変更があると、基礎となるトランザクションのコミット時にデータベースから変更通知が公開されます。この通知に応答して、アプリケーションでは問合せを再実行してキャッシュをリフレッシュできます。

たとえば、Webフォーラム・アプリケーションのユーザーは、バックエンド・データベースに挿入された新規のコンテンツを挿入直後に参照しなくてもよい場合があります。この種のアプリケーションの場合、本質的にデータが少し古いものであっても問題はないため、中間層でキャッシュするとメリットが得られます。このような使用例では、データベース変更通知を使用するとキャッシュをバックエンド・データベースで常に更新できます。

中間層におけるデータベース変更通知の使用

データベース変更通知は多数の開発コンテキストに関係しますが、特に役立つのはキャッシュ内のデータに依存する中間層アプリケーションの場合です。図13-1に、中間層でキャッシュされたデータがバックエンドのOracle Databaseで処理されてから、インターネットを介してアクセスされる標準的な使用例を示します。

図 13-1    中間層キャッシュの例


画像の説明

中間層のアプリケーションは、キャッシュをデータベースと相対でできるかぎり最新の状態に保ちつつ、データベース・オブジェクトのキャッシュ・コピーに迅速にアクセスする必要があります。キャッシュ・データは、トランザクションにより変更されてコミットされると古くなるか失効するため、アプリケーションは不正な結果にアクセスする危険性があります。アプリケーションがデータベース変更通知を使用している場合は、登録済オブジェクトに変更が発生した時点でOracle Databaseは変更内容の詳細を使用して通知を公開できます。この通知に応答して、アプリケーションではキャッシュ・データをバックエンド・データベースからフェッチしてリフレッシュできます。

図13-2に、中間層のWebクライアントが変更を受信して通知を処理できるプロセスを示します。

図 13-2    データベース変更通知の基本プロセス


画像の説明

図13-2の各ステップの内容は次のとおりです。

  1. この例では、アプリケーションがHR.EMPLOYEESの問合せの結果セットをキャッシュしているものとします。開発者は、変更通知PL/SQLインタフェースを使用してHR.EMPLOYEESの問合せに関する登録を作成します。また、通知を処理するPL/SQLストアド・プロシージャを作成し、サーバー側PL/SQLプロシージャを通知ハンドラとして提供します。

  2. データベースでは、データ辞書に登録情報が移入されます。

  3. ユーザーが登録済オブジェクトの1つをDML文で変更し、トランザクションをコミットします。たとえば、ユーザーがバックエンド・データベース上でhr.employees表の1行を更新します。中間層でキャッシュされていたhr.employeesのデータは失効になります。

  4. Oracle Databaseにより、内部キューに変更を説明するメッセージが追加されます。

  5. JOBQバックグラウンド・プロセスが、新規の変更通知メッセージに関する通知を受け取ります。

  6. JOBQプロセスにより、クライアント・アプリケーションから指定されたストアド・プロシージャが実行されます。この例では、JOBQからサーバー側PL/SQLプロシージャにデータが渡されます。PL/SQLコールバック・プロシージャの実装により、通知の処理方法が決まります。

  7. 開発者は、サーバー側PL/SQLプロシージャ内に、登録済オブジェクトに対する変更を中間層のクライアント・アプリケーションに通知するための論理を実装できます。たとえば、hr.employees内で変更された行のROWIDをアプリケーションに通知します。

  8. 中間層のクライアント・アプリケーションでは、バックエンド・データベースを問い合せて、変更があった行のデータを取得します。

  9. クライアント・アプリケーションにより、キャッシュが新規データで更新されます。


    注意

    前述のステップを適用できるのは、PL/SQLを介して作成された登録の場合です。OCIインタフェースを介して作成された登録の場合、アプリケーションではOCISubscriptionRegisterインタフェースを使用して登録を作成し、クライアント側Cコールバックを通知ハンドラとして指定します。登録後は、処理中にクライアント側プログラムでイベント・スレッドが起動されます。トランザクションにより登録済オブジェクトのいずれかが変更され、コミットされると、RDBMSのEMONプロセスからイベント・スレッドに通知が送信されます。これにより、アプリケーションで指定されたCコールバックが、イベント・スレッドのコンテキストで実行されます。 


データベース変更通知に対する問合せの登録

この項の内容は次のとおりです。

権限

ユーザーが変更通知に対する登録を作成するには、CHANGE NOTIFICATIONシステム権限が必要です。また、登録するすべてのオブジェクトに対するSELECT権限も必要です。登録の作成時にはオブジェクトに対するSELECT権限が付与されていても、それ以降に(取消しにより)権限を失った場合は、登録がパージされ、それを反映した通知が公開されることに注意してください。

データベース変更登録の概要

データベース変更登録は、概念上(受信者R、問合せリストQL)の1組とみなすことができます。問合せリストに含まれる問合せで参照されるオブジェクトのいずれかに変更が発生すると、受信者が通知を受信します。受信者には、サーバー側PL/SQLストアド・プロシージャまたはクライアント側Cコールバックを使用できます。作成された登録は、Oracleデータベースに永続エンティティとして格納されます。このエンティティは、Oracle Real Application Clustersのすべてのインスタンスが参照できます。トランザクションによりクラスタのいずれかのインスタンス内で登録済オブジェクトが変更されると、通知が生成されます。作成された登録は、クライアント・アプリケーションが明示的に登録を解除するか、タイムアウトになるか、またはデータベースがなんらかの理由(権限の消失など)で暗黙的に削除するまで存続します。

データベース・セッションでCHANGE NOTIFICATION権限が付与されている場合は、次の手順で問合せを登録して通知を受信できます。

  1. 登録する問合せの通知受信者を作成します。受信者には次のいずれかを使用できます。

  2. 「PL/SQLを介した変更通知への問合せの登録」の説明に従って、指定した通知受信者について問合せ登録を作成します。この登録は、SQL問合せを実行して行うことができます。SQLの実行後に登録が完了します。


    注意

    登録を作成するには、SYS以外のユーザーとして接続する必要があります。また、コミットされていないトランザクションが実行中でないことも必要です。

    正常に登録を作成して通知を受信するには、dml_locks init.oraパラメータを0(ゼロ)以外の値に設定しておく必要があります。

    dml_locksのデフォルト値は0(ゼロ)以外の値であるため、アプリケーションでdml_locksパラメータが明示的に設定されていなければ、この要件は自動的に満たされます。 


変更通知登録はデフォルトで永続的であり、アプリケーションで明示的に解除するまで存続します。

登録が正常に作成された後は、基礎となるトランザクションのコミット時に、登録済問合せで参照されるオブジェクトに変更があると、それに応答してOracle Databaseからクライアント・アプリケーションに通知されます。通知は、INSERTUPDATEおよびDELETE(トランザクションのコミット時)などのDML操作と、ALTERおよびDROPなどのDDL操作の結果として生成されます。

通知には、変更されたオブジェクトの名前、変更を行ったトランザクションのトランザクションIDおよび操作のTYPEINSERTUPDATEまたはDELETE)の情報が含まれます。


注意

1つのトランザクションで複数の登録済オブジェクトが変更されると、アプリケーションはトランザクションのコミット時に変更されたオブジェクトすべてについて通知を1つ受信します。 


サポートされる問合せタイプ

変更通知により、アプリケーションはストアド・プロシージャやREFカーソルの一部として実行される問合せなど、ほとんどの問合せタイプを登録できます。登録の実行時に、アプリケーションは問合せのみを実行します。つまり、DML操作やDDL操作は許可されません。また、次のタイプの問合せは、登録ではサポートされません。

登録プロパティ

Oracle Databaseでは、次のオブジェクト登録オプションがサポートされています。

  1. 通知時にパージ・オプション: 最初の変更通知後に登録を解除します。

  2. 「タイムアウト」オプション: 指定した時間間隔の後に登録が失効するように指定します。

  3. ROWID」オプション: 変更された行のROWIDは、通知の「ROWID」オプションの一部です。

  4. 信頼できる通知オプション: デフォルトでは、通知は共有メモリー内に生成されます。このオプションを選択すると、通知は永続データベース・キュー内に生成されます。通知は、登録済オブジェクトを変更するトランザクションとともにアトミックにエンキューされます。通知はデータベース内で永続的であるため、通知の生成後にインスタンスがクラッシュした場合は、以降の再起動時に配信するか、RACが稼働中の場合はクラスタの残りのインスタンスから配信できます。(注意: この場合、通知パフォーマンスと信頼性の間にトレードオフが存在します。信頼できる通知の生成時にはCPUとI/Oのコストが発生するため、通知パフォーマンスの向上を必要とする場合はデフォルトのイン・メモリー・オプションを選択することをお薦めします)。

  5. 「操作」フィルタ: 特定の操作に関する通知を受信できます(INSERTおよびUPDATEのみに関する通知など)。

  6. トランザクション・ラグ: 連続する通知間のカウントを指定します。

ROWID」オプションを選択した場合は、変更された行のROWIDが通知の一部として公開されます。ROWIDは、外部文字列形式で公開されます。その後、アプリケーションでは、通知に含まれるROWID情報に基づいて、SELECT * from table_name_from_notification where ROWID = rowid_from_notification形式の問合せを実行し、変更された行の内容を取得できる必要があります。ROWIDの長さは、通常のヒープ表の場合は18バイトです。索引構成表(IOT)の場合、ROWIDの長さは主キーのサイズに応じて異なるため、18バイトよりも長くなることがあります。

ROWID通知は階層形式で要約されます。サーバー側がメモリー不足のためにROWIDを保持できないと、通知はFULL-TABLE-NOTIFICATIONにロールアップされる場合があります(そのために通知記述子内の特殊フラグが予約されています)。このような通知を受信した場合、アプリケーションでは表全体(つまりすべての行)が変更された可能性があるものと想定する必要があります。この種の通知には、ROWIDは含まれていません。ROWIDが原因で共有メモリーの使用量合計が大きくなりすぎる(動的共有プール・サイズの1%を超える)場合、1回のトランザクションで1つの登録済オブジェクト内で変更された行数が多すぎる場合(約80行以上)、またはIOTについて変更された行の論理ROWIDの合計長が大きすぎる(約1800バイトを超える)場合は、ROWIDがロールアップされることがあります。

表の削除

表が削除されると、DROP NOTIFICATIONが公開されます。削除された表に登録があると、そのオブジェクトから対象が暗黙的に削除されます(存在しなくなるため)。この種の登録の対象が他のオブジェクトにも存在する場合、その登録は引き続き存在し、問題のオブジェクトに対するDMLトランザクションのコミット時には引き続き通知が発行されます。削除された表が特定の登録の対象となる唯一のオブジェクトであっても、引き続き登録は保持されます。登録を作成したユーザーは、以降にその登録を他のオブジェクトや問合せの追加に使用できます。

登録は、問合せの登録時点におけるオブジェクトのバージョンと定義に基づきます。オブジェクトが削除されると、そのオブジェクトに対する登録の対象はオブジェクトから永続的に失われます。その後は、同じスキーマ内で一致する名前を使用して別のオブジェクトが作成されても、新規に作成されたオブジェクトは既存のデータベース変更通知登録の目的では新規オブジェクトとなります。つまり、この新規に(同じスキーマおよび名前で)作成されたオブジェクトに変更があっても、オブジェクトの前バージョンに存在していた登録に関する通知は公開されません。

データベース変更登録用インタフェース

PL/SQLとOCIの両方で登録インタフェースを使用できます。

PL/SQL APIを使用して登録ブロックを定義できます。PL/SQL通知ハンドラなどの登録プロパティは、登録ブロックの開始フェーズで指定します。登録ブロック内で実行される問合せは重要な問合せとみなされ、その問合せで参照されるオブジェクトが登録に追加されます。登録は、登録ブロックの終了時に完了します。PL/SQLでは、DBMS_CHANGE_NOTIFICATIONパッケージを使用して変更通知の受信登録を行います。

OCI Registration APIでは、DBCHANGEという名前空間で関数OCISubscriptionRegisterを起動する必要があります。クライアント側のC通知コールバックなどの登録プロパティは、サブスクリプション・ハンドルの属性として指定します。OCISubscriptionRegisterからの戻り時に、データベースにエンドポイント登録が正常に作成されます。これにより、アプリケーションではその登録に複数の問合せを関連付けることができます。そのためには、サブスクリプション・ハンドルを文ハンドルの属性の1つとして移入します。文ハンドルに関連付けられているDBCHANGE名前空間にサブスクリプション・ハンドルがある場合、オブジェクトの登録は文の実行中、つまりOCIStmtExecuteコールの一部として発生します。

関連項目

OCIの例は、『Oracle Call Interfaceプログラマーズ・ガイド』のデータベース変更通知に関する項を参照してください。 

変更通知受信者としてのPL/SQLストアド・プロシージャの作成

登録済オブジェクトの変更に応答してデータベース・サーバーにより起動されるPL/SQLストアド・プロシージャを作成できます。通知を受信するプロシージャには次のシグネチャが必要です。schema_nameはデータベース・スキーマ名、proc_nameはストアド・プロシージャ名です。

PROCEDURE schema_name.proc_name( ntfnds IN SYS.CHNF$_DESC )

JOBQプロセスは、コールバック・プロシージャにCHNF$_DESCオブジェクト(通知記述子)を渡します。このオブジェクトの属性で変更の詳細が記述されます。たとえば、オブジェクトにはトランザクションID、発生した変更のタイプ、変更された表などが含まれます。コールバック・プロシージャは、このデータを中間層クライアント・アプリケーションに送信し、そこでさらに処理できます。


注意

JOB_QUEUE_PROCESSES初期化パラメータでは、ジョブの実行用に作成できるプロセスの最大数を指定します。指定したコールバック・プロシージャはジョブ・キュー・プロセス内で実行されるため、PL/SQL通知を受信するには、この初期化パラメータを0(ゼロ)以外の値に設定する必要があります。 


関連項目

 

PL/SQLを介した変更通知への問合せの登録

変更通知を受信する必要のあるデータベース問合せを登録する必要があります。登録は、次の2つの手順で行います。

  1. コールバック・プロシージャ名と通知に関する他のメタデータを指定するCHNF$_REG_INFOオブジェクトを作成します。

  2. DBMS_CHANGE_NOTIFICATIONパッケージ内でプログラム・ユニットを実行して問合せ登録を作成または更新してから、登録対象の問合せを実行します。

CHNF$_REG_INFOオブジェクトの作成

CHNF$_REG_INFOタイプのオブジェクトでは、登録済オブジェクトのいずれかに変更があった場合にデータベースで実行する必要のあるコールバック・プロシージャを指定します。タイプ属性は、SQL*Plusで次のコマンドを実行して表示できます。

DESC SYS.CHNF$_REG_INFO

表13-1に、SYS.CHNF$_REG_INFOの属性を示します。

表 13-1    SYS.CHNF$_REG_INFOの属性 
属性  説明 

CALLBACK 

通知の生成時に実行するPL/SQLコールバック・プロシージャの名前を指定します。この名前は、hr.dcn_callbackのようにschema_name.procedure_name形式で指定する必要があります。 

QOSFLAGS 

DBMS_CHANGE_NOTIFICATIONパッケージ内の次の定数を1つ指定します。

  • QOS_RELIABLE。通知がデータベース内で存続し、インスタンス障害後も残るように指定します。Real Application Clustersでインスタンスに障害が発生した場合は、正常なインスタンスがキューにある通知メッセージを配信できます。デフォルトでは、パフォーマンスを高めるために、データベースでは変更通知メッセージが共有メモリーのバッファに入れられます(つまり、メッセージは永続記憶域に記録されません)。

  • QOS_DEREG_NFY。データベースで最初の通知時に登録が解除されるように指定します。

  • QOS_ROWIDS。変更されたROWIDの情報を通知に含めるように指定します。

ビット単位のORを使用すると、前述のオプションの組合せを指定できます(dbms_change_notification.QOS_RELIABLE + dbms_change_notification.QOS_ROWIDSなど)。 

TIMEOUT 

登録のタイムアウト期間を指定します。0(ゼロ)以外の値に設定する場合は、データベースにより登録がパージされるまでの秒数を指定します。0またはNULLの場合、登録はクライアントにより明示的に解除されるまで存続します。

注意: QOSFLAGS属性ではTIMEOUTオプションとQOS_DEREG_NFYオプションを併用できます。 

OPERATIONS_FILTER 

SQL文の型に基づいてメッセージをフィルタリングします。DBMS_CHANGE_NOTIFICATIONパッケージ内の次の定数を1つ指定できます。

  • ALL_OPERATIONS。すべての変更時に通知します。

  • INSERTOP。INSERT時に通知します。

  • UPDATEOP。UPDATE時に通知します。

  • DELETEOP。DELETE時に通知します。

ビット単位のORを使用すると、操作の組合せを指定できます。たとえば、DBMS_CHANGE_NOTIFICATION.INSERTOP + DBMS_CHANGE_NOTIFICATION.DELETEOPのように複数の操作の組合せを指定できます。 

TRANSACTION_LAG 

クライアントがデータベースから遅れることのできるトランザクション数またはデータベース変更数を指定します。0の場合、クライアントは無効化メッセージを生成直後に受信します。5の場合、登録済オブジェクトを変更するトランザクションのうち5番目ごとに通知が送信されます。Oracle Databaseでは、中間の変更がオブジェクトの粒度で追跡され、変更が通知とともにバンドルされます。そのため、クライアントから中間の変更が失われることはありません。

注意1: トランザクションのコミット時にオブジェクトの変更通知をそれ以上の遅延なしで受信する必要のあるほとんどのアプリケーションでは、トランザクション・ラグ0(ゼロ)を選択するように期待されます。0(ゼロ)以外のトランザクション・ラグが役立つのは、アプリケーションで通知に対してなんらかのフロー制御を実装する必要がある場合のみです。0(ゼロ)以外のトランザクション・ラグを使用する場合は、アプリケーションのワークロードのプロパティを、通知が妥当な間隔で生成されるように設定することをお薦めします。そうしないと、ラグに達するまで通知が無限に遅延する可能性があります。

注意2: TRANSACTION_LAGを指定すると、登録時にQOS_ROWIDSを指定した場合にも、通知メッセージにはROWIDレベルの粒度を使用できなくなります。 

登録済オブジェクトに変更があるたびに、プロシージャhr.dcn_callbackを起動する必要があるとします。例13-1では、hr.dcn_callbackが変更通知を受け取るように指定するCHNF$_REG_INFOオブジェクトを作成します。オブジェクトを作成するには、DBMS_CHANGE_NOTIFICATIONパッケージに対するEXECUTE権限が必要であることに注意してください。

例 13-1    CHNF$_REG_INFOオブジェクトの作成

DECLARE
  v_cn_addr SYS.CHNF$_REG_INFO;
BEGIN
  -- create the object
  v_cn_addr := 
    SYS.CHNF$_REG_INFO
    (
      'hr.dcn_callback',                    -- name of PL/SQL callback procedure
       DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS, -- include rowids of modified objects
       0,                              -- registration persists until unregistered
       0,                              -- notify on all types of DML
       0                               
-- notify immediately (no transaction lag)
    );
  -- ... register objects ...
END;
/

関連項目

「データベース変更通知の構成: 使用例」 

DBMS_CHANGE_NOTIFICATIONを使用した登録の作成

DBMS_CHANGE_NOTIFICATIONパッケージ内のサブプログラムを使用し、通知受信者を指定して問合せを登録します。このパッケージの詳細は、『Oracle Database PL/SQLパッケージ・プロシージャおよびタイプ・リファレンス』を参照してください。このパッケージの内容は、SYSとして接続して次のコマンドを実行するとSQL*Plusで表示できます。

DESC SYS.DBMS_CHANGE_NOTIFICATION

表13-2に、DBMS_CHANGE_NOTIFICATIONサブプログラムの概要を示します。

表 13-2    DBMS_CHANGE_NOTIFICATIONサブプログラム 
プログラム・ユニットの
シグネチャ
 
説明 

NEW_REG_START (reg_info IN SYS.CHNF$_REG_INFO) RETURNS reg_id NUMBER 

インバウンド・オブジェクトreg_infoの登録ブロックの開始をマークします。このファンクションは、reg_infoオブジェクト・タイプで指定されたプロパティを使用して新規登録を作成します。登録には、データベースによりregistration_idという一意の整数識別子が割り当てられます。このregistration_idは、ファンクションからの戻り時にクライアントに戻されます。アプリケーションでは、registration_idを使用して、作成済の登録をすべて追跡できます。この登録に関して通知が生成されるときに、registration_idが通知の一部として含まれます。

このファンクションのコール後に、登録する問合せを実行し、REG_ENDをコールして登録境界を終了できます。登録の進行中は、新規登録を開始できません。  

REG_END 

NEW_REG_STARTまたはENABLE_REGで開始した登録境界の終了をマークします。REG_ENDのコール後は、新規に実行される問合せはデータベースに登録されません。 

ENABLE_REG (reg_id IN INTEGER) 

既存のregistration_idにデータベース・オブジェクトを追加します。このインタフェースはNEW_REG_STARTに似ていますが、オブジェクトの追加先として既存のregistration_idを受け入れます。以降に問合せを実行すると、問合せで参照されているオブジェクトが登録に追加されます。登録を終了するにはREG_ENDを起動します。 

DEREGISTER (reg_id IN INTEGER) 

登録IDで指定された登録を無効にします。 

オブジェクト登録の例として、hr.employees表の1行が変更されるたびにクライアントが通知を必要とするとします。例13-2に、この表をhr.dcn_callbackプロシージャで登録する無名PL/SQLブロックを示します。このブロックを実行するには、CHANGE NOTIFICATION権限を付与されている必要があることに注意してください。

例 13-2    変更通知へのemployees表の登録

DECLARE
  v_cn_recip       SYS.CHNF$_REG_INFO;
  v_regid          NUMBER;
  v_employee_id    hr.employees.manager_id%TYPE;
BEGIN
  v_cn_recip := SYS.CHNF$_REG_INFO('hr.dcn_callback', 
                DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS, 0, 0, 0);
  -- begin the registration boundary
  v_regid := DBMS_CHANGE_NOTIFICATION.NEW_REG_START(v_cn_recip);
    SELECT employee_id 
      INTO   v_employee_id 
    FROM   hr.employees       -- register the employees object
    WHERE  ROWNUM < 2;        -- write the query so that it returns a single row
  -- end the registration boundary
  DBMS_CHANGE_NOTIFICATION.REG_END;
  DBMS_OUTPUT.PUT_LINE('the registration id for this query is '||v_regid);
END;
/

例13-2では、プログラムはオブジェクト自体、つまりhr.employees表を登録します。フェッチにより複数行が戻されるため、エラー生成を回避するために、WHERE句により問合せは結果セット内の最初の従業員に制限されています。DBMS_CHANGE_NOTIFICATIONパッケージはオブジェクト自体を登録します。つまり、表に変更があると、登録済の問合せで戻される行が変更対象かどうかに関係なく、通知が生成されます。

関連項目

「データベース変更通知の構成: 使用例」 

既存の登録へのオブジェクトの追加

hr.employeesの問合せの登録IDにhr.departments表の問合せを追加することになったとします。保存済SQL*Plus出力またはUSER_CHANGE_NOTIFICATION_REGSの問合せから登録IDを取得した後、例13-3のコードを使用し、reg_idの数値IDを置き換えて、このオブジェクトを追加できます。

例 13-3    既存の登録へのオブジェクトの追加

DECLARE
  v_department_id     hr.departments.department_id%TYPE;
BEGIN
  -- begin registration boundary
  DBMS_CHANGE_NOTIFICATION.ENABLE_REG(reg_id);
    SELECT department_id
      INTO   v_department_id 
    FROM   hr.departments
    WHERE  ROWNUM < 2; -- register this query
  -- end registration boundary
  DBMS_CHANGE_NOTIFICATION.REG_END;
END;
/

変更通知登録の問合せ

次のデータ・ディクショナリ・ビューを問い合せて、データベース変更通知機能の登録済クライアントに関する情報を取得できます。

たとえば、クライアントの登録IDと通知を受信するオブジェクトのリストを取得できます。HRの登録IDと表名を表示するには、SQL*Plusから次のコマンドを実行できます。

connect hr/hr;
SELECT regid, table_name FROM USER_CHANGE_NOTIFICATION_REGS;

関連項目

DBA_CHANGE_NOTIFICATION_REGSおよびUSER_CHANGE_NOTIFICATION_REGSの説明は、『Oracle Databaseリファレンス』を参照してください。 

データベース変更通知の解析

トランザクションのコミット時に、Oracle Databaseでは登録済オブジェクトがトランザクション中に変更されたかどうかが判別されます。対象となったクライアントが検出されると、登録で指定されているコールバック・プロシージャが実行されます。

CHNF$_DESCオブジェクトの解析

データベース変更の詳細は、データベースからCコールバックまたはPL/SQLプロシージャに引数として渡される記述子を介して公開されます。特に、Oracle Databaseでは、トップレベルの変更通知記述子であるCHNF$_DESCタイプのオブジェクトが渡されます。タイプ属性は、SYSとして接続して次のコマンドを実行するとSQL*Plusで表示できます。

DESC SYS.CHNF$_DESC

表13-3に、CHNF$_DESCの属性を示します。

表 13-3    SYS.CHNF$_DESCの属性 
属性  指定 

REGISTRATION_ID 

登録時に戻された登録ID。 

TRANSACTION_ID 

変更を行ったトランザクションのID。 

DBNAME 

変更があったオブジェクトが常駐するデータベースの名前。 

EVENT_TYPE 

通知をトリガーするデータベース・イベント。たとえば、属性には様々なデータベース・イベントに対応する次の定数を含めることができます。

  • EVENT_NONE

  • EVENT_STARTUP(インスタンスの起動)

  • EVENT_SHUTDOWN(インスタンスの停止 - RACの場合は最後のインスタンスの停止)

  • EVENT_SHUTDOWN_ANY(RACの場合は任意のインスタンスの停止)

  • EVENT_DEREG(登録の削除)

  • EVENT_OBJCHANGE(登録済の表に対する変更)

 

NUMTABLES 

変更された表の数。 

TABLE_DESC_ARRAY 

CHNF$_TDESCタイプの表変更記述子のvarray表13-4を参照)。表記述子は、それぞれ変更された表に対応します。 

CHNF$_TDESCオブジェクトの解析

CHNF$_DESCタイプには、CHNF$_TDESCタイプの表記述子の配列を保持する属性TABLE_DESC_ARRAYが含まれています。タイプ属性は、SYSとして接続して次のコマンドを実行するとSQL*Plusで表示できます。

DESC CHNF$_TDESC

表13-4に、CHNF$_TDESCの属性を示します。

表 13-4    SYS.CHNF$_TDESCの属性 
属性  指定 

OPFLAGS 

変更があった表に対して実行された操作のタイプ。たとえば、属性には様々なデータベース操作に対応する次の定数を含めることができます。

  • ALL_ROWSは、DELETE *のように表全体に変更があるか、行レベルの粒度の情報が要求されないか、通知に使用できず、受信者は表全体が変更されたとみなす必要があることを示します。

  • UPDATEOPは更新を示します。

  • DELETEOPは削除を示します。

  • ALTEROPALTER TABLEを示します。

  • DROPOPDROP TABLEを示します。

  • UNKNOWNOPは不明な操作を示します。

 

TABLE_NAME 

変更があった表の名前。 

NUMROWS 

変更があった行数。 

ROW_DESC_ARRAY 

CHNF$_RDESCタイプの行記述子のvarray表13-5を参照)。opflagsALL_ROWSが設定されていた場合、desc_arrayメンバーはNULLです。 

CHNF$_RDESCオブジェクトの解析

登録時にROWIDオプションを選択した場合、CHNF$_TDESCタイプには変更された行のROWIDを含むCHNF$_RDESCタイプの配列が保持されます。注意: CHNF$_TDESCオブジェクトのopflagsフィールドにALL_ROWSが設定されていた場合、ROWID情報は使用できません。


表13-5に、CHNF$_RDESCの属性を示します。

表 13-5    SYS.CHNF$_RDESCの属性 
属性  指定 

OPFLAGS 

変更があった表に対して実行された操作のタイプ。表13-4OPFLAGSの説明を参照してください。 

ROW_ID 

変更された行のROWID。 

データベース変更通知の構成: 使用例

この使用例では、開発者として従業員データ(名前、所在地、電話番号など)を提供するWebアプリケーションを管理しているとします。このアプリケーションはOracle Application Serverで実行され、使用量が多く、バックエンド・データベースにあるhr.employees表およびhr.departments表の頻繁な問合せを処理します。この2つの表はあまり変更されないため、アプリケーションでは表の行をキャッシュすることでパフォーマンスを改善できます。キャッシュすると、バックエンド・データベースへのラウンドトリップのみでなく、サーバー側での実行待機時間も回避されます。

DBMS_CHANGE_NOTIFICATIONパッケージを使用すると、hr.employees表およびhr.departments表に基づく問合せを登録できます。データベース変更通知を構成する手順は、次のとおりです。

  1. 通知をリスニングし、hr.employees表とhr.departments表に対する変更の通知に応答して中間層キャッシュを更新するように、中間層HTTPリスナーを実装します。

  2. 「PL/SQLコールバック・プロシージャの作成」の説明に従って、変更通知を処理するサーバー側PL/SQLストアド・プロシージャを作成します。

  3. 「問合せの登録」の説明に従って、hr.employees表とhr.departments表を登録します。

手順1〜3を完了すると、手順2で定義したサーバー側PL/SQLプロシージャが、hr.employeesまたはhr.departmentsに対する変更に応答して実行されます。コールバック・プロシージャは、表の変更をWebアプリケーションに通知します。この通知に応答して、アプリケーションではバックエンド・データベースを問い合せてキャッシュをリフレッシュします。

PL/SQLコールバック・プロシージャの作成

この手順では、変更通知を処理するサーバー側ストアド・プロシージャを作成します。最初に、DBA権限を持つユーザーとしてデータベースに接続し、hrEXECUTE権限を付与します。

GRANT EXECUTE ON DBMS_CHANGE_NOTIFICATION TO HR;
GRANT CHANGE NOTIFICATION TO HR;

通知を受信するためにjob_queue_processesパラメータを有効化します。

ALTER SYSTEM SET "JOB_QUEUE_PROCESSES"=4;

受信した通知イベントのレコードを保持するデータベース表を作成できます。

connect hr/hr;
Rem Create a table to record notification events
CREATE table nfevents(regid number, event_type number);
Rem Create a table to record changes to registered tables
create table nftablechanges(
       regid number, 
       table_name varchar2(100),
       table_operation number);
Rem Create a table to record rowids of changed rows.
create table nfrowchanges(
       regid number, 
       table_name varchar2(100), 
       row_id varchar2(2000));

次に、例13-4に示すようにプロシージャhr.chnf_callbackを作成します。

例 13-4    サーバー側PL/SQLコールバック・プロシージャ

CREATE OR REPLACE PROCEDURE chnf_callback(ntfnds IN SYS.CHNF$_DESC) IS
regid NUMBER;
tbname VARCHAR2(60);
event_type NUMBER;
numtables NUMBER;
operation_type NUMBER;
numrows NUMBER;
row_id VARCHAR2(2000);
 
BEGIN
  regid := ntfnds.registration_id;
  numtables := ntfnds.numtables;
  event_type := ntfnds.event_type;
  insert into nfevents values(regid, event_type);
  
  IF (event_type = DBMS_CHANGE_NOTIFICATION.EVENT_OBJCHANGE) THEN
    FOR i IN 1..numtables LOOP
      tbname := ntfnds.table_desc_array(i).table_name;
      operation_type := ntfnds.table_desc_array(I). Opflags;
      insert into nftablechanges values(regid, tbname, operation_type);
      /* Send the table name and operation_type to  client side listener 
         using UTL_HTTP */                          
      /* If interested in the rowids, obtain them as follows */
      IF (bitand(operation_type, dbms_change_notification.ALL_ROWS) = 0)
      THEN     
        numrows := ntfnds.table_desc_array(i).numrows;
      else 
        numrows :=0;   /* ROWID INFO NOT AVAILABLE */
      END IF;
      
      /* The body of the loop is not executed when numrows is ZERO */
      FOR j IN 1..numrows LOOP
          Row_id := ntfnds.table_desc_array(i).row_desc_array(j).row_id;
          insert into nfrowchanges values(regid, tbname, Row_id);
          /* optionally Send out row_ids to client side listener using
             UTL_HTTP */
      END LOOP;
      
    END LOOP;
  END IF;
  commit;
END;
/

問合せの登録

コールバック・プロシージャの作成後に、通知を受信する表に基づいて問合せを登録します。例13-5では、データベースによる通知の生成時に実行されるサーバー側PL/SQLプロシージャの名前としてhr.chnf_callbackを渡します。プロシージャを作成するには、CHANGE NOTIFICATION権限が必要であることに注意してください。

例 13-5    表の登録

CREATE OR REPLACE PROCEDURE hr.table_reg
IS
  v_regds          SYS.CHNF$_REG_INFO;
  v_regid          NUMBER;
  v_employee_id    NUMBER;
  v_department_id  NUMBER;
BEGIN
  v_regds := SYS.CHNF$_REG_INFO ('hr.chnf_callback',  
                             DBMS_CHANGE_NOTIFICATION.QOS_ROWIDS, 
                             0,
                             0,
                             0);
  v_regid :=  DBMS_CHANGE_NOTIFICATION.NEW_REG_START(v_regds);
    SELECT employee_id
      INTO   v_employee_id
    FROM   hr.employees     -- register employees object
    WHERE ROWNUM < 2;       -- return a single row to avoid multiple fetch error
    SELECT department_id
      INTO   v_department_id
    FROM   hr.departments   -- register departments object
    WHERE  ROWNUM < 2;      -- return a single row to avoid multiple fetch error
  DBMS_CHANGE_NOTIFICATION.REG_END;
END;
/

EXEC hr.table_reg

次の問合せを発行すると、新規に作成した登録を表示できます。

SQL> select regid, table_name from user_change_notification_regs;
     REGID TABLE_NAME
---------- -------------------------------------------------------------
        16 HR.EMPLOYEES
        16 HR.DEPARTMENTS

前述のように登録を作成した後は、HR.EMPLOYEES表またはHR.DEPARTMENTS表に対するCOMMIT済変更に応答して、前述のサーバー側PL/SQLプロシージャchnf_callbackが実行されます。たとえば、employees表に対して次の更新が実行されるとします。

UPDATE employees SET salary=salary*1.05 WHERE employee_id=203;
COMMIT;

通知が処理された後、nfeventsnftablechangesおよびnfrowchanges表に次のような行があることがわかります。

SQL> select * from nfevents;
     REGID EVENT_TYPE
     ---------- ----------
     20045          6

SQL> select * from nftablechanges;
     REGID      TABLE_NAME         TABLE_OPERATION
     -------------------------------------------
     20045   HR.EMPLOYEES            4

SQL> select * from nfrowchanges;
     REGID  TABLE_NAME   ROW_ID
     ---------------------------------------------
     20045 HR.EMPLOYEES   AAAKB/AABAAAJ8zAAF

ベスト・プラクティス

変更通知に最適なパフォーマンスを得るためのガイドラインを次に示します。

登録済オブジェクトは少数であり、ほとんどが読取り専用で、この種のオブジェクトに対する変更はルールではなく例外です。オブジェクトの揮発性が非常に高いと、多数の無効化通知が送信されることになり、サーバー上の無効化キューに大量に格納される可能性があります。通知が頻繁で多数の場合は、通知の生成に伴うオーバーヘッドが原因でOLTPのスループットが低下する可能性があります。

また、同じ通知メッセージが複数の受信者宛に複製されないように、特定のオブジェクトについて重複登録の数を少なく(理想的には1に)しておくことをお薦めします。

トラブルシューティング

登録を作成済であるのに通知を受信していないと思われる場合、または登録を作成できない場合は、次のリストをチェックしてください。

  1. job_queue_processesパラメータが0(ゼロ)以外の値に設定されているかどうか。ハンドラを介してPL/SQL通知を受信するには、このパラメータを0(ゼロ)以外の値に設定する必要があります。

  2. SYS以外のユーザーとして登録を作成したかどうか。

  3. 登録済オブジェクトのDML変更を試行する場合に、トランザクションをコミットするかどうか。通知はトランザクションであり、トランザクションのコミット時に生成されることに注意してください。

  4. オブジェクトに対する登録がデータベースに正常に作成されたかどうかを確認するには、USER_CHANGE_NOTIFICATION_REGS またはDBA_CHANGE_NOTIFICATION_REGSビューから問い合せます。たとえば、現行のユーザーに関するすべての登録と登録済オブジェクトを表示するには、次のSELECT文を発行します。

    SELECT regid, table_name FROM user_change_notification_regs;
    
    
  5. コールバックでの実装エラーが原因で、PL/SQLコールバックの実行中に実行時エラーが発生する場合があります。その場合は、プロシージャの実行を試行したJOBQプロセスのトレース・ファイルに記録されます。通常、トレース・ファイル名は<ORACLE_SID>_j*_<PID>.trcです。たとえば、ORACLE_SIDがdbs1で、JOBQプロセスのプロセスIDが12483の場合、トレース・ファイル名はdbs1_j000_12483.trcとなります。

    実行時エラーがある場合は、JOBQトレース・ファイルにレポートされます。たとえば、通知ハンドラにchnf_callback、登録IDに100を指定して登録が作成されている場合を考えてみます。ここで、chnf_callbackストアド・プロシージャがデータベースに定義されていないとします。この場合、JOBQトレース・ファイルには次の書式のメッセージが含まれます。

    *************************************************************************** 
       Runtime error during execution of PL/SQL cbk chnf_callback for reg CHNF100.
       Error in PLSQL notification of msgid:
       Queue : 
       Consumer Name :
       PLSQL function :chnf_callback
       Exception Occured, Error msg:
       ORA-00604: 再帰SQLレベル2でエラーが発生しました。
       ORA-06550: 行1、列7、
       PLS-00201: 識別子CHNF_CALLBACKを宣言してください。
       ORA-06550: 行1、列7、
       PL/SQL: Statement ignored 
       ****************************************************************************
    
    
  6. コールバックの実行中にエラーになる場合は、実際に通知を受信していることを確認するために、次に示すように非常に単純なバージョンのコールバックを作成することを考慮してください。そのコールバックを段階的に発展させて、さらにアプリケーション論理を追加できます。たとえば、ユーザーがHRの場合は、次のように非常に単純なバージョンの通知ハンドラを作成できます。

    Rem create a table in the HR schema to hold a count of number of notifications 
    received.
    Create table nfcount(cnt number);
    Insert into nfcount values(0);
    Commit;
    CREATE OR REPLACE PROCEDURE chnf_callback (ntfnds IN SYS.CHNF$_DESC)
    IS
    BEGIN
      update nfcount set cnt = cnt+1;
      commit;
    END;
    /
    
    

    作成した単純なプロシージャにより、表のカウント列が増分されてコミットされます。通知が公開されているかどうかを確認するには、表nfcountから問い合せて、登録済オブジェクトが変更されてトランザクションがコミットされたときに、cnt列の値が実際に増えるかどうかを調べます。

  7. トランザクションがコミットされてからエンド・ユーザーが通知を受信するまでに、タイム・ラグが発生している場合があります。


戻る 次へ
Oracle
Copyright © 2006 Oracle Corporation.

All Rights Reserved.
目次
目次
索引
索引