海量订单产生的业务高峰期,如何避免消息的重复消费?

阿里云幸运券

幂等含义
我们先了解一下什么叫幂等?在分布式应用中,幂等是非常重要的,也就是相同条件下对一个业务的操作,不管操作多少次,结果都是一样。

由来背景
为什么要有幂等这种场景?因为在大的系统中,都是分布式部署,如:订单业务 和 库存业务有可能都是独立部署的,都是单独的服务。用户下订单,会调用到订单服务和库存服务。

海量订单产生的业务高峰期,如何避免消息的重复消费?
因为分布式部署,很有可能在调用库存服务时,因为网络等原因,订单服务调用失败,但其实库存服务已经处理完成,只是返回给订单服务处理结果时出现了异常。这个时候一般系统会作补偿方案,也就是订单服务再此放起库存服务的调用,库存减1.

update t_goods set count = count -1 where good_id=2

这样就出现了问题,其实上一次调用已经减了1,只是订单服务没有收到处理结果。现在又调用一次,又要减1,这样就不符合业务了,多扣了。

幂等这个概念就是,不管库存服务在相同条件下调用几次,处理结果都一样。这样才能保证补偿方案的可行性。

乐观锁方案
借鉴数据库的乐观锁机制,如:

update t_goods set count = count -1 , version = version + 1 where good_id=2 and version = 1

根据version版本,也就是在操作库存前先获取当前商品的version版本号,然后操作的时候带上此version号。我们梳理下,我们第一次操作库存时,得到version为1,调用库存服务version变成了2;但返回给订单服务出现了问题,订单服务又一次发起调用库存服务,当订单服务传如的version还是1,再执行上面的sql语句时,就不会执行;因为version已经变为2了,where条件就不成立。这样就保证了不管调用几次,只会真正的处理一次。

唯一ID + 指纹码
原理就是利用数据库主键去重,业务完成后插入主键标识

select count(1) from t_check where ID=唯一ID + 指纹码
唯一ID就是业务表的唯一的主键,如商品ID
指纹码就是为了区别每次正常操作的码,每次操作时生成指纹码;可以用时间戳+业务编号的方式。
上面的sql语句:

返回如果为0 表示没有操作过,那业务操作后就可以insert into t_check(唯一ID+指纹码)
返回如果大于0 表示操作过,就直接返回
好处:实现简单

坏处:高并发下数据库瓶颈

解决方案:根据ID进行分库分表进行算法路由

redis原子操作
利用redis的原子操作,做个操作完成的标记。这个性能就比较好。但会遇到一些问题。

第一:我们是否需要把业务结果进行数据落库,如果落库,关键解决的问题时数据库和redis操作如何做到原子性?

这个意思就是库存减1了,但redis进行操作完成标记时,失败了怎么办?也就是一定要保证落库和redis 要么一起成功,要么一起失败

第二:如果不进行落库,那么都存储到缓存中,如何设置定时同步策略?

这个意思就是库存减1,不落库,直接先操作redis操作完成标记,然后由另外的同步服务进行库存落库,这个就是增加了系统复杂性,而且同步策略如何设置

以上我们结束了幂等相关的解决方案,以后文章中我们会重点介绍方案的实现。

腾讯云代金券

原文链接

https://www.toutiao.com/i6666219332915167756/?group_id=6666219332915167756

服务推荐

猜你喜欢

转载自blog.csdn.net/weixin_44476888/article/details/89886479