The Dabsong Conshirtoe

技術系の話を主にします。

Kafkaのパーティンション数を決める時に考慮すること

kafkaのパーティション数を選択する際に考慮する点について、kafka作者のブログにまとめられていました。

blog.confluent.io

自身の理解のために、ちょっとまとめてみようと思います。

※この記事はあくまでも個人的なまとめですので、作者の原文に一度目を通すことをオススメします。

概要

考慮する点は、ざっくりまとめると以下があります。

必要とされるスループット

パーティションを追加することは、メッセージのproducerとconsumerの並列化につながるのでスループット向上が期待できます。

なら、max(t/p, t/c)パーティションを設定する必要があります。

将来的に必要となりそうなパーティション

設定しようとしているパーティション数が現在のスループットを満たすとしても、近い将来増加するだろうなという見込み何ある場合は、多めに設定しましょう。

なぜなら、パーティションをあとで増やすと、あるキーに割り振られるパーティションが変わってしまうためです。

ここからは自分の理解ですが、例えば、key=xというメッセージと、その次にkey=yというメッセージを受け、HashPartitionerのもとではどちらもpartition=1に割り振られていたとします。

この後、パーティション数を増やすと、xが割り当てられるパーティションの値が変わってしまい、key=xのメッセージを再度受けると別のパーティションに割り当てられてしまうため、key=xの次にkey=yが来る、というパーティション内で保障された順序同一性が崩れてしまう、ということでしょう。

kafkaの管理するファイルディスクリプタの数

kafkaは、パーティション1つにつきファイルを2つオープンする(データファイル、インデックスファイル)ので、osのファイルディスクリプタ上限を超えないように注意しましょう。

ノード障害からの復旧時間

kafkaのbrokerが停止した場合、そのbrokerがリーダーとなっていたパーティションは別のサーバーがリーダーとして振舞うようになるのですが、brokerの停止の仕方によって動きが異なります。

graceful shutdown

graceful shutdown(おそらくkafka-server-stop.shによる停止のこと)した場合、パーティション移動は一つずつ行われるので数msのダウンタイムで済む。また、ログをディスクに一旦書き出すことで再起動時のログリカバリプロセスを省くことができ、再起動を素早くできるメリットもあると公式ドキュメントにあります。

hard kill (kill -9)

強制終了した場合、停止したbrokerのパーティションはすべて同時にダウンしてしまいます。1パーティションのリーダー移動に5ms程度かかるので、1000パーティションあった場合は5秒のダウンタイムとなってしまいます。

更に停止したbrokerがcontroller(パーティションリーダーの管理を司る特別なbroker)だった場合、新しいcontrollerがZookeeperからメタデータを取得するために、2ms * パーティション数の時間が余計にかかります。

レプリケーションに伴う負荷

Kafkaはコミット(すべてのレプリケーションbrokerにメッセージが保存されること)されたメッセージのみconsumerに見せるのですが、レプリケーションはシングルスレッドで行われるので、1台のbrokerのレプリケーション数が多いほどレイテンシに影響します。

例えば、2台構成で2000パーティションある場合、片方は1000パーティションレプリケーションを行うので、結果としてだいたい20msスループットに影響します。これはサーバー台数を増やして1台あたりのレプリケーション数を削減することで軽減できます。

とはいえ、1メッセージは1パーティションにしか属さないので、1メッセージを受けた後に実行されるレプリケーションも1台あたり1回と考えられるため、レプリケーションよりも速いペースでメッセージが来ない限りは詰まらないんではとも思いました。

作者によると、パーティション数は100 * broker数 * レプリケーション数に収めるのが良いそうです。

Producerのバッファリングメッセージ

producerは毎回メッセージをbrokerに送るのではなくある程度バッファリングしてから送るのですが、このバッファリングはパーティションごとに行われるため、パーティション数を増加させるとproducerが全体でバッファリングするメッセージも増えることになります。

もし、事前に設定されたバッファリングに使えるメモリをオーバーしてしまった場合、新規の受信をブロックするかメッセージを落とすことになってしまいます。

これに関係のありそうなproducerのパラメータは以下。

  • buffer.memory:producerがバッファリングに使えるメモリ
  • block.on.buffer.full:trueの場合、バッファリングするメッセージ量がbuffer.memoryを超えたら受信をブロックする。falseなら例外投げる。
  • queue.buffering.max.messages:バッファに積める最大メッセージ量。

Sum up

いろいろ調べながら書いたので結構勉強なりました。Kafkaのトピック設計はなかなか奥が深そうですね。