10

私は JOliver の Event Store 3.0 をプロジェクトの潜在的なコンポーネントとして実験しており、Event Store を介したイベントのスループットを測定しようとしています。

新しいストリームを作成し、GUID id と文字列プロパティで構成される非常に単純なイベントを MSSQL2K8 R2 DB にコミットする for ループを本質的に反復する単純なハーネスの使用を開始しました。ディスパッチャは基本的にノーオペレーションでした。

このアプローチにより、別の 32 ウェイ G7 DL580 上の DB を使用して、8 ウェイ HP G6 DL380 で 1 秒あたり約 3,000 回の操作を実行することができました。テスト マシンはリソースにバインドされていませんでした。私の場合、ブロッキングが限界のようです。

Event Store のスループットを測定した経験のある人はいますか? また、どのような数値が達成されましたか? 実行可能なオプションにするために、少なくとも 1 桁多くのスループットを得ることを望んでいました。

4

3 に答える 3

7

IO のブロックが最大のボトルネックになることに同意します。ベンチマークで確認できる問題の 1 つは、単一のストリームに対して操作を行っていることです。1 秒あたり 3,000 件以上のイベントで、ドメイン内にいくつの集約ルートがありますか? EventStore の主な設計は、複数の集約に対するマルチスレッド操作用であり、読み取り世界アプリケーションの競合とロックを減らします。

また、どのシリアル化メカニズムを使用していますか? JSON.NET? 私は Protocol Buffers の実装を (まだ) 持っていませんが、すべてのベンチマークは、パフォーマンスの点で PB が大幅に高速であることを示しています。アプリケーションに対してプロファイラーを実行して、最大のボトルネックがどこにあるかを確認すると興味深いでしょう。

私が気付いたもう 1 つのことは、ネットワーク ホップを方程式に導入しているため、単一のストリームに対するレイテンシ (およびブロック時間) が増加することです。ソリッド ステート ドライブを使用するローカル SQL インスタンスに書き込みを行った場合、磁気ドライブを実行し、データ ファイルとログ ファイルが同じプラッターにあるリモート SQL インスタンスと比較して、数値がはるかに高いことがわかりました。

最後に、ベンチマーク アプリケーションは System.Transactions を使用しましたか、それともデフォルトでトランザクションなしに設定しましたか? (EventStore は、System.Transactions またはあらゆる種類の SQL トランザクションを使用しなくても安全です。)

以上のことから、EventStore には少しの注意で劇的に最適化できる領域があることに疑いの余地はありません。実際のところ、1 回のコミット操作中に SQL Server (および一般的な RDBMS エンジン) 内で実行される書き込みの数を減らすために、3.1 リリース用に下位互換性のあるスキーマ リビジョンをいくつか試しています。

3.x の基盤となる 2.x の書き直しを開始するときに直面した最大の設計上の問題の 1 つは、非同期のノンブロッキング IO のアイデアです。node.js やその他のノンブロッキング Web サーバーが、スレッド化された Web サーバーよりも桁違いに優れていることは誰もが知っています。ただし、呼び出し側が複雑になる可能性が高くなります。これは、ほとんどのプログラムとライブラリの動作方法の根本的な変化であるため、強く考慮する必要があります。イベント化されたノンブロッキング モデルに移行する場合、それは 4.x の時間枠でより長くなるでしょう。

結論: ボトルネックがどこにあるかを確認できるように、ベンチマークを公開してください。

于 2011-11-23T04:07:42.063 に答える
6

素晴らしい質問マット (+1)、そしてオリバー氏自身が答え (+1) として答えたのを見ました!

私は、あなたが目にしている毎秒 3,000 コミ​​ットのボトルネックを助けるために、私自身が遊んでいる少し異なるアプローチを取り入れたかったのです。

JOliver の EventStore を使用するほとんどの人が従おうとしているように見える CQRS パターンでは、多数の「スケールアウト」サブパターンが可能です。人々が最初に待ち行列に入れるのは、イベント コミット自体であり、ボトルネックが見られます。「待ち行列に入れる」とは、実際のコミットからオフロードし、それらを書き込み最適化されたノンブロッキング I/O プロセスに挿入することを意味し、または「列"。

私の大まかな解釈は次のとおりです。

コマンド ブロードキャスト -> コマンド ハンドラ -> イベント ブロードキャスト -> イベント ハンドラ -> イベント ストア

これらのパターンには、コマンド ハンドラーイベント ハンドラーの 2 つのスケールアウト ポイントが実際にあります。上記のように、ほとんどの場合、イベント ハンドラーの部分、または場合によってはコミットを EventStore ライブラリにスケール アウトすることから始めます。これは通常、どこか (Microsoft SQL Server データベースなど) に永続化する必要があるため、これが最大のボトルネックになるためです。

私自身、これらのコミットを「キューに入れる」ための最高のパフォーマンスをテストするために、いくつかの異なるプロバイダーを使用しています。CouchDB と .NET の AppFabric キャッシュ (優れた GetAndLock() 機能を備えています)。[OT]AppFabric の耐久性の高いキャッシュ機能が非常に気に入っています。これにより、複数のマシンにまたがってリージョンをバックアップする冗長キャッシュ サーバーを作成できます。したがって、少なくとも 1 つのサーバーが稼働している限り、キャッシュは存続します。[/OT]

したがって、イベント ハンドラーがコミットを EventStore に直接書き込まないことを想像してください。代わりに、Windows Azure Queue、CouchDB、Memcache、AppFabric Cache などの「キュー」システムにハンドラを挿入します。ポイントは、イベントをキューに入れるためのブロックがほとんどまたはまったくないシステムを選択することです。これは冗長性が組み込まれているため耐久性があります (Memcache は冗長性オプションとしてはあまり好きではありません)。サーバーがドロップした場合でもイベントがキューに入れられている場合に備えて、その冗長性が必要です。

この「Queued Event」から最終的にコミットするには、いくつかのオプションがあります。これについては、Windows Azure のキュー パターンが気に入っています。これは、多くの「ワーカー」が常にキュー内で作業を探すことができるためです。ただし、Windows Azure である必要はありません。バックグラウンド スレッドで実行される「キュー」と「ワーカー ロール」を使用して、ローカル コードで Azure のキュー パターンを模倣しました。それは本当にうまくスケーリングします。

ユーザー更新イベントのこの「キュー」を常に調べている 10 人のワーカーがいるとします (私は通常、イベント タイプごとに 1 つのワーカー ロールを作成します。これにより、各タイプの統計を監視できるようになるため、スケールアウトが容易になります)。2 つのイベントがキューに挿入され、最初の 2 つのワーカーがそれぞれ即座にメッセージを取得し、同時に EventStore に直接挿入 (コミット) します。Jonathan が回答で述べたように、マルチスレッドです。そのパターンのボトルネックは、選択したデータベース/イベントストアのバッキングになります。EventStore が MSSQL を使用していて、ボトルネックがまだ 3,000 RPS であるとします。システムは、RPS が 20,000 バースト後に 50 RPS などに低下したときに「追いつく」ように構築されているため、問題ありません。これは、CQRS が許可する自然なパターンです: 「結果整合性」。

CQRS パターンに固有のスケールアウト パターンが他にもあると言いました。もう 1 つは、前述のとおり、コマンド ハンドラー (またはコマンド イベント) です。これは私が行ったことの 1 つです。特に、私のクライアントの 1 つがそうであるように、非常に豊富なドメイン ドメインがある場合 (すべてのコマンドでプロセッサを集中的に使用する検証チェックが数十回行われます)。その場合、コマンド自体を実際にキューに入れ、一部のワーカー ロールによってバックグラウンドで処理されます。これにより、イベントの EvetnStore コミットを含むバックエンド全体をスレッド化できるため、優れたスケールアウト パターンも得られます。

明らかに、これの欠点は、リアルタイムの検証チェックが失われることです。私は通常、ドメインを構築する際に検証を 2 つのカテゴリに分割することで、この問題を解決しています。1 つは、ドメインでの Ajax またはリアルタイムの「軽量」検証 (プレコマンド チェックのようなもの) です。その他は、ドメイン内でのみ実行され、リアルタイムのチェックには使用できないハード フェイル検証チェックです。次に、ドメイン モデルで障害対応のコードを作成する必要があります。つまり、何かが失敗した場合の回避策を常にコーディングします。通常は、何かがうまくいかなかったというユーザーへの通知メールの形式で行われます。このキューに入れられたコマンドによってユーザーがブロックされなくなったため、コマンドが失敗した場合にユーザーに通知する必要があります。

そして、「バックエンド」に行く必要がある検証チェックは、クエリまたは「読み取り専用」データベースに送られますよね?たとえば、一意の電子メール アドレスを確認するために EventStore にアクセスしないでください。フロント エンドのクエリ用に、可用性の高い読み取り専用データストアに対して検証を行うことになります。CQRS のクエリ部分として、システム内のすべてのメール アドレスのリスト専用の 1 つの CouchDB ドキュメントを用意してください。

CQRSは単なる提案です...重い検証方法のリアルタイムチェックが本当に必要な場合は、その周りにクエリ(読み取り専用)ストアを構築し、検証を高速化できます-PreCommandステージで、挿入される前に待ち行列。多くの柔軟性。また、空のユーザー名や空のメールなどの検証はドメインの問題ではなく、UI の責任 (ドメインでリアルタイムの検証を行う必要性をオフロードする) であるとさえ主張します。私は、MVC/MVVM ViewModel で非常に豊富な UI 検証を行ったいくつかのプロジェクトを設計しました。もちろん、処理前に有効であることを確認するために、私のドメインには非常に厳密な検証がありました。しかし、平凡な入力検証チェック、または私が「軽量」検証と呼んでいるものをViewModelレイヤーに移動すると、エンドユーザーにほぼ瞬時のフィードバックが得られます。私のドメインに到達することなく。(ドメインとの同期を維持するためのトリックもあります)。

要約すると、これらのイベントがコミットされる前にキューに入れることを検討してください。Jonathan が回答で述べているように、これは EventStore のマルチスレッド機能にうまく適合します。

于 2012-06-21T20:25:33.400 に答える
0

Erlang/Elixir、 https://github.com/work-capital/elixir-cqrs-eventsourcingを使用した Eventstore を使用して、大規模な同時実行のための小さなボイラープレートを作成しました。データベース接続、プーリングなどを最適化する必要はまだありますが、複数のデータベース接続を持つ集約ごとに 1 つのプロセスを持つという考えは、ニーズに沿っています。

于 2016-10-27T08:12:51.940 に答える