具有多个分区的 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 消息顺序的主要内容,如果未能解决你的问题,请参考以下文章