Kafkaへのメッセージ送信の一部としてキーが必要ですか?


93
KeyedMessage<String, byte[]> keyedMessage = new KeyedMessage<String, byte[]>(request.getRequestTopicName(), SerializationUtils.serialize(message)); 
producer.send(keyedMessage);

現在、キー付きメッセージの一部として、キーなしのメッセージを送信していますが、それでも動作しdelete.retention.msますか?メッセージの一部としてキーを送信する必要がありますか?メッセージの一部としてキーを作成するのは良いことですか?

回答:


172

キーは、キーに強い順序が必要で、ステートマシンのようなものを開発している場合に、ほとんどの場合に役立ちます。同じキー(一意のIDなど)を持つメッセージを常に正しい順序で表示する必要がある場合、メッセージにキーをアタッチすると、同じキーを持つメッセージが常にトピック内の同じパーティションに送られます。Kafkaは、パーティション内の順序を保証しますが、トピック内のパーティション間は保証しません。そのため、代わりにキーを提供しないと、パーティション間でラウンドロビン分散が行われ、そのような順序は維持されません。

ステートマシンの場合、log.cleaner.enableでキーを使用して、同じキーを持つエントリを重複排除できます。その場合、Kafkaはアプリケーションが特定のキーの最新のインスタンスのみを対象とすると想定し、キーがnullでない場合にのみ、ログクリーナーが特定のキーの古い重複を削除します。この形式のログ圧縮は、log.cleaner.delete.retentionプロパティによって制御され、キーが必要です。

または、デフォルトで有効になっているより一般的なプロパティlog.retention.hoursは古くなったログの完全なセグメントを削除することで機能します。この場合、キーを提供する必要はありません。Kafkaは、指定された保持期間より古いログのチャンクを削除するだけです。

つまり、ログの圧縮を有効にした場合、または同じキーのメッセージに対して厳密な順序を要求した場合は、キーを使用する必要があります。それ以外の場合は、一部のキーが他よりも多く表示される場合に、nullキーを使用すると、配布が改善され、潜在的なホットスポットの問題が回避されます。


私は多くの質問をする理由であるカフカに新しいです:これにはいくつかの質問があります:最初の質問、キーベースでメッセージを消費できますか?現在、MessagAndMetadata mmからのメッセージを消費しています。またはメッセージを消費するときにキーを無視しても問題ありません。私はhig Level Consumer Apiを使用しています。
gaurav 2015

1
@kuujoこの重複除外はログエントリのみを対象としていると思いますが、必ずしもトピックキューのメッセージが重複除外されるわけではありませんか?
user1658296 2016年

2
@oblivionは、メッセージを同じパーティションに順番に入れることは、重要でない更新を処理するために重要です。たとえば、顧客は配信日を選択しますが(1つのメッセージ)、後で気が変わります(2番目のメッセージ)。メッセージが異なるパーティションに送られる場合、どちらのメッセージも最初/最後に処理されます。たとえば、各パーティションから2つのコンシューマーが消費します。同じ配信に関連する両方のメッセージが同じパーティションに入る場合、それらは先入れ先出しで処理され、正しい最終配信日が与えられます。
Kunal

3
順序の保証は、キーからではなく、同じパーティションにあるメッセージから行われます。メッセージのパーティションへのルーティングは、キーベースである必要はありません。作成時にパーティションを明示的に指定できますProducerRecord
Malt

2
私の理解では、プロデューサークライアントはパーティション(kafka.apache.org/documentation.html#design_loadbalancing)を選択する責任があります。パーティションはキーに基づいていてもいなくてもかまいません。では、注文にキーが必要だと言うのはなぜですか?
lfk

5

非常に役立つ承認された回答に加えて、もう少し詳細を追加したいと思います

パーティショニング

デフォルトでは、Kafkaはメッセージのキーを使用して、書き込み先のトピックのパーティションを選択します。これは次のような方法で行われます

hash(key) % number_of_partitions

キーが提供されない場合、Kafkaはデータをランダムにラウンドロビン方式で分割します。

ご注文

与えられた回答で述べたように、Kafkaはパーティションレベルでのみメッセージの順序付けを保証しています。

2つのパーティションを持つKafkaトピックに顧客の金融トランザクションを格納するとします。メッセージは(key:value)のようになります。

null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 2, "changeInBankAccount": +100}
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": -1337}
null:{"customerId": 1, "changeInBankAccount": +200}

キーを定義していないため、2つのパーティションはおそらく次のようになります。

// partition 0
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": +200}

// partition 1
null:{"customerId": 2, "changeInBankAccount": +100}
null:{"customerId": 1, "changeInBankAccount": -1337}

そのトピックを読んでいる消費者は、アカウントの残高が特定の時間に600であると言ってしまう可能性があります。パーティション1のメッセージの前に、パーティション0のすべてのメッセージを読み取っていたからです。

意味のあるキー(customerIdなど)を使用すると、パーティショニングが次のようになるため、これを回避できます。

// partition 0
1:{"customerId": 1, "changeInBankAccount": +200}
1:{"customerId": 1, "changeInBankAccount": +200}
1:{"customerId": 1, "changeInBankAccount": -1337}
1:{"customerId": 1, "changeInBankAccount": +200}

// partition 1
2:{"customerId": 2, "changeInBankAccount": +100}

ログ圧縮

メッセージの一部としてキーがないと、トピック構成cleanup.policyをに設定できませんcompactedドキュメントによると、「ログ圧縮により、Kafkaは常に、少なくとも1つのトピックパーティションのデータのログ内の各メッセージキーの最後の既知の値を保持します。」

この便利で便利な設定は、キーがないと使用できません。

キーの使い方

実際の使用例では、Kafkaメッセージのキーは、パフォーマンスとビジネスロジックの明確さに大きな影響を与える可能性があります。

たとえば、キーはデータを分割するために自然に使用できます。特定のパーティションから読み取るようにコンシューマーを制御できるため、これは効率的なフィルターとして役立ちます。また、キーには、後続の処理の制御に役立つメッセージの実際の値に関するメタデータを含めることができます。キーは通常値よりも小さいため、値全体ではなくキーを解析する方が便利です。同時に、すべてのシリアライゼーションとスキーマ登録を、キーと一緒に値を使用して行うように適用できます。

注意として、情報を格納するために使用できるヘッダーの概念もありますドキュメントを参照してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.