秒杀模块中常问分布式事务怎么实现、怎么防止超卖(提前预想问题)?

2021-03-09 10:00发布

7条回答

减库存可以采用同步调用,也可以采用异步调用,我们这里使用的是同步调用,为什么不用异步呢,我给你分析一下原因

如果我们采用异步调用的方式,也就说减库存这条消息发送到MQ就不管了,那如果这时候库存不足,减库存失败了,但是service的业务不会回滚,这个问题就是分布式事务问题,而且我们的购物车放在redis中,redis本身是单线程的,也用不了异步啊。

如果我们使用同步的方式调用,一个微服务执行失败就抛出异常,事务自然回滚,这里减库存的操作只能放在创建订单业务的最后,因为减库存执行失败的话事务自然回滚订单也不会创建成功,但是如果上来就先减库存,那订单创建失败的话库存也无法回滚。

那怎么解决分布式事务的问题呢?

2PC(两阶段提交),第一阶段,事务开始执行时就发送一条消息给相关的服务告诉他们,我要开始执行了,执行完以后返回一条消息,告诉这个服务业务执行成功了没有;第二阶段,如果上一阶段返回的是执行成功了,那么再发一条消息告诉所有的服务事务执行成功了,相关的事务都可以提交了,如果第一阶段失败,所有事务回滚。这种方式缺点就比较明显,实现太复杂,事务执行过程中数据锁定的范围太大了,在业务本身未执行完毕之前,数据库相关的表都是锁定状态,所以这种方式性能比较差,在高并发的业务中用的比较少。

TCC(补偿业务),这种方式的前提是面对事务都要有一套确认事务执行的业务,一套取消执行的业务。比如说减库存这个业务,确认事务就是减库存,补偿事务就是加库存,使用这种处理方式时,所有业务都开始执行,互不等待,完成了就提交,解决了两阶段提交问题中大面积数据锁定的情况,但是如果业务A提交成功,业务B提交失败的话,没关系,我们有补偿事务,这种解决方案不是靠事务回滚的方式,靠的是事务的补偿。但是也有缺点,虽然它解决了业务问题,但是它让业务变得更复杂了,写一个业务必须写一个确定业务执行方法和一个补偿业务的方法,除此之外还要考虑补偿方案的失败问题,如果补偿方案也执行失败了,这时候就要考虑重试问题,人工介入问题。

异步确保,执行时发送一条消息,另外一方接收消息,如果执行不成功就一直重试,直到执行成功。

缺点呢,事务无法回滚,不适合减库存这个业务

2PC+MQ,两阶段提交方式加异步确保


其实在电商项目中最适合的还是TCC,虽然业务变复杂了,但是行之有效,如果是转账业务的话,就比较适合异步确保,转账业务只要消息可靠就可以,执行时间晚一点也没关系,所以异步确保的关键点是消息可靠。

 

 

但是在我们这个项目中使用的是同步调用,不用把业务变得那么复杂,我们使用的是在同步调用中加锁实现的方式:

就是先查询库存,然后if判断,如果库存足够就减库存,这个逻辑是对的,但是这样做会有线程安全问题,我们是分布式项目,线程很多的时候可能会引发超卖问题。

加锁

就是synchronized,但是这种方式性能太差了,只有一个线程可以执行,当搭了集群时synchronized只锁住了当前一个tomcat,看起来是可行的,但是在分布式系统下是不安全的。

还可以使用分布式锁

zookeeper是树结构,它利用节点的唯一性来实现,加了分布式锁以后,任何一个逻辑进入到减库存这个地方,都会创建一个节点,创建成功就认为得到了锁,继续执行代码;反之则失败,返回或者wait,因此只有一个人可以拿到这个锁,执行完毕后删除节点释放锁,其他人可以再次创建锁。zookeeper可以创建临时节点,当服务器宕机或者断开连接的时候,会自动删除节点,自动释放锁,但是这种方式实现起来太复杂。

redis:setnx命令

原理类似于上述的节点,只能set不存在的key,如果不存在则创建;如果存在它会set失败,并返回0,拿到锁以后可以使用del命令释放锁。缺点是会存在搜索问题,如果setnx成功,之后执行代码,但是此时服务器宕机的话,那释放锁del的命令一直没有执行,相当于这个锁一直被别人拿着,那这个值就不会再被set成功了。

所以我们在秒杀模块中使用的是加锁,使用redis分布式锁来解决超卖。

但是购物车并不推荐加锁实现,因为用了锁就变成单线程了,相当于执行这段代码的时候把数据库锁死了,同一时刻只能有一个人来操作,这就类似于悲观锁,默认线程安全问题一定会发生,在面对高并发时性能会很差。

所以我们通过乐观锁的方式来实现,不做查询不做判断,业务执行到减库存代码这之后直接减库存,这里可能会发生超卖,为了避免这种情况,我们在sql内部加条件判断,就是在库存数大于这个num的时候库存减这个num,如果失败了就回滚,因为所有人不管怎么操作,最后都是要操作数据库的,所以我们通过这种方式解决超卖问题,本质上还是乐观锁,如果执行失败了就返回失败信息,而不像悲观锁那样让线程阻塞,一直处于等待状态。


一周热门 更多>