MakingSenseofStreamProcessing / Why Kafka?


MakingSenseofStreamProcessing

なぜKafka?(Why Kafka?)

要約

詳細

  • Kafkaはウェブサーバーのログ、ユーザーのクリックイベントなどの高容量アックティビティイベントを転送するものとしてよく知られている。通常このようなイベントは、一定の期間(例えば、数日間)のために保持した後、破棄または長期保存のためにアーカイブされる。Kafkaは本当にデータベース変更イベントにフィットするのだろうか?データベースのデータを廃棄したくはありません!
  • 実際には、Kafkaは完璧にフィットする。それはこの目的のために綿密に設計されたのログ圧縮機能があるからだ。(図3-4)

14 “Apache Avro,” Apache Software Foundation, avro.apache.org. 15 “HDFS Connector,” Confluent Platform 2.0.0 documentation, docs.confluent.io, December 2015.

  • 図3-4。Kafkaのログ圧縮は、バックグラウンドでストリームを書き換える:同じキーを持つメッセージがある場合は、最新の1件のみが保持され、古いメッセージは破棄される。
  • ログ圧縮を有効にした場合、データの時間ベースでの有効期限はない。その代わりに、すべてのメッセージはキーを持っており、無期限にKafkaは与えられたキーの最新のメッセージを保持する。指定されたキー以前のメッセージは、最終的にはガベージコレクトされる。これは、キーバリューのストア内の古い値を新しい値に上書きするのと非常に類似しており、- - 基本的にはログ構造化ストレージエンジンを使用するのと同様の手法だ。(図2-17)
  • ミネラルウォーターは、Postgresの中の各テーブルの主キー(またはレプリカ)で特定し、Kafkaに送信するメッセージのキーとして使用する。メッセージの値は、イベントの種類(図3-5)に依存する。
  • 挿入と更新のために、メッセージの値はAvroとしてエンコードされ、行のフィールドのすべて含まれている。
  • 削除するためには、メッセージの値はnullに設定する。これはKafkaにログ圧縮中にメッセージを削除させる原因となる。それでそのディスク領域が解放される。
  • 図3-5。Postgresの概念とミネラルウォーターがKafkaでそれらを表現方法。
  • Postgresの中の各テーブルはKafka内の別のトピックに送信される。必ずしもそのようにする必要はないが、このアプローチはログ圧縮に最高の仕事を指せる。:SQLでは主キーはテーブルの行を一意に識別するが、Kafkaではメッセージキーはトピックのにログ圧縮の単位を定義する。(主キーまたはレプリカのキーを有しないテーブルは、現在のところロジカルでコーディングによってサポートされていない。Postgresの将来のバージョンで解決されることを望む)
  • ログ圧縮の素晴らしいところは、それがデータベースの初期スナップショットと進行中の変化の流れとの間の区別をあいまいにすることだ。ミネラルウォーターは、データベース内のすべての単一の行を、主キーでキー化され、Kafkaブローカーへ送られるメッセージの中に調整することによって、初期スナップショットをKafkaに書き込む。スナップショットが完了すると、挿入、更新、または削除されたすべての行は、同様に、メッセージに変わる。
  • 行が頻繁に更新された場合、各アップデートがメッセージになるので、同じキーを持つ多くのメッセージがあるだろう。幸いにも、
  • Kafkaのログ圧縮は、これを整理し、我々はディスクスペースを無駄にしないように、古い値をガベージコレクトする。行が決して更新または削除されない場合には、それだけで永遠にそれはガベージコレクトされることはなく、Kafkaで変わらない。
  • これはログ圧縮によって、データベースに存在するすべての行が、Kafkaに存在することを意味する。データベースに上書きされるか削除された後にのみKafkaから削除される。言い換えると、Kafkaのトピックは、データベース全体の完全なコピーが含まれている。(図3-6)
  • 図3-6。ログ圧縮を有効にすると、同じキーを持つ別のメッセージで上書きされている場合にのみKafkaがメッセージを削除します。それ以外の場合はそれは維持される。
  • 同じシステム(Kafka)で完全なデータベース・ダンプとリアルタイムストリームを持つことはとても強力だ。なぜなら、それはKafkaのログからその内容をロードすることによって、新たなコンシューマを起動するすることができるからだ。
  • たとえば、ミネラルウォーターを使用して、データベースをKafkaに供給し、そのKafkaのトピックを消費して維持される検索インデックスを持っていると仮定する。今現在インデックスに登録されていない新しいフィールドに対して検索をサポートする必要のある、新しいアプリケーション機能に取り組んでいると仮定する。
  • 伝統的なセットアップでは、何らかの形で新しいフィールドでそれらをすべてのドキュメントを徹して、インデックスを再作成する必要がある。新しいデータを古いデータで上書きしてしまう可能性があるため、ライブアップデートを処理すると同時にこれを行うことは危険だ。
  • もしもログ圧縮されたKafkaのトピックに完全なデータベース・ダンプを持っている場合には、これは問題にならない。あなたただ、新しく完全に空のインデックスを作成し、トピックの最初からKafkaのコンシューマを起動する。(「offset 0」としても知られる)図3-7に示すように。
  • 図3-7。Kafkaのトピックにて新しいインデックスまたはデータのビューを構築するには、最初からトピックを消費する。
  • あなたのコンシューマはそれぞれのメッセージをトピックの順番で逐次処理し、新しいインデックス(新しいフィールドを含む)にそれを書くことで徐々に仕事がこなせる。これが起こっている間は、古いインデックスはまだそれは通常使用可能に保たれ、新しいインデックスが同時に構築されていても完全に影響させない。ユーザーの読み込みは古いインデックスによって処理される。
  • 最後に、いくらかの時間の後、新しいインデックスは、トピック(図3-8)に最新のメッセージに達する。この時点で、特別なことは何も起こらない。それは以前にやっていたのと同じく、メッセージを消費し続けているだけだ。しかし、我々は偉大なことを行っている:トピック内のすべてのデータを含む新しいインデックス、そしてデータベース内のデータのすべてを作成した!
  • 図3-8。新しいインデックスを構築する間、ユーザーは古いインデックスから読み続けることができる。新しいインデックスの準備ができたら、手漉きの時にユーザーを切り替えることができる。
  • これで、データの2つの完全なインデックスを持って並ぶ。:古いものと新しいもの、両方ともKafkaからのリアルタイム更新で最新の状態に保たれている。ユーザーは、古いインデックスから読んでいるが、すぐに新しいインデックスをテストしているとして、新しいものに古いインデックスからユーザーを切り替えることができる。しかもこのスイッチは、段々に行うことができ、何かがうまくいかない場合にはいつでも戻ることができる。古いインデックスは依然として維持されて、まだそこにありる。
  • すべてのユーザーが新しいインデックスに移動し、すべてがうまくあることを自分自身で保証した後、あなたは、古いインデックスの更新を停止してシャットダウンし、そのリソースを再利用することができる。
  • このアプローチは、大規模で危険なデータ移行を回避し、小さなステップを取る緩やかなプロセスに置き換える。常にあなたが全身進するための大きな自信を与え、各ステップで何かが間違っていた場合は、戻ることができる。このアプローチは「最小化の不可逆性」(Martin Fowler氏)、あなたは物事を壊すことなく、より速く移動し、より機敏であることを可能にする。
  • また、バグから回復するためにこの技術を使用することができる。データベースに不正なデータを書き込む、悪いアプリケーションのバージョンを展開したとする。アプリケーションがデータベースに直接書き込む伝統的なセットアップでは、これを回収することは困難だ。(ほとんどの場合、データの損失を被ることになり、バックアップから復元する)しかしながら、ログを介し、バグがログから下流にある場合は、コードのバグ修正版を使用して再度ログ内のすべてのデータを処理するだけで、説明したのと同じ技術を用いて回復することができる。誤って書き込まれたデータから再処理によって回復することが可能であることは、時にはヒューマンフォルトトレランス 18として知られている。
  • Kafkaでデータベースのコピーを維持するためのアイデアは、伝統的なエンタープライズメッセージングとその限界に精通している人々を驚かせる。実際のところ、このユースケースは、Kafkaが複製されたログを中心になぜ構築されたのかの正確な理由だ。それは大規模データの保存および配信のこの種を実現させるためだ。下流のシステムは、低遅延のクエリを提供している上流のデータベースのパフォーマンスに影響を与えることなく自由にデータを再ロードし、再処理することができる。