CodeWalk

Kafka消息重复消费与幂等性保证

作者:苦行僧 · 2026-05-30 12:55

Kafka中为什么会出现消息重复消费(Consumer Rebalance、自动提交offset、业务处理时间过长)?如何保证消费的幂等性?请介绍至少三种幂等实现方案。

回答

苦行僧

重复消费原因

  1. Consumer Rebalance:分区重新分配时,已拉取但未提交offset的消息被重复消费
  2. 自动提交offset:enable.auto.commit=true,业务处理过程中崩溃,offset已提交但业务未完成
  3. 手动提交失败:commitSync抛出异常,下次拉取从头开始
  4. 生产者重试:producer发送消息后Broker写入成功但ack丢失,生产者重试导致消息重复

幂等性保证方案

方案1:数据库唯一约束

// 插入业务主键去重
INSERT INTO order (order_id, ...) VALUES (?, ...) 
ON DUPLICATE KEY UPDATE ...
// 或使用INSERT ... WHERE NOT EXISTS

方案2:Redis分布式锁 + 去重表

// 消费前检查是否已处理
String key = "consumer:" + msgId;
Boolean success = redisTemplate.opsForValue().setIfAbsent(key, "1", 1, TimeUnit.HOURS);
if (Boolean.TRUE.equals(success)) {
    // 执行业务逻辑
    process(message);
} else {
    // 已处理过,跳过
    log.info("消息重复,跳过: {}", msgId);
}

方案3:Kafka生产者幂等性

# 生产者配置
enable.idempotence=true
# 配合acks=all + retries>0使用
# 原理:每个Producer绑定Producer ID + 序列号,Broker去重

方案4:状态机校验

// 利用业务状态机,判断当前状态是否允许消费
// 如订单已支付,则忽略支付回调消息
switch (order.getStatus()) {
    case UNPAID: // 处理支付成功逻辑
    case PAID:   // 已支付,忽略
}

推荐:优先使用业务数据库唯一索引保证幂等,简单可靠。再配合Kafka自身幂等性生产者减少服务端重复。