みなさんこんにちは。私はウー兄弟です。 これは、「Mastering MQ Series」の第 2 部「Kafka の高パフォーマンス設計」です。前回の記事では、高性能設計の 2 つの重要な側面、つまり「Tao」として理解できるコンピューティングと IO について説明しました。同時に、カフカの高性能なデザインを俯瞰することができ、それは「芸術」として理解することができます。 図1: Kafkaの高性能設計の概要 この記事では、メッセージを保存および消費するための 8 つの高性能設計手法を 1 つずつ分析していきます。さっそく始めましょう。 1. メッセージ保存のパフォーマンス最適化方法メッセージの保存はブローカーのコア機能です。使用される最適化方法は次の 4 つです。 1. IO多重化Kafka ブローカーの場合、高いパフォーマンスを実現するために最初に考慮すべきことは、ブローカーとプロデューサーおよびコンシューマー間のメッセージ転送の問題を処理するための効率的なネットワーク通信モデルを設計することです。 まず、Kafka 2.8.0 ソース コードの SocketServer クラスにある重要なコメントを引用します。 このコメントを通じて、Kafka が非常に典型的な Reactor ネットワーク通信モデルを使用していることが実際にわかります。完全なネットワーク通信層フレームワーク図は次のとおりです。 図2: Kafkaネットワーク通信層のフレームワーク図 単純なメモリは 1 + N + M です。
IO を研究した人なら、Reactor モードが従来の IO 多重化テクノロジを使用していることは明らかです。スレッドを再利用して多数のソケット接続を処理できるため、高いパフォーマンスが保証されます。 Netty と Redis が数十万、あるいは数百万もの同時接続を実現できるのはなぜでしょうか?実際、どちらも Reactor ネットワーク通信モデルを使用しています。 2. ディスクシーケンシャル書き込みIO 多重化によるネットワーク通信が完了したら、ブローカーが次に考慮する必要があるのは、メッセージをすばやく保存する方法です。記事「Kafka ストレージ選択の秘密」では、Kafka がメッセージを保存するために「ログ ファイル」を使用することが述べられています。では、このディスク ファイル書き込み方法はどのようにして高いパフォーマンスを実現するのでしょうか?これらはすべてディスクの順次書き込みによるものです。それをどう理解すればいいのでしょうか?メッセージ キューとしての Kafka は、本質的には先入先出のキューであり、メッセージが生成されると不変になります。この秩序性と不変性により、Kafka はログ ファイルを順番に「書き込む」、つまり、ファイルの末尾にメッセージを追加するだけが可能になります。逐次書きを前提として、比較実験を見てみましょう。下の図から、ディスクのシーケンシャル書き込みのパフォーマンスはディスクのランダム書き込みよりもはるかに高く、メモリのランダム書き込みよりもさらに高いことがわかります。 図3: ディスクとメモリのIO速度の比較 その理由は非常に単純です。通常の機械式ディスクの場合、ランダム書き込み、つまりファイルの特定の場所を見つけてデータを書き込むだけの場合、パフォーマンスが非常に低くなります。しかし、シーケンシャルに書き込むと、ディスクのシークとディスクの回転にかかる時間が大幅に節約されるため、パフォーマンスが 3 桁向上します。 3. ページキャッシュディスクのシーケンシャル書き込みはすでに非常に高速ですが、メモリのシーケンシャル書き込みに比べるとまだ数桁遅いです。さらに最適化することは可能ですか?答えはイエスです。 ここで Kafka はページ キャッシュ テクノロジーを使用します。簡単に言えば、オペレーティングシステム自体のキャッシュ技術を使用します。ディスク ログ ファイルの読み取りと書き込みを行う際、操作は実際にはメモリ上で実行され、その後、オペレーティング システムがページ キャッシュ内のデータを実際にディスクにフラッシュするタイミングを決定します。次の例の図はこれを明確に示しています。 図4: Kafkaのページキャッシュの原理 では、ページ キャッシュはいつ最も効果を発揮するのでしょうか?これには、ページ キャッシュで使用される 2 つの古典的な原則について言及する必要があります。ページ キャッシュは、最近アクセスされたデータが次にアクセスされる可能性が高いという原則に基づく「時間的局所性」の原則を使用して、最近使用されるディスク データをキャッシュします。ページ キャッシュに事前に読み込まれたディスク データは、データが継続的にアクセスされることが多いという事実に基づいて、「空間的局所性」の原則を利用します。メッセージ キューとして、Kafka はメッセージを順番に書き込み、コンシューマーがそれをすぐに読み取ります。これは間違いなく、上記の 2 つの局所性原則に準拠しています。そのため、ページ キャッシュは Kafka が高スループットを実現するための重要な要素の 1 つであると言えます。 さらに、ページ キャッシュにはもう 1 つの大きな利点があります。 Java を使用したことがある人なら誰でも、ページ キャッシュを使用せずに JVM プロセス内のキャッシュを使用すると、オブジェクトのメモリ オーバーヘッドが非常に大きくなる (通常は実際のデータ サイズの数倍、あるいはそれ以上になる) ことを知っています。さらに、ガベージコレクションが必要となり、GC によって発生する Stop The World 問題もパフォーマンスの問題を引き起こします。ページ キャッシュには明らかな利点があり、Kafka のコード実装が大幅に簡素化されることがわかります。 4. パーティションとセグメントの構造ディスクのシーケンシャル書き込みとページ キャッシュを組み合わせることで、ログ ファイルの高パフォーマンスな読み取りと書き込みの問題が効果的に解決されます。ただし、トピックが 1 つのログ ファイルのみに対応する場合、トピックは 1 つのブローカー マシンにのみ保存できることは明らかです。膨大な量のメッセージに直面すると、単一のマシンのストレージ容量と読み取り/書き込みパフォーマンスは確実に制限されるため、別の独創的なストレージ設計、つまりストレージ用のデータをパーティション分割することが必要になります。私の記事「Kafka アーキテクチャ設計の Ren 子午線と Du 子午線」では、パーティションの概念と役割について詳しく説明しました。パーティションは Kafka の同時処理の最小粒度であり、ストレージのスケーラビリティの問題を非常にうまく解決します。パーティションの数が増えると、Kafka のスループットがさらに向上します。実際、Kafka の最下部のストレージ レイヤーには、パーティションの下に別のレイヤー、つまり「セグメンテーション」があります。簡単に理解すると、パーティションは実際にはフォルダーに対応し、セグメントは実際のログ ファイルに対応します。 図5: Kafkaパーティションセグメントストレージ 各パーティションは複数のセグメントに分割されます。では、パーティションの後にセグメントが必要なのはなぜでしょうか? セグメントが導入されていない場合、1 つのパーティションは 1 つのファイルのみに対応し、ファイルは大きくなり続けるため、必然的に単一のパーティション ファイルが大きくなりすぎて、検索や保守が不便になります。また、履歴メッセージを削除する場合は、ファイルの以前の内容を削除する必要があります。ファイルが 1 つしかないということは、明らかに Kafka の順次書き込みの考え方に準拠していません。セグメントが導入された後は、古いセグメント ファイルを削除するだけで、各セグメントの順次書き込みが保証されます。 2. メッセージ消費のパフォーマンス最適化手法Kafka は、100 万 TPS の書き込みパフォーマンスを実現するだけでなく、高性能なメッセージ読み取りの問題も解決する必要があり、そうでなければ高スループットとは言えません。次に、Kafka がメッセージを消費するときに使用する 4 つの最適化方法を見てみましょう。 1. スパースインデックス読み取りパフォーマンスを向上させるにはどうすればよいでしょうか?インデックス作成は誰でも簡単に考えられます。 Kafka が直面するクエリ シナリオは実際には非常に単純です。オフセットまたはタイムスタンプに従ってメッセージを検索できます。 B-Tree インデックス構造を使用する場合、データが書き込まれるたびにインデックスを維持する必要があり (ランダム IO 操作)、また、「ページ分割」などの時間のかかる操作も発生します。単純なクエリ要件のみを実装する必要がある Kafka にとって、これらのコストは非常に大きくなります。したがって、B-Tree インデックスは Kafka には適していません。逆に、ハッシュ インデックスが適しているようです。読み取り操作を高速化するには、オフセットからログ ファイル オフセットへのマッピング関係をメモリ内で維持するだけでよい場合は、オフセットに基づいてメッセージを検索するたびに、ハッシュ テーブルからオフセットを取得してファイルを読み取ることができます。 (同じ考え方は、タイムスタンプに基づいてメッセージを検索するために使用できます) ただし、ハッシュ インデックスはメモリ内に常駐し、大量のデータを処理できないのは明らかです。 Kafka は 1 秒あたり数百万のメッセージを書き込む可能性があり、メモリが確実にバーストします。しかし、メッセージのオフセットは順序付けされるように設計できることがわかりました (実際には単調に増加する long 型のフィールドです)。そのため、メッセージはログ ファイル自体に順序どおりに格納されます。メッセージごとにハッシュインデックスを構築する必要はありません。メッセージを複数のブロックに分割し、各ブロックの最初のメッセージのオフセットのみをインデックスすることができます。まずサイズ関係に基づいてブロックを見つけ、次にブロック内を順番に検索します。これがKafkaの「スパースインデックス」の設計思想です。 図6: Kafkaのスパースインデックス設計 「スパース インデックス」の使用は、ディスク領域、メモリ領域、検索パフォーマンスなど、多くの面で妥協点として考えられます。スパース インデックスでは、オフセットが指定されると、Kafka はバイナリ検索を使用して、オフセットを超えない物理的な変位を効率的に特定し、ターゲット メッセージを検索します。 2. mmapスパース インデックスの使用により、効率的なクエリの問題は基本的に解決されましたが、このプロセスにはさらなる最適化の余地が残されています。つまり、上記のスパース インデックス ファイルを mmap (メモリ マップ ファイル) を介して読み書きし、メッセージのクエリ速度をさらに向上させる必要があります。
mmap を理解するにはどうすればいいですか?前述したように、従来のファイル操作では、ページ キャッシュ メカニズムを使用して読み取りと書き込みのパフォーマンスが向上します。ただし、ページ キャッシュはカーネル空間にあり、ユーザー プロセスが直接アクセスすることはできないため、ファイルを読み取るときには、ページ キャッシュ内のデータを再度ユーザー空間にコピーするためのシステム コールが必要になります。 mmap を使用すると、ディスク ファイルがプロセスの仮想アドレスにマップされ、システム コールや追加のメモリ コピー オーバーヘッドが発生しなくなるため、ファイルの読み取り効率が向上します。 図 7: mmap 図、「Coder's Desert Island Survival」より引用 mmap に関しては、私の親友 Xiaofeng が非常に人気のある記事を書きました: mmap を使用すると、プログラマーはどのようなクールな操作をアンロックできるのでしょうか?参考にしていただけます。具体的には、Kafka ソース コード レベルでは、JDK nio パッケージの MappedByteBuffer のマップ関数に基づいて、ディスク ファイルをメモリにマップします。ログファイルで mmap が使用されないのはなぜでしょうか?これは実は非常に良い質問です。コミュニティはこの質問に対して公式の回答を出していないため、インターネット上の回答では作者の意図について推測することしかできません。私は個人的に、stackoverflow のこの回答に同意します: mmap によってメモリにマップできるバイト数は、アドレス空間によって異なります。 32 ビット アーキテクチャでは、4 GB 以下のファイルしか処理できません。 Kafka ログは、一度に一部しかマップできないほど大きい場合が多く、読み取りが非常に複雑になります。ただし、インデックス ファイルはまばらで、比較的小さいです。これらをメモリにマップすると、検索プロセスが高速化されます。これがメモリ マップ ファイルによってもたらされる主な利点です。 3. ゼロコピースパース インデックスを使用してメッセージが照会された後、次のステップは、ディスク ファイルからメッセージを読み取り、ネットワーク カードを介してコンシューマーに送信することです。このステップをどのように最適化できますか? Kafka はパフォーマンスを向上させるためにゼロコピー テクノロジーを使用します。いわゆるゼロ コピーとは、アプリケーションを経由せずにディスク ファイルからネットワーク カード デバイスにデータが直接コピーされ、カーネルとユーザー モード間のコンテキスト切り替えが削減されることを意味します。 以下のプロセスは、ゼロコピー技術を使用しない場合に、ディスクからファイルを読み取り、ネットワーク カードを介して送信するプロセスです。 4 つのコピーと 4 つのコンテキスト スイッチが実行されていることがわかります。 図8: 非ゼロコピー技術のフローチャート(「艾小仙」より引用) ゼロコピー技術(sendfile メソッドを通じて最下層に実装)を使用する場合、プロセスは次のようになります。ご覧のとおり、必要なのは 3 つのコピーと 2 つのコンテキスト スイッチだけであり、明らかにパフォーマンスが向上します。 図9: ゼロコピー技術のフローチャート(「艾小仙」より引用) 4. バッチプルプロデューサーがメッセージをバッチで送信するのと同様に、メッセージ コンシューマーもメッセージをバッチでプルし、一度に 1 つのメッセージ セットをプルします。これにより、ネットワーク転送のオーバーヘッドが大幅に削減されます。さらに、Kafka の優れた高性能設計 (パート 1) で紹介したように、プロデューサーは実際にクライアント側でバッチ メッセージを圧縮します。これらのメッセージのバッチがブローカーに永続化されるとき、それらは圧縮されたままであり、最終的にコンシューマー側で解凍されます。 3. 最後に以上が、Kafka の 12 の高性能設計手法の詳細な説明です。これら 2 つの記事では、まず IO とコンピューティングという 2 つの側面からマクロなアプローチを取り、次に MQ の 1 回の送信、1 回のストレージ、1 回の消費のコンテキストに沿って、ミクロの観点から Kafka の高パフォーマンスの全体像を分析します。 Kafka は、高性能設計の教科書的な例であると言えます。 Prodcuer から Broker、そして Consumer に至るまで、細部に至るまで慎重に最適化し、最終的に単一のマシンで毎秒数十万 TPS という究極のパフォーマンスを実現しました。 最後に、この記事の分析手法が他の高性能ミドルウェアの理解に役立つことを願っています。ウー兄弟です。また次回お会いしましょう! |
<<: マジック: メモリプーリングと分散 AI クラスターの最適化
>>: おすすめする価値のある Kubernetes ダッシュボード ツール トップ 9
[51CTO.com からのオリジナル記事] モバイル ゲームには毎日数千万人のアクティブ ユーザー...
現在の企業 IT インフラストラクチャ技術の唯一の選択肢として、クラウド コンピューティングは探索と...
少し前に、「Douban では、興味関心マーケティングによって 2 億人のユーザーが 200 のブラ...
SEO 業界の人々にとって、2012 年から 2013 年への移行は、まさに苦痛を伴うものでした。経...
昨日、私が管理しているいくつかのウェブサイトのすべてのソーシャル共有ツールを baidushare ...
ローカルポータルコミュニティサイトの運営は、通常のサイトとは大きく異なります。たとえば、ローカルポー...
先月、ウェブマスターがフレンドホスティングを紹介しました。同社がアジア、特に中国でのユーザー拡大を非...
11月28日〜29日に開催された「ベイエリアインサイト2020サイバーセキュリティカンファレンス」で...
domain.com は EIG 傘下のドメイン名ブランド会社です (素晴らしい EIG をご紹介し...
WeChat のプロモーション手法は数多くあります。プロモーションを行う前に、WeChat について...
少し前に、QQグループで友人とチャットをしていました。彼はインターネットマーケティングのトレーニング...
第1四半期を振り返ると、医療美容業界は消費アップグレード路線に属し、浸透度が低く成長率が高い段階にあ...
タオバオは、現在では一般的に使用されているオンライン収益方法です。操作が簡単で、利益が直接得られます...
多くの人は、検索エンジンマーケティングを検索ランキングと同一視し、ウェブサイトのランキングが上がれば...
[おすすめ:香港の安いVPS、香港の安いVPS、香港の低価格VPS] 香港のVPSの帯域幅コストは高...