MakingSenseofStreamProcessing / Bringing Together Event Sourcing and Stream Processing


MakingSenseofStreamProcessing

イベントソーシングとストリーム処理を取り入れる(Bringing Together Event Sourcing and Stream Processing)

要約

ストリーム処理例(Googleアナリティクス)に戻る。データを格納するための2つのオプションがあることを思い出そう。:(a)生イベント)raw event、または(b)の集約要約(aggregated summaries)だ。(図1-13)。

図1-13。 集計されたデータに対する生のイベントを格納する

  • このように入れ、分析のためのイベント処理とイベントソーシングは非常に似始めている。PageViewEvent?(図1-3)とイベントソースのデータベース(AddedToCart?UpdatedCartQuantity?)の両方が時間とともに何が起こったかの履歴を含む。しかし、ショッピングカート、またはページビューのカウントの内容を見ているとき、現在のシステムの状態を見る。つまりそれはイベントの全履歴を1つに押しつぶした時に得た最終結果だ。
  • だから、カートの現在の状態は数量2というかもしれない。生のイベントの履歴は、以前のいくつかの時点で時間に数量が3だったが、顧客が後で心がわりして、それを2に更新したことを教えてくれる。集約された最終的な結果は、現在の数量が2であることを示している。
  • さらに、それについて考えると、生のイベントは、データベース書き込みのすべての情報が単一のブロブに含まれた、理想的な形態であることを観察することができる。生のイベントを保存するなら、5つの異なるテーブルを更新する必要はない。ログの最後にイベントを追加すればよいのだ。つまり、データベース書き込みの最も簡単かつ最速の方法だ(図1-14)。 図1-14。イベントは書き込み用に最適化され、集約値は読み込みに最適化されている。
  • 一方、集約されたデータは、データベースからデータを読み取るために理想的な形態だ。もしも顧客がショッピングカートの中身を見ている場合は、現在の状態に至った変更の全体の履歴には興味を持っていない:彼らはたった今、カート内に何があるか知りたいのだ。分析アプリケーションは、通常、ユーザーに完全なページビューの完全なリストを表示する必要はなく、ただ、チャートのフォームの中で集約された要約のを表示すれば良い。
  • それで、読んでいるときに、変更の履歴が現在の状態を表す1つのオブジェクトに一緒に既に押しつぶされている場合に、最高のパフォーマンスを得ることができる。一般的に、書き込み用に最大限最適化されていたデータの形式は、読み込み用に最大限最適化された形と同じではない。従って、後ほど、読み取り方法から書き込み方法を分離するための理解ができる。(このアイデアは、時にはコマンドクエリ責務分離として知られている、またはCQRS5)- 5 Greg Young: “CQRS and Event Sourcing,” codebetter.com, 13 February 2010. 16

図1-15。大雑把に言うと、ボタンをクリックするとイベントが書き込まれ、ユーザーが画面上で見ている物は読まれる集約されたデータに対応する。

  • さらに進めて、データベースの読み取りと書き込み、につながるユーザー・インターフェースを考える。一般的にデータベースへの書き込みは、ユーザーがいくつかのボタンをクリックしたために起こる。例えば、彼らはいくつかのデータを編集し、保存ボタンをクリックする。そのため、ユーザー・インターフェースのボタンはイベントソーシングの履歴の中で、生のイベント(図1-15)に対応している。
  • 一方で、ユーザーはいくつかの画面を見ているために、データベース読み込みが一般的に発生する。彼らはいくつかのリンクをクリックするか、いくつかのドキュメントを開き、そして内容を読む必要がある。これらの読み込みは、典型的にはデータベースの現在の状態を知りたい。したがって、ユーザインターフェースの画面は集約された状態に対応する。 これは非常に抽象的アイデアですので、いくつかの例を見ていこう。

Twitter

  • 最初の例では、Twitter(図1-16)を見てみよう。Twitterのデータベースに書き込むの最も一般的な方法。それは、Twitterへの入力を提供するすることで、それはつぶやくことだ。つぶやき(Tweet)は非常に簡単た:それはいくつかのテキスト、タイムスタンプ、およびつぶやいたSERのID、(おそらくま場所や写真が任意)で構成されている。ユーザはそれで、データベース書き込みが発生するイベントを生成する「Tweet」ボタンをクリックする。 図1-16。Twitterの入力:つぶやきボタン。Twitterの出力:タイムライン。
  • 出力側では、Twitterのデータベースから読み込む方法はタイムラインを表示することだ。それはあなたがフォローしているユーザーによって書かれたすべてのものを示していいる。それは非常により複雑な構造(図1-17)だ。

図1-17。データは、単純な形式で書かれている。そしてはるかに複雑な形で読み込まれる。

  • 各つぶやきのために、あなたは今、テキスト、タイムスタンプ、およびユーザIDだけでなく、ユーザーの名前、プロフィール写真、とつぶやきと結合された他の情報を持っている。また、つぶやきのリストは、あなたがフォローしているユーザーに基づいて選択され、それ自体が変更される可能性がある。
  • どのようにして、簡単な入力からより複雑な出力にできるだろうか?さて、あなたは図1-18に示すように、SQLでそれを表現してみてください。 図1-18。SQLを使用して、ツイートのタイムラインを生成する。
  • つまり、ユーザーはフォローしているすべてのユーザーを見つけ、彼らが書かれているすべてのつぶやきを見つけ、時間によってそれらを並び替えて、最新の100を取り上げる。だがこのクエリは実際にはうまくスケールしないことがわかる。Twitterが初期の頃にずっと動かないクジラ(the fail whale)であったことを覚えているだろうか?本質的に、彼らは上記のクエリのようなものを使用していたのだ。
  • ユーザーは自分のタイムラインを表示するときに、ユーザーのツイートを取得して、すべての人々を反復処理するのはあまりにも高くつく。その代わりに、Twitterは事前にユーザーのタイムラインを計算し、それは、ユーザがそれを見たときに高速に読めるようにそれをキャッシュする必要がある。そのためにシステムは、読み込みに最適化された集約(つまり、タイムライン)への書き込みに最適化されたイベント(つまり、つぶやき)から変換するプロセスを必要とする。Twitterはこのようなプロセスがあり、ファンアウト(fanout)サービスと呼ばれる。第5章で詳細にそれを説明する。

フェイスブック

  • 別の例として、Facebookを見てみましょう。 Facebookのデータベースに何かを書くことを可能にする多くのボタンがありますが、古典的なものは "好きな"ボタンです。あなたがクリックすると、6つのRaffi Krikorian:QCon San Francisco、2012年11月の「Timelines at Scale」のイベントが生成されます。 事実は非常にシンプルな構造です:(あなたのユーザIDで特定される)(動詞)のような(IDで識別される)アイテム(図1-19)。 図1-19。 Facebookの入力:「好き」ボタン。 Facebookの出力:たくさんの人が好きなタイムラインの投稿。
  • しかし、あなたが出力サイドリーディングをFacebook上で見ると、それは信じられないほど複雑です。この例では、テキストだけでなく、作者とプロフィール写真の名前でもあるFacebook投稿があります。このアップデートのような160,216人のうち3人が特に強調表示されている(Facebookがこのアップデートを気に入った人の中には、これが私が最もよく知っていると思うからだと推測している)。 6,027株と12,851のコメントがあり、そのうちのトップ4のコメントが表示されていることがわかります(ここでは明らかに何らかのコメントランキングが行われています)。等々。
  • 非常に単純なイベントを入力として受け取り、大規模で複雑でパーソナライズされた出力構造(図1-20)を生成する変換プロセスがいくつか存在する必要があります。       igure 1-20。 Facebookの投稿を見ると、何十万というイベントが集まっているかもしれません。 その1つのFacebookアップデート内のすべての情報を取得するために、データベースクエリがどのように見えるかを想像することさえできません。 Facebookがこれを全面的に効率的にクエリすることはできないでしょう.10万人を超えるユーザーはそうではありません。このようなものを構築したい場合は、賢明なキャッシュが不可欠です。

不変な事実と真実の源

  • TwitterやFacebookの例から、特定のパターンを見ることができます。ユーザーインターフェイスのボタンに対応する入力イベントは非常に簡単です。彼らは不変な事実であり、単にすべて保存することができ、真理の源として扱うことができます(図1-21)。

図1-21。ユーザーインターフェイスのボタンに対応する入力イベントは非常に簡単です。

  • Webサイトで見ることができるすべてのもの、つまりデータベースから読み込んだすべてのものを、それらの生のイベントから派生させることができます。これらの集約を生のイベントから派生させ、新しいイベントが入ったときにキャッシュを更新し、そのプロセスが完全に決定論的であるプロセスがあります。必要に応じて、最初から再実行することができます。サイトで起こったことのすべての履歴をすべてフィードする場合は、すべてのキャッシュエントリを以前と同じように再構築できます。読み込んだデータベースは、単にイベントログのキャッシュビューです。
  • 真実とキャッシュのソースの間のこのような分離についての美しい点は、キャッシュ内で、データをあなたの心のコンテンツに非正規化できることです。通常のデータベースでは、データが正常に変更された場合は、1つの場所だけを変更する必要があるため、データを正規化することをお勧めします。ノーマライゼーションは、書き込みを高速かつシンプルにしますが、読み込み時にもっと多くの作業(結合)を行う必要があることを意味します。 読み取り速度を上げるために、データを非正規化することができます。つまり、さまざまな場所で情報を複製して、より速く読み取ることができます。 2007年6月14日、blogs.msdn.com、prob7 Pat Helland: "会計士は、消しゴムを使わないでください"
  • 元のデータが変更された場合は、コピー先のすべての場所も変更する必要があります。典型的なデータベースでは、何かがコピーされた場所をすべて知っていないかもしれないので、それは悪夢です。しかし、キャッシュが反復可能なプロセスを使用して生のイベントから構築されている場合、どのデータがどこに流れるかを知っているため、非正規化する自由はずっとあります。

ウィキペディア

  • 別の例はWikipediaです。これは、Wikipediaでは入力と出力がほぼ同じであるため、TwitterやFacebookの反例に近いものです(図1-22)。 図1-22。 Wikipediaの入力:編集フォーム。 Wikipediaの出力:記事。
  • Wikipediaでページを編集すると、ページコンテンツ全体(wikiマークアップを使用)を含む大きなテキストフィールドが表示され、保存ボタンをクリックすると、ページコンテンツ全体がサーバーに返されます。サーバーは、ページ全体を投稿したものに置き換えます。誰かがページを表示すると、図1-23に示すように、同じコンテンツをユーザーに戻します(HTML形式)。

図1-23。 Wikipediaでは、入力と出力はほぼ同じです。

  • したがって、この場合、入力と出力は本質的に同じです。
  • この場合、イベントソーシングはどのような意味ですか?書き込みイベントを、ページ全体のコピーではなく、パッチファイルのような差分として表現するのはおそらく意味がありますか?考えてみると面白いケースです。 (Google Docsは、個々の文字の粒度で差分を継続的に適用することで機能し、事実上、キーストロークごとにイベントが発生します8)

LinkedIn?

  • 最後の例として、LinkedIn?について考えてみましょう。 LinkedIn?プロファイルを更新し、現在のジョブを追加するとします。このジョブは、役職、会社、および一部のテキストで構成されています。この場合も、データベースに書き込むための編集イベントは非常に簡単です(図1-24)。
  • 8 John Day-Richter:「新しいGoogleドキュメントの違い:コラボレーションを高速化する」googledrive.blogspot.com、2010年9月23日 図1-24。 LinkedIn?の入力:プロの編集。 LinkedIn?の出力:全員の検索結果を検索するエンジン。
  • このデータを読むにはさまざまな方法がありますが、この例では検索機能を見ていきます。あなたがLinkedIn?のデータベースを読むことができる1つの方法は、検索ボックスにいくつかのキーワード(そしておそらく会社名)を入力し、それらの基準に合致するすべての人物を見つけることです。
  • それはどのように実装されていますか?検索するには、本質的に大きな辞書であるフルテキストインデックスが必要です。すべてのキーワードに対して、キーワードを含むすべてのプロファイルのIDが表示されます(図1-25)。
  • 図1-25。フルテキストインデックスは、どのファイルにどのキーワードが含まれているかを要約します。プロファイルが更新されると、それに応じてインデックスを更新する必要があります。
  • この検索インデックスは別の集約構造であり、一部のデータがデータベースに書き込まれるたびに、この構造を新しいデータで更新する必要があります。
  • たとえば、私の仕事「Author at O'Reilly」を自分のプロフィールに追加すると、「author」と「o'reilly」のエントリの下にプロフィールIDを含むように検索インデックスを更新する必要があります。検索インデックスキャッシュの別の種類です。また、真実の源(これまでに起こったすべてのプロフィールの編集)から構築する必要があり、新しいイベントが発生するたびに(誰かがプロフィールを編集して)更新する必要があります。