Appearance
消息的重复消费
1. 幂等性
Note
幂等(idempotent、idempotence),是一个数学与计算机学概念,常见于抽象代数中。在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,
setTrue()函数就是一个幂等函数,无论多次执行,其结果都是一样的,更复杂的操作幂等保证是利用唯一交易号(流水号)实现。
简单来说,幂等性就是一个数据或者一个请求,给你重复来了多次,你得确保对应的数据是不会改变的,不能出错。
2. 重复消费场景
首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题。因为这个问题通常不是由 MQ 来保证的,而是消费方自己来保证的。
举例 Kafka 来说明重复消费问题。
Kafka 有一个叫做 offset 的概念,就是每个消息写进去,都有一个 offset 代表他的序号,然后 consumer 消费了数据之后,每隔一段时间,会把自己消费过的消息的 offset 提交一下,代表我已经消费过了,下次就算重启,Kafka 就会让消费者从上次消费到的 offset 来继续消费。
但是万事总有例外,如果 consumer 消费了数据,还没来得及发送自己已经消费的消息的 offset 就挂了,那么重启之后就会收到重复的数据。
Kafka 重复消费示意图:

3. 保证幂等性(重复消费)
要保证消息的幂等性,这个要结合业务的类型来进行处理。
下面提供几个思路供参考:
- 如果是写 Redis 那没有问题,每次都是 set,天然的幂等性;
- 让生产者发送消息时,每条消息加一个全局的唯一 id,然后消费时,将该 id 保存到 Redis 里面。消费时先去 Redis 里面查一下有么有,没有再消费(只能保证在某段时间内的幂等性,比如:历史缓存中已过期的消息仍可能出现被重复消费的情况);
- 如何要写数据库,可以拿唯一键先去数据库查询一下,如果不存在再写,如果存在直接更新或者丢弃消息;
- 数据库操作可以设置唯一键,防止重复数据的插入,这样插入只会报错而不会插入重复数据;
可在内存中维护一个 set,只要从消息队列里面获取到一个消息,先查询这个消息在不在 set 里面,如果在表示已消费过,直接丢弃;如果不在,则在消费后将其加入 set 当中;