13

私たちの組織では、SQL Server 2005 データベースとかなりの数のデータベース クライアント (Web サイト (php、zope、asp.net)、リッチ クライアント (レガシー fox pro)) を使用しています。ここで、コア データベースから特定のイベントを他のシステム (MongoDb、LDAP など) に渡す必要があります。メッセージング パラダイムは、この種の問題をかなり解決できるようです。そこで、RabbitMQ ブローカーをミドルウェアとして使用することにしました。

データベースからイベントを消費するという問題には、最初は 2 つの解決策しかないように思われました。

  1. 送信メッセージについてデータベースをポーリングし、それらをメッセージ ブローカーに渡します。
  2. 特定のテーブルでトリガーを使用して、同じマシン上のブローカーにメッセージを渡します。

最初のアイデアは、SQL の定期的な実行が関係している場合に発生する遅延の問題のために嫌いでした。

しかし、イベントベースのトリガーアプローチには、現時点では解決できないように見える問題があります。次のシナリオを検討してください。

  1. テーブルに行が挿入されます。
  2. トリガーが起動してメッセージを送信する (C# で記述された CLR ストアド プロシージャを使用)

データを書き込むトランザクションがロールバックされない限り、すべて問題ありません。この場合、データは一貫していますが、トランザクションのコミット時ではなく、データベース ログへの書き込み時にトリガーが起動するため、メッセージは既に送信されており、ロールバックできません (これは RDBMS の正しい動作です)。 .

あまりにも多くのトリガーを要求していて、データを操作する以外のタスクには適していないことに気付きました。

だから私の質問は:

  1. トリガーを使用してデータイベントを抽出できた人はいますか?
  2. データ イベントを使用するその他の方法としてアドバイスできるものはありますか?
  3. クエリ通知 (Service Broker の上に構築) は私の状況に適していますか?

前もって感謝します!

4

2 に答える 2

12

明らかな不適合を最初に排除しないでください。比較的安定したデータのキャッシュ無効化に対処するように設計されているため、クエリ通知はこれに適したテクノロジーではありません。QNを使用すると、テーブルが変更されたことだけがわかりますが、が変更されたかを知ることはできません。

SQLCRLを呼び出すトリガーが機能しない理由を理解してくれたことを称賛します。ロールバック時に一貫性が失われます。

では、何機能しますか?これを考慮してください:BizTalkServer。言い換えれば、この問題空間を中心に構築されたビジネス全体があり、解決策は些細なことではありません(そうでなければ、誰もそのような製品を購入しません)。

いくつかの原則に従うことで、かなり遠くまで行くことができます。

  • デカップリング。イベントベースのトリガーはOKですが、トリガーからメッセージを送信しないでください。ロールバックの一貫性の問題とは別に、すべてのDML操作で外部API呼び出し(RabbitMQ送信)を待機するレイテンシーの問題と、外部API呼び出しの失敗の可用性の問題(RabbitMQが利用できない場合、DBは利用できません)もあります。 )。解決策は、トリガーに通常のテーブルをキューとして使用させることです。トリガーはローカルデータベースキューにメッセージをエンキューし(つまり、このテーブルに挿入します)、外部プロセスはメッセージをデキューすることによってこのキューを処理します(つまり、テーブル)およびそれらをRabbitMQに転送します。これにより、トランザクションがRabbitMQ操作から切り離されます(外部プロセス元のxactがコミットした場合にのみメッセージが表示されます)が、コストは明らかに追加されたレイテンシーです(余分なホップが含まれ、ローカルテーブルがキューとして機能します)。
  • べき等。RabbitMQはデータベースとの分散トランザクションに登録できないため、DB操作(キューとして機能するローカルテーブルからのデキュー)およびRabbitMQ操作(送信)の原子性を保証することはできません。どちらか一方が失敗したときに成功する可能性があり、明示的な分散トランザクション登録サポートがなければ、どちらかを回避する方法はありません。これは、アプリケーション時々重複メッセージを送信することを意味します(通常、何らかの理由ですでに問題が発生している場合)。そして、簡単に説明します。明示的な「確認」メッセージの動作に登録し、シーケンス番号を送信することは、メッセージングに加えてTCPを再発明していることにすぐに気付くため、敗戦です。その道は体で舗装されています。
  • 許容範囲。上記の項目と同じ理由で、時々送信されたと思われるメッセージが届かないことがあります。繰り返しますが、これが引き起こす損害は完全にビジネス固有のものです。問題は、この状況をどのように防ぐか(ほとんど不可能です...)ではなく、この状況をどのように検出し、どうするかです。特効薬はありません。恐れ入ります。

あなたはServiceBrokerを渡す際に言及します(クエリ通知を強化しているという事実は、それの最も興味のない側面です...)。SQL Serverに組み込まれたメッセージングプラットフォームとして、Exactly Once In Orderの配信が保証され、完全に処理されるため、上記のすべての問題点が解決されます(SENDトリガーから免責なしで、アクティベーションを使用できます)遅延の問題を解決するために、メッセージの重複や欠落が発生することはなく、明確なエラーセマンティクスがあります)、および以前に言及しなかったその他の問題点(データとメッセージが同じであるため、バックアップ/復元の一貫性)ストレージの単位-データベース、SSBがデータベースのミラーリングとクラスタリングの両方をサポートするためのHA / DRフェイルオーバーの一貫性など)。ただし、欠点は、SSBが別のSSBサービスとのみ通信できることです。つまり、SSBは2つ(またはそれ以上)のSQLServerインスタンス間でメッセージを交換するためにのみ使用できます。その他の使用では、当事者がSQLServerを使用してメッセージを交換する必要があります。ただし、エンドポイントがすべてSQL Serverである場合は、ServiceBrokerを使用した大規模な展開があることを考慮してください。phpやasp.netなどのエンドポイントSQL Serverエンドポイントと見なされます。これらは、DB APIの上位にあるプログラミングレイヤーにすぎません。たとえば、別のエンドポイントでは、ハンドヘルドデバイス(電話)からデータベースに直接メッセージを送信する必要があります(99%の時間はWebサービスを介して、つまり、最終的にSQL Serverに到達できることを意味します)。もう1つの考慮事項は、SSBが低遅延ではなく、スループットと信頼性の高い配信を対象としていることです。たとえば、HTTPWebリクエストで応答を返すために使用するテクノロジーではありません。Webリクエストによってトリガーされたものを処理するために送信するために使用するテクノロジーです。

于 2012-10-26T13:13:59.407 に答える
2

Remus の答えは、イベントを生成して処理するためのいくつかの健全な原則を示しています。トリガーからイベントのプッシュを開始して、低レイテンシーを実現できます。

トリガーから必要なすべてを達成できます。これを引き続き2 つのコンポーネントに分離します。イベントを生成するトリガーと、イベントを読み取るローカル リーダーです。

最初のコンポーネントはトリガーです。

  • トランザクションがコミットされたときに何をする必要があるかを準備する CLR トリガーを作成します。
  • System.Transactions.IEnlistmentNotification常に準備することに同意し、そのvoid Commit(System.Transactions.Enlistment)メソッドが準備済みアクションを実行する を作成します。
  • トリガーで、呼び出しますSystem.Transactions.Transaction.Current.EnlistVolatile(enlistmentNotification, System.Transactions.EnlistmentOptions.None)

メモリ内のロックレス キューにデータを追加したり、メモリ内の他の状態を更新したりするなど、アクションは短くて快適なものにする必要があります。他のマシンやプロセスと通信しようとしないでください。ディスクに書き込まないでください (ディスクに書き込みたい場合は、キュー テーブルに挿入する通常のトリガーを作成してください)。共有された静的状態が一意になるように、アセンブリが一度だけ読み込まれるように注意する必要があります。これは、静的状態が他のアセンブリによって参照されていない最上位アセンブリにある場合に最も簡単です。そのため、他のアセンブリはそれを読み込もうとしません。

また、次のいずれかを行う必要があります

  • 以前にキューに入れられたすべてのメッセージを送信せずにシステムが再起動された場合でも、正しい状態になるように状態を初期化します (メモリ内の短いキューは永続的ではないため)。これは、メッセージを再送信する可能性があることを意味するため、べき等である必要があります。また
  • 見逃したメッセージを拾うために、別のコンポーネントの耐性に依存する

2 番目のコンポーネントは、トリガーによって更新された状態を読み取ります。キューまたは状態から読み取り、必要なことを実行する別の CLR コンポーネントを作成します (メッセージング システムにべき等メッセージを送信する、送信されたことを記録するなど)。このコンポーネントが失敗する可能性がある場合 (ヒント: 失敗する可能性があります)、別のシステムに属する何らかの形式の許容範囲が必要になります。新しい状態が利用可能になったときにトリガーが 2 番目のコンポーネントに信号を送ることで、低レイテンシーを実現できます。

アーキテクチャ上の可能性の 1 つは、別の低待機時間コンポーネントが取得できるように、トリガーがコミット時にイベントをメモリに配置し、2 番目のコンポーネントが冪等メッセージの低待機時間で信頼性の低いコピーを送信するようにすることです。これを、SSB などのより信頼性が高く耐久性のあるメッセージング システムと組み合わせることができます。これは、信頼性と耐久性に優れていますが、遅延が大きくなり、後で同じ冪等メッセージを送信します。

于 2014-03-05T01:44:46.717 に答える