Skip to content

顺序性 ​

MortarMQ 保证什么顺序、在哪儿不再保证,以及怎么绕着这条边界设计。

保证:单分区、追加顺序 ​

一个分区同一时刻只有一个写入者(该分区的 broker leader),一个组里也只有一个消费者拥有它。这个「单写者 / 单 owner」配对让保证变得很便宜:

一个分区内的消息,按追加顺序投递。

不需要对序号、不需要乱序缓冲 —— 日志本身就是顺序。

你用什么 key 决定了什么被排序 ​

分区选择是 fnv1a32(key) % partition_count。所以:

如果你的 key 是…那么有序的是
同一流里每个消息都用同一个业务 id(如 order_id)该实体的全部消息,全局有序 —— 因为它们都落进同一个分区
空 / 缺省轮询散开;任意两条消息之间没有顺序关系
随机或基于时间等同于空,而且还会偏斜 —— 比空更糟

规矩: 顺序是按 key 的,而按 key 的顺序之所以存在,只因为 key 把消息钉死在了一个分区上。

text
order_id = "A-1001"  ─┐
order_id = "A-1001"  ─┼─▶ 分区 2  ──▶ 严格有序
order_id = "A-1001"  ─┘
order_id = "A-1002"  ───▶ 分区 0  ──▶ 仅对 A-1002 有序

明确不保证的 ​

不保证为什么
跨分区两个分区是两条独立日志、两个独立写入者
跨 topic比跨分区离得更远
生产者用多个 key 时的先后不同 key 可能落进不同分区
消费者看到的全局顺序一个组把分区摊给多个消费者;每个消费者只对自己那几个分区有序,组整体无序
某个消费者跨 rebalance 的连续性消费者可能持有分区 0、失去它、之后又拿回来 —— 分区的顺序完好,但这个消费者的视角中间缺了一段

最后那行最让人意外:日志从不重排,但你的处理序列可能在「不拥有该分区」的那段出现空洞。

绕着边界设计 ​

  1. 先定排序范围。 「按订单号有序」→ 把 order_id 放进 key,接受不同订单之间交错。「全局有序」→ 只用一个分区,接受你已经把并行度扔掉了。
  2. 绝不在一个分区内部并发处理。 归属保证一个分区一个消费者;如果这个消费者自己往外并行,是它破坏了日志保下来的顺序。按分区串行。
  3. 别指望 rebalance 对你的处理逻辑不可见。 收到 NOTICE ASSIGN 时消费者可能从日志中段开始 —— 从 ack_floor 恢复,而不是从「我最后碰巧看到的那条」。
  4. 分区数在 DECLARE 时定死。 之后改分区数会让每个 key 重映射到不同分区,毁掉所有按 key 的顺序 —— 这正是 v0 禁止改它的原因。

怎么验证你的假设 ​

bash
# 1) 确认 topic 的分区数
mortarmq topics list
#   name    partitions  state   created_epoch
#   orders  4           active  1

# 2) 确认单个 key 没有被摊开:
#    用同一个 key 发 100 条,然后看各分区的 depth ——
#    应该只有一个分区承载了这批负载

带 topic,partition label 的 mortarmq_partition_depth 就是这件事的可观测形式:如果一个 key 的流量出现在多个分区里,说明你的 key 没有起到你以为的作用。

相关 ​