REDIS

Redis实战12-优惠券实现一人一单功能

本文收获在上一篇,我们已经把超卖问题解决了。接下来,我们来开发,优惠券一人一单功能。通过本文学习,您将有如下收获:1:悲观锁、乐观锁的使用场景;2:synchronized关键字,在不同位置,锁的颗粒度是不同的,怎么优化呢;3:toString方法之后,不能保证唯一,如果要保证唯一,需要在调用String的intern方法;4:对spring事务有更深入了解-解决spring事务失效一种情况;5:

本文收获

在上一篇,我们已经把超卖问题解决了。接下来,我们来开发,优惠券一人一单功能。通过本文学习,您将有如下收获:

1:悲观锁、乐观锁的使用场景;

2:synchronized关键字,在不同位置,锁的颗粒度是不同的,怎么优化呢;

3:toString方法之后,不能保证唯一,如果要保证唯一,需要在调用String的intern方法;

4:对spring事务有更深入了解-解决spring事务失效一种情况;

5:spring boot怎么开启对AspectJ的支持。

因为涉及到的知识点比较多,所以,这篇文章会比较长,但是凯哥(kaigejava)可以很负责地告诉大家,学习完本篇之后,你一定会有收获的。希望大家能耐心学完。好了,话不多少了,咱们开始学习吧~

我们来看看上一篇,解决超卖问题时候,100个优惠券领取情况:

4f2ec2177c255ff9d201de44dcdabff0.png

都是被同一个用户领取了,这肯定不符合实际业务情况。

一个用户只能抢到一个优惠券的业务逻辑:

4012be2c4cfc59347a7bdc788dc121dd.png


我们在原有业务中,订单入库之前,添加一人一单相关代码逻辑:

aa3a230f3e76d27b395aa55c8c5d9a73.png

我们同样使用JMeter并发跑下试试:

a69f0f7cbe264de3668b1178c49e2342.png

设置登录状态请求头是一个用户的

114ed1ed43107338157d8bba6266dab4.png

我们,来看看执行结果:

e3ba334fda9660bad16c224288ca0caf.png

异常率是95%。这不对啊,95%,意味着有10个成功的,不是一人一单吗?怎么这一人10单呢?

我们看看数据库中库存情况:

60a5eea69a2456657f0c24315b45b654.png

再来看看订单:

991a860b791d18baea3c23d25a427229.png

果然是10个单子。这个不符合我们实际业务情况啊。出现了一人多单的情况了。

是什么原因导致的呢?

其实和超卖情况是一样的,先查询,再判断。当多线程过来的时候,依然会出现多个线程竞争同一个资源并发安全问题。通过超卖问题,我们知道,可以通过加锁方法来解决。

那么是加乐观锁还是加悲观锁呢?

我们需要知道乐观锁和悲观锁使用的场景:

乐观锁:更新数据的时候,可以使用

悲观锁:插入数据的时候。

那么,在我们这个一人一单场景下,是用乐观锁还是用悲观锁呢?应该用悲观锁。为什么呢?因为,我们查询的是数据是否存在。而不是更新数据的。

我们还需要分析,悲观锁代码块的添加范围是什么?悲观锁代码块范围应该是,查询是否已经抢到过优惠券、扣除库存以及优惠券订单入库这些逻辑都应该被悲观锁锁管理。

所以,我们就来对相关代码做抽取后进行封装:悲观锁,我们使用synchronized关键字来加锁。

如下图:

c45245b3922898a87317ddb641c6f013.png

我们将锁直接加到方法上,可以吗?我们需要知道,如果我们在方法上加锁的话,

会存在以下问题:

1:锁对象就是this.当前类对象。锁的粒度很大

2:整个方法都被锁住了。所有调用这个方法的线程,都要排队等候,前面线程释放锁之后,才可以继续操作。这就将并行强制转成串行了

3:我们其实是想处理的,同一个用户多下单

原创不易,完成人机校验,阅读全文

相关推荐