1 什么是幂等性
用户对于同一操作发起的一次请求或者多次请求的结果是一致的。
比如数据库的乐观锁,在执行更新操作前,先去数据库查询version,然后执行更新语句,以version作为条件,如果执行更新时有其他人先更新了这张表的数据,那么这个条件就不生效了,也就不会执行操作了,通过这种乐观锁的机制来保障幂等性.
2 Con幂等性
2.1 什么是Con幂等性
消费端实现幂等性,就意味着,我们的消息永远不会消费多次,即使我们收到了多条一样的消息。
在业务高峰期最容易产生消息重复消费问题,当Con消费完消息时,在给Pro返回ack时由于网络中断,导致Pro未收到确认信息,该条消息就会重新发送并被Con消费,但实际上该消费者已成功消费了该条消息,这就造成了重复消费.
2.2 主流幂等性实现方案
2.2.1 唯一ID+指纹码
唯一ID +指纹码机制,利用DB主键去重。
SELECT COUNT(1) FROM T_ORDER WHERE ID = 唯一ID +指纹码
- 唯一ID:业务表的主键
- 指纹码:为了区别每次正常操作的码,每次操作时生成指纹码;可以用时间戳+业务编号或者标志位(具体视业务场景而定)
- 优势
实现简单 - 弊端
高并发下有数据库写入的性能瓶颈 - 解决方案
根据ID进行分库分表算法路由
小结
首先我们需要根据消息生成一个全局唯一ID,然后还需要加上一个指纹码。这个指纹码它并不一定是系统去生成的,而是一些外部的规则或者内部的业务规则去拼接,它的目的就是为了保障这次操作是绝对唯一的。
将ID + 指纹码拼接好的值作为数据库主键,就可以进行去重了。即在消费消息前呢,先去数据库查询这条消息的指纹码标识是否存在,没有就执行insert操作,如果有就代表已经被消费了,就不需要管了
2.2.2 利用Redis的原子性
需要考虑的问题:
- 是否要进行数据落库,若落库,数据库和缓存如何做到原子性
- 若不落库,那么都存储到缓存中,如何设置定时同步策略
这里只提用Redis的原子性去解决MQ幂等性重复消费的问题
MQ的幂等性问题 根本在于的是生产端未正常接收ACK,可能是网络抖动、网络中断导致
可能的方案
Con在消费开始时将 ID放入到Redis的BitMap中,Pro每次生产数据时,从Redis的BitMap对应位置若不能取出ID,则生产消息发送,否则不进行消息发送。
但是有人可能会说,万一Con,ProRedis命令执行失败了怎么办,虽然又出现重复消费又出现Redis非正常执行命令的可能性极低,但是万一呢?
OK,我们可以在Redis命令执行失败时,将消息落库,每日用定时器,对这种极特殊的消息进行处理。