bigywater.tech
Kafka 内部原理 · 41

消息顺序:为什么只能保证 Partition 内有序

建一个3分区topic,用3个key各发10条严格编号的消息,验证同一个key的消息在分区内部是否严格有序,以及consumer看到的跨分区全局到达顺序是否等于发送顺序。

同一分区
user-A和user-B真实哈希碰撞到了同一个分区
先0后2
consumer真实的全局到达顺序,与发送时的交替顺序不同

验证:三个key各发10条严格递增的消息

命令 · python KEYS = ["user-A", "user-B", "user-C"] for seq in range(10): for k in KEYS: p.produce("order-demo", key=k.encode(), value=f"{k}:{seq}".encode()) p.flush() # 每一轮都flush,三个key的第seq条消息真实按接近同时的顺序发出

验证:同一个key的消息,无论如何都落在同一个分区,分区内部严格有序

真实实测 · 本机执行输出
order-demo真实的分区数: 3 [0, 1, 2]

分区0: 真实包含的key={'user-C'}, 前5条=['user-C:0', 'user-C:1', 'user-C:2', 'user-C:3', 'user-C:4']
  真实序号序列: [0,1,2,3,4,5,6,7,8,9] , 是否严格递增: True
分区1: 真实包含的key=set(), 前5条=[]
  真实序号序列: [] , 是否严格递增: True
分区2: 真实包含的key={'user-B', 'user-A'}, 前5条=['user-A:0', 'user-B:0', 'user-A:1', 'user-B:1', 'user-A:2']
  真实序号序列: [0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9] , 是否严格递增: True

真实结果比预想的更能说明问题:user-A和user-B这两个不同的key,被真实的哈希函数分到了同一个分区(分区2),而分区1完全没有收到任何消息——分区分配是按key哈希取模决定的,不是"雨露均沾"式的轮询,只要key的数量不够多、运气不好,真实就会出现哈希碰撞和空分区。分区2内部虽然混着user-A和user-B两个key的消息,但它们真实还是按照producer发送的先后顺序严格排列(每一轮的user-A排在user-B前面,因为代码里就是这么发的),这就是"分区内部有序"真实的含义:有序性是针对这个分区上追加的全体消息,不是针对某一个key单独排的队。

验证:consumer看到的全局到达顺序,完全不是三个key交替的样子

真实实测 · 本机执行输出
真实消费者看到的前15条全局到达顺序(分区号, 内容):
  (0, 'user-C:0')
  (0, 'user-C:1')
  ...(分区0的全部10条)...
  (0, 'user-C:9')
  (2, 'user-A:0')
  (2, 'user-B:0')
  (2, 'user-A:1')
  (2, 'user-B:1')
  (2, 'user-A:2')
consumer真实是先把分区0的10条全部拉取完,才开始拉分区2的——这不是Kafka承诺的顺序,只是这一次真实的网络拉取节奏恰好如此。如果分区0所在的broker当时负载高一点、分区2所在的broker响应快一点,真实的到达顺序完全可能反过来,或者两个分区交替到达。user-A:0 和 user-C:5 谁先被consumer处理到,Kafka没有做出任何保证——这也是为什么"全局时间线"这种需求没办法只靠Kafka自身的顺序性来实现,必须在消息体里真实带上业务时间戳,由消费方自己排序。

分步演示:顺序保证真实止步于分区边界

每一步都是本文上面用3分区topic跑出来的原始结果。

分区内部严格有序,跨分区顺序真实不可预测
这一步新验证的现象

    结论

    Kafka真实的顺序保证严格限定在"同一个分区内部":同一个key的消息永远落在同一个分区(因为分区号是key哈希取模算出来的),这个分区上的消息永远按追加顺序排列,consumer读这个分区也永远按这个顺序读——这条链路上每一环都真实成立。但只要涉及多个分区(不同的key,或者同一个topic的整体视角),Kafka不做任何跨分区的顺序承诺,consumer看到的到达顺序真实只取决于当时的网络和调度节奏。

    参考与说明

    • 数据来自本机3节点Kafka 4.3.1集群,topic order-demo(3分区3副本),三个key各发10条严格编号的消息。
    • 没有验证:不同key数量、不同分区数下哈希碰撞的概率分布;生产者指定分区号(而不是靠key哈希)时的行为。
    京ICP备2026067502号-1京公网安备11010502063487号