Oracleでのオブジェクト変更通知の使用経験

最近、私は面白いことをしなければならなかったので、これをHabrahabrの読者と共有することにしました。 Oracleでオブジェクト変更通知を使用した経験についてお話したいと思います。 要求を行わずにデータが変更されたことを確認する方法について。



なぜオブジェクト変更通知(OCN)が必要なのですか



データベース内のデータの変化を観察する必要がありました。 最初に思いついた解決策は単純なものでした-一定の時間間隔(たとえば、1分に1回)でデータを更新します。 このアプローチの欠点は明らかです。選択した期間にわたってデータが変更されていない可能性があるため、サーバーとデータ転送チャネルをロードするだけです(観察するデータは非常に膨大です)。 または、時間間隔の開始時にデータが変更され、時間がなくなるまでデータについて知ることができません(これはそれほど重要ではありませんが、最初の可能性が高いです)。 非常に多くの顧客が存在する可能性があることを考えると、最初のマイナスは本当に深刻です。 幸いなことに、バージョン10g以降では、Oracleがこの問題に対するより良いソリューションを提供していることがわかりました。



OCNの仕組み



OCNは、データベースのサーバー側でのみ動作し、監視するデータが変更されたことを特別なインターフェイスを介してクライアントアプリケーションに通知できます。



これが内部でどのように機能するかについての詳細情報は、Googleで簡単に確認できます。 既に豊富な情報で記事を膨らませる必要はないと思ったので、簡単に説明します。











最初に、サーバーがリクエスト登録を使用できるようにし、同時通知の使用を許可する必要があります。 次に、ユーザーはアラートを登録します。 登録するとき、次の例のように、監視する必要がある要求が示されます。
select * from TESTUSER.TESTTABLE;
      
      



結果セットのデータが変更されると、oracleはこれについてクライアントに通知します(登録されたIPを介して特別なパケットを送信するか、登録されたストアドプロシージャを実行します)。 このメカニズムのサポートは、.NETおよびJavaのデータベースプロバイダーに組み込まれています。 アプリケーションを.NETで記述したので、そのコンテキストで話し続けます(ただし、サンプルコードを除き、ほとんどのことは、サーバーで最も興味深いことが行われているため、これに依存しないと思います)。



コード例のない場所



順番に行きましょう:



素晴らしいコード例
 using System; using System.Threading; using System.Data; using Oracle.ManagedDataAccess.Client; namespace ChangeNotificationSample { public class ChangeNotificationSample { public static bool IsNotified; public static void Main(string[] args) { const string constr = "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=myHost)(PORT=myPort)))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=myServiceName))); Validate Connection = true; Connection Timeout=180;User Id=TESTUSER;Password=myPass"; OracleConnection con = null; try { con = new OracleConnection(constr); OracleCommand cmd = new OracleCommand("select * from TESTUSER.TESTTABLE", con); con.Open(); //      . OracleDependency.Port = 1005; // OracleDependency var dep = new OracleDependency(cmd); //,       //  ,        cmd.Notification.IsNotifiedOnce = false; //  ,    cmd.Notification.Timeout = 300; //   "- " dep.OnChange += OnMyNotificaton; //,    ,     cmd.ExecuteNonQuery(); } catch (Exception e) { Console.WriteLine(e.Message); } finally { if (con != null) { con.Close(); con.Dispose(); } } //    ,      10    while (IsNotified == false) { Thread.Sleep(100); } Console.ReadLine(); } private static void OnMyNotificaton(object sender, OracleNotificationEventArgs eventArgs) { Console.WriteLine("Notification Received"); DataTable changeDetails = eventArgs.Details; Console.WriteLine("Data has changed in {0}", changeDetails.Rows[0]["ResourceName"]); IsNotified = true; } } }
      
      





私はほとんど完全にドキュメントから例を取り上げましたが、私の意見ではいくつかの重要な点で補足し、わずかに減らしました。 コードが記述され、コンパイルされ、プログラムが実行されたら、次のように実行できます。



 insert into testuser.testtable (col1, col2) values (1,1); commit;
      
      





完了すると、次のようになります。











やれやれ、すべてうまくいく! より詳細な情報は、例えばドキュメンテーションで取得できます 。 ドキュメントに通常示されていないポイント、つまり遭遇する困難に直行する方が良いでしょう。



短所



私たちの場合、このアプローチの利点を上回るほど多くのマイナスはありませんでしたが、それらのいくつかはほとんどマイナスとは言えないため、ニュアンスです。 しかし、敵は自分で知っておくべきだと思います。



まず第一に、私たちは自宅でそれを実行しようとしましたが、すべてがうまくいきました。 そして、それが通常起こるように、何も顧客のために働きませんでした。 本当に、それさえ離陸しませんでした。 問題が何であるかを見つけることは、かなり重要な作業であることが判明しました。 登録後、特別なリクエストを完了することで確認できます:



 select * from USER_CHANGE_NOTIFICATION_REGS;
      
      





そのため、登録後すぐにこの登録を見ましたが、通知が機能するはずだったその時点で、登録レコードは単に消えました。 残念ながら、この動作を説明するログには何も見つかりませんでした。 ファイアウォールをすべてのせいにすることなく、チャンスをつかまなければなりませんでした。 幸いなことに、私たちは正しく推測しました。 したがって、最初の推奨事項は、通知を登録するときにファイアウォールを確認し、ポートを指定することを忘れないことです。



すべての登録要求がサポートされているわけではありません。 1回のリクエストでデータを読み取って通知をかけることはできない可能性があることを覚えておく必要があります。 リクエストに多くの参加がある場合は、同様のシンプルなリクエストを書くための心構えを整えることをお勧めします。 さらに、結合を使用してクエリのアラートを登録する場合、クエリ内のテーブル(それぞれ独自のテーブルにある)と同じ数のアラートを実際に登録しますが、これはリソースの浪費になる可能性があります。



デフォルトでは、登録はタイムアウトなしで作成されます。 つまり、そうであったように、永遠に。 通常、アプリケーションの稼働時間は限られているため、このような登録は必要ありません。 このような登録は、特にそれらが多数ある場合、データベースから取り出すのは非常に困難です。 どうやら、正しい方法は、作業を終えたら登録を解除することです(同じ接続でこれを行い、OracleDependencyインスタンスへのリンクがある場合、これは簡単です。OracleDependencyクラスのインスタンスでRemoveRegistrationメソッドを呼び出すだけです)。 ただし、この方法は、アプリケーションがクラッシュせず、通信チャネルが破損せず、リソースが常に解放される理想的な世界にのみ適しているように思えます。 それ以外の場合、最終的には、削除しない場合と同じ結果になりますが、結果は後で(本番環境に移行したときに)表示され始めます。 この方法で問題を解決しました-登録中に常にタイムアウトを示すようになりました。 有効期限の数秒前に、古い登録を削除して新しい登録を作成します(登録の不在時間を最小限に抑えるため)。 もちろん、解決策は最も理想的なものではありませんが、原則としては満足しています。 より良い方法についてのアイデアをお持ちの方は、このコードを書き直したいと思います。



1つのアプリケーションのフレームワーク内でアラートをより速く動作させるために、データベースを操作するロジック、オブジェクトにデータ変更イベントを通知するロジック、および両方を管理するロジックを別々のクラスに分けました。 これにより、1つの一般的な通知メカニズムを使用して、外部に移動することなく(oracleで)アプリケーション内で手動で通知を送信することができました。 セッションの一部として働いており、アクションの結果を即座に確認する必要があるユーザーにとっては、すべてがうまく機能します。



ありがとう



彼らが私の場所でこの記事を読んだこと。 私のコードやプレゼンテーションの方法に対する批判から、このテクノロジーを知る過程で経験した感情に至るまで、フィードバックをいただければ幸いです。 OCNでの経験について教えていただければ、私も非常に満足しており、貯金箱に資料を追加できます。



事前に感謝します。



All Articles