具有多个分区的 Apache Kafka 消息顺序

Posted

技术标签:

【中文标题】具有多个分区的 Apache Kafka 消息顺序【英文标题】:Apache Kafka order of messages with multiple partitions 【发布时间】:2015-07-01 10:59:26 【问题描述】:

根据 Apache Kafka 文档,消息的顺序可以在分区内或主题中的一个分区内实现。在这种情况下,我们得到的并行性好处是什么,它相当于传统的 MQ,不是吗?

【问题讨论】:

这有点像说:如果我在我的所有代码上都放置一个同步块,那么多线程的好处在哪里? 如果你进行分区,你可以充分利用 kafka 的并行性你的数据很好,你真的不需要订单。如果您的特定用例要求您使用单个分区,那么是的,您不会看到并行性的好处,但这不是 kafka 的缺点——您可能需要重新考虑您的用例。我承认,现实世界中可能存在不适合 kafka 的用例。 Kafka 是一个分布式代理。在任何分布式代理中,整体排序没有意义。如果您仍想维持整体秩序,您应该考虑重新考虑您的架构。 【参考方案1】:

在 Kafka 中,并行度等于主题的分区数。

例如,假设您的消息基于 user_id 进行分区,并考虑 4 条消息的 user_id 分别为 1、2、3 和 4。假设您有一个包含 4 个分区的“用户”主题。

由于分区是基于 user_id 的,假设 user_id 为 1 的消息将进入分区 1,用户 ID 为 2 的消息将进入分区 2,依此类推..

还假设您有 4 个该主题的消费者。由于您有 4 个消费者,Kafka 会将每个消费者分配到一个分区。所以在这种情况下,一旦推送了 4 条消息,它们就会立即被消费者消费。

如果主题有 2 个消费者而不是 4 个,那么每个消费者将处理 2 个分区,消耗的吞吐量几乎是一半。

要完整回答您的问题, Kafka 仅提供分区内消息的总顺序,而不是主题中不同分区之间的总顺序。

也就是说,如果partition 2消费很慢,partition 4消费很快,那么user_id为4的message会在user_id为2的message之前被消费。Kafka就是这样设计的。

【讨论】:

是的,@John。但是在上述情况下,不能保证消息会按照发送的顺序被接收。我指的是这个,***.com/questions/21293937/… 是的,没有这样的保证。在这种情况下,您将不得不为您的主题使用单个分区,并且您将失去并行化的能力。您可能需要重新考虑手头的问题。 @RajanR.G 我认为您应该在生成消息时正确分区消息。例如,您可以按 user_id 进行分区,然后特定 user_id 的消息将到达特定分区(始终相同),从而保证该 user_id 的所有消息将保持有序。您不需要保留不同 user_id 之间的顺序,对吧? 为什么不考虑在数据中添加时间戳。消费数据后,您可以根据时间戳对数据进行排序。尝试创建时间序列数据以保留顺序。【参考方案2】:

我决定将我的评论移至单独的答案,因为我认为这样做是有意义的。

虽然约翰对他所写的内容 100% 正确,但您可以考虑重新考虑您的问题。你真的需要所有消息来保持秩序吗?或者您是否需要特定 user_id(或其他)的所有消息才能保持有序?

如果是第一个,那就没办法了,你应该使用1个分区,失去所有的并行能力。

但是如果是第二种情况,您可能会考虑按某个键对消息进行分区,因此该键的所有消息都将到达一个分区(如果您调整主题大小,它们实际上可能会到达另一个分区,但这是另一种情况)和因此将保证该键的所有消息都是有序的。

【讨论】:

感谢您的 cmets。让我们以数据库更新为例,如果我们将消息传输到其他系统,则需要按顺序进行。在这种情况下,那么 Kafka 只能用于日志传输,不能用于任何实时事务消息系统,不是吗?否则我们必须在消费者enterpriseintegrationpatterns.com/Resequencer.html 处构建 ReSequencer。相反,我们可以使用 RabbitMQ 或其他 MQ 不是吗? 对不起,我对其他 MQ 没有任何专业知识,因为我只熟悉 Kafka。无论如何,我认为这在很大程度上取决于您打算如何处理数据。在这种情况下,您可能应该检查其他解决方案而不是 Kafka。【参考方案3】:

在 kafka 中,具有相同密钥、来自同一个生产者的消息按顺序传递给消费者

此外,分区内的数据将按照写入顺序存储,因此从分区读取的数据将按该分区的顺序读取

因此,如果您想在多个分区中按顺序获取消息,那么您确实需要使用键对消息进行分组,以便具有相同键的消息进入同一个分区,并且在该分区中消息是已订购。

简而言之,您需要在逻辑上设计一个像上面这样的两级解决方案,以使消息跨多个分区排序。

【讨论】:

【参考方案4】:

您可以考虑在源处创建数据集时有一个包含时间戳/日期的字段。

一旦数据被消费,您就可以将数据加载到数据库中。在将数据集用于任何用例之前,需要在数据库级别对数据进行排序。嗯,这是一种帮助您以多种方式思考的尝试。

假设我们有一个消息键作为创建数据时生成的时间戳,值是实际的消息字符串。

当消息被消费者拾取时,消息被写入 HBase,其中 RowKey 作为 kafka 键,值作为 kafka 值。

由于 HBase 是一个以时间戳为键的排序映射,它将自动按顺序对数据进行排序。然后,您可以为下游应用提供来自 HBase 的数据。

通过这种方式,您不会失去 kafka 的并行性。您还拥有在数据库级别对数据进行排序和执行多种处理逻辑的特权。

注意:任何分布式消息代理都不保证整体排序。如果您坚持这样做,您可能需要重新考虑使用另一个消息代理,或者您需要在 kafka 中使用单个分区,这不是一个好主意。 Kafka 通过增加分区或增加消费者组来实现并行性。

【讨论】:

如果消息接收乱序但分批消费怎么办? 如果你能再次阅读答案,我说在接收器上对数据进行排序。接收器可以是数据库。当您使用 kafka 时,根本无法保证维持秩序。 @lostsoul29 如果用户正在寻找所有订单,他不应该考虑分布式消息代理。【参考方案5】:

传统 MQ 的工作方式是,一旦消息被处理,它就会从队列中删除。消息队列允许一群订阅者从队列末尾拉出一条消息或一批消息。队列通常在拉出消息时允许某种级别的事务,以确保在消息被删除之前执行所需的操作,但是一旦处理完消息,它就会从队列中删除。

另一方面,使用 Kafka,您可以将消息/事件发布到主题,并且它们会被持久化。当消费者收到它们时,它们不会被移除。这允许您重播消息,但更重要的是,它允许大量消费者处理基于相同消息/事件的逻辑。

您仍然可以横向扩展以在同一域中获得并行处理,但更重要的是,您可以添加基于同一事件执行不同逻辑的不同类型的消费者。换句话说,使用 Kafka,您可以采用反应式发布/订阅架构。 参考:https://hackernoon.com/a-super-quick-comparison-between-kafka-and-message-queues-e69742d855a8

【讨论】:

【参考方案6】:

嗯,这是一个旧线程,但仍然相关,因此决定分享我的观点。

我觉得这个问题有点混乱。

如果您需要消息的严格排序,则在使用消息时应保持相同的严格排序。在队列中排序消息绝对没有意义,但在消费时则不然。卡夫卡允许两全其美。它允许从生成到消费对分区内的消息进行排序,同时允许多个分区之间的并行性。因此,如果您需要

在一个主题上发布的所有事件的绝对排序,使用单个分区。你不会有并行性,你也不需要(同样并行和严格的顺序不在一起)。

多partition和consumer,使用一致的hash保证所有需要遵循相对顺序的消息都到一个partition。

【讨论】:

真的很有帮助。

以上是关于具有多个分区的 Apache Kafka 消息顺序的主要内容,如果未能解决你的问题,请参考以下文章

Flink消费Kafka如何保证相同标识消息的有序性

kafka的关键特征

kafka如果有多个patition,消费消息的时候消息是没有顺序的

kafka总结

Apache Kafka 是不是适合用作无序任务队列?

Apache flink:使用keyBy / connect维护流中的消息输入顺序