Kafka 保证消费顺序性(核心方案+原理)
Kafka 默认只保证分区内消息有序,分区之间无序,所有顺序方案都围绕分区设计。
一、基础原理
-
生产者:消息发往同一个分区,才能保证发送顺序。
-
消费者:同一个分区只能被消费者组内一个消费者消费,天然保证分区内消费有序。
-
多分区 = 全局无法有序,这是前提。
二、不同场景实现方案
-
全局严格有序(最强顺序,最常用)
方案:单分区 + 单消费者
• 配置:Topic 只建 1 个分区
• 生产者:所有消息都发往该唯一分区
• 消费者组:组内只启动1个消费实例
• 特点:全局完全有序;缺点:并发低,吞吐量受限
- 局部有序(业务分区有序,高并发)
方案:按业务维度分区(分区键)
适用:只需同业务标识消息有序,不同业务可并行。
生产者实现(指定分区/分区键)
Kafka 分区路由规则:
partition = hash(key) % 分区数
• 把需要有序的同一类消息设置相同 key,会被路由到同一个分区
• 示例:订单ID、用户ID、设备ID 作为 key
代码示意(Java)
// 同一订单所有消息用同一个 key,保证进同一分区
producer.send(new ProducerRecord<>("topic", "order_1001", 消息内容));
消费者配合
• 消费者组正常扩实例(多线程/多进程)
• 不同分区由不同消费者并行消费
• 同一个分区依旧单线程消费,保证分区内有序
结论:同订单/同用户消息有序,不同业务互不干扰,兼顾顺序与并发。
- 消费端进一步控序(解决重试/并发乱序)
即使分区有序,批量消费、异步处理、消息重试仍会乱序,配套规则:
-
关闭批量消费
不使用批量拉取+异步处理,单条串行处理。
-
单分区单线程消费
一个分区绑定一个消费线程,禁止多线程并发处理同一分区消息。
-
禁止乱序重试
◦ 方式1:使用死信队列,失败消息投递到 DLQ,不原地重试
◦ 方式2:开启重试时,顺序重试、不插队
-
关闭自动提交,手动 offset 提交
处理完一条/一批再提交位移,避免消息重复+乱序。
三、避坑点(常见乱序原因)
-
Topic 多分区,又没指定业务 key → 全局乱序
-
同一分区使用多线程并发处理消息 → 消费乱序
-
消息失败后立即重试、插队重试 → 顺序破坏
-
生产者配置 linger.ms 批量发送 + 压缩,分区内仍有序(不影响)
四、方案选型速记
-
全局绝对有序、低并发:Topic 单分区 + 消费者组单实例
-
业务维度有序、高并发:多分区 + 业务Key分区 + 分区单线程消费
-
追求高吞吐又要顺序:优先按业务分片分区(主流企业方案)
五、补充:Kafka 新版特性
• Kafka 不支持分区内并行消费;想要顺序就必须串行处理分区消息
• max.poll.records 调小,减少批量消息,降低乱序风险