基于Redisson分布式锁解决秒杀系统的“超卖”问题

2023-12-24 02:58

本文主要是介绍基于Redisson分布式锁解决秒杀系统的“超卖”问题,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

作者:钟林森,出版书籍:《分布式中间件技术实战Java版》《Spring Boot企业级项目开发-入门到精通》

Redisson,字如其名,是搭建在缓存中间件Redis基础上的一款综合中间件,除了拥有Redis本身提供的强大功能外,还提供了诸如分布式锁、分布式服务、延迟队列、远程调用等强大的功能。

从名字上就可以看出来:Redis + son,犹如Redis的儿子,儿子不仅继承了老爸强大的血脉,而且还自己修炼、发展出了属于自己的一套本领…..

Redisson开源地址为https://github.com/redisson/redisson/wiki/目录

△ Redisson官网首页截图

本文我们将使用中间件Redisson中强大的功能组件“分布式锁”,来解决高并发下多线程导致的超卖以及重复秒杀等安全问题。

在正文开始之前,先来解决一个问题:为什么要用Redisso,而不是Redis?

相较于原生RedisSetNX+Expire实现的分布式锁而言,Redisson的分布式锁组件可以解决原生Redis组合命令带来的一些缺陷,即在执行SetNX命令之后,在还没来得及执行Expire操作之前,如果此时Redis的服务器节点恰好出现宕机或者服务不能用的情况,那将会导致相应的Key永远存于缓存中,即处于所谓的“永久被锁死”的状态!

Redisson的分布式锁则可以很好地解决这种问题,其底层的实现机制在于:Redisson内部提供了一个监控锁的看门狗WatchDog,其作用在于Redis实例被关闭之前,不断延长锁的有效期。除此之外,Redisson还通过加锁的方法提供了leaseTime等参数来指定加锁的有效时间,即超过这个时间后“锁”便自动解开了。

下面我们将加入相关的依赖Jar及其配置,使用“分布式锁”组件,来解决秒杀过程中出现的“库存超卖”、“重复秒杀”等并发安全问题。

(1)首先,需要加入Redisson的依赖,版本号为3.8.2,如下所示:
<!--redisson-->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.8.2</version>
</dependency>

然后需要在配置文件application.properties中加入Redis服务所在的服务器节点Host、端口Port等信息,如下所示:

redis.config.host=redis://127.0.0.1:6379
spring.redis.password=
(2)紧接着,自定义注入操作组件RedissonClient

其自定义注入Bean组件的源代码如下所示:

@Configuration
public class RedissonConfig {@Autowiredprivate Environment env;@Beanpublic RedissonClient redissonClient(){Config config=new Config();config.useSingleServer().setAddress(env.getProperty("redis.config.host")).setPassword(env.getProperty("spring.redis.password"));RedissonClient client=Redisson.create(config);return client;}
}
(3)前期工作已准备完毕,接下来需要将其应用到秒杀系统中秒杀的核心业务逻辑

KillService服务类中我们开辟了一个新的处理方法,即killItem,其完整的源码如下所示:

@Autowired
private RedissonClient redissonClient;//秒杀核心业务逻辑的处理-redisson分布式锁
@Override
public Boolean killItem (Integer killId, Integer userId) throws Exception {Boolean result=false;final String lockKey=new StringBuffer().append(killId).append("-RedissonLock").toString();RLock lock=redissonClient.getLock(lockKey);try {//TODO:第一个参数30s=表示尝试获取锁,并且最大等待获取锁的时间为30s//TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS);if (cacheRes){//TODO:核心业务逻辑的处理if (itemKillSuccessMapper.countByKillUserId(killId,userId) <= 0){ItemKill itemKill=itemKillMapper.selectById(killId);if (itemKill!=null && 1==itemKill.getCanKill() && itemKill.getTotal()>0){int res=itemKillMapper.updateKillItem(killId);if (res>0){commonRecordKillSuccessInfo(itemKill,userId);result=true;}}}else{throw new Exception("redisson-您已经抢购过该商品了!");}}}finally {//TODO:释放锁lock.unlock();}return result;
}

从该源代码中,我们主要利用了Redisson分布式锁中的“可重入锁”组件,其使用过程需要经过如下几个步骤:
A.需要尝试去获取锁,其对应的代码以及注释如下所示,

//TODO:第一个参数30s=表示尝试获取分布式锁,并且最大的等待获取锁的时间为30s
//TODO:第二个参数10s=表示上锁之后,10s内操作完毕将自动释放锁
Boolean cacheRes=lock.tryLock(30,10,TimeUnit.SECONDS);

B.在获取到锁之后,即cacheRes=true,即可进入秒杀核心业务代码的执行;同时在处理完成之后,需要释放锁,如下所示:

//TODO:释放锁
lock.unlock();
(4)至此,并发多线程对于共享资源并发访问所出现的并发安全问题的代码实战已经完毕了!

接下来进入压测环节,压测之前设定待秒杀商品的可秒杀总数total=6,并设置随机可选取的用户ID列表的总数为10个,其取值为10040~10049;
在压测完成之后,理论上最好的结果是:total最终变为=0,同时item_kill_success数据库表中有 6 条秒杀成功而生成的订单记录。

此时,我们尝试将线程组中并发的线程数调整为10w,点击启动按钮,稍等片刻,观察控制台的输出信息以及item_killitem_kill_success这两个数据库表的数据变化情况,并查看其最终的记录结果,如下图所示:

对于这一结果,其实可以说是预料之中了!!!

经过反复压测、可以得出结论:Redisson的分布式锁确实可以解决多线程高并发场景中并发安全性问题。

本文详细内容在笔者录制的视频课中有更详细的介绍。感兴趣的读者可以扫码了解课程详情。

????今日特惠

《Java 架构师全套实战课》

从技术栈到企业项目实战

10+技术栈&50+应用案例&5大企业级项目实战

原价1042元,线下售价高达8000多

 今日特惠直降,仅需  ¥199  

????长按识别扫描二维码,立即秒杀

01

为什么推荐这门课?

项目驱动,解锁实战经验

课程以当前正火爆的Spring Boot为主线,将10+主流技术栈与50+应用案例结合,并精选5大企业级项目进行实战,真正将所学理论应用到实际项目中!

完善的学习路径,3大阶段逐层递进

本系列课程共3个阶段,每个阶段都具有相应的关联性,是一层嵌一层,逐层递进。

还原架构演进过程,从0到1做项目

课程并不是割裂的,而是从需求分析开始到最终运行测试的完整闭环,让你学到的不仅仅是知识点、项目案例,更是一个完整的项目从需求分析到落地实施的全流程。

上下滑动查看更多↑↑↑ 

02

谁来教你?

讲师是前阿里高级后端开发工程师钟林森。

畅销书《分布式中间件技术实战(Java版)》《Spring Boot企业级项目开发-入门到精通》作者;程序员实战基地-fightjava.com 创始人;长期扎根于一线撸码开发与系统架构设计!

03

购课福利

现在购买,你可以马上获得以下福利:

1、主讲老师社群答疑,解决学习问题!

2、源码及课件等资料,全部赠送!

3、覆盖多领域的高品质直播课,0元学!

4、互联网大厂高频面试考题,免费分享!

此外,还赠送给你 门强化学习效果的程序员精品课,仅限今日!

再次提醒您

原价 ¥1042

今日特惠,仅需199元

还等什么,立即扫码抢购

你最关心的问题

Q:如何学习?有效期多久?

A:购课后登陆「程序员学院」APP或者 CSDN 学院官网,随时可学,并且永久有效。

Q:如何领取价值500元的精品课程?

A:购课后扫码添加下方微信,获得讲师答疑服务,并领取价值500元的精品课程。

Q:学习时遇到不懂的问题怎么办?

A:遇到问题可以随时在交流群,与授课老师或者助教进行沟通。

阅读原文,低价抢课!

这篇关于基于Redisson分布式锁解决秒杀系统的“超卖”问题的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/530346

相关文章

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

qt5cored.dll报错怎么解决? 电脑qt5cored.dll文件丢失修复技巧

《qt5cored.dll报错怎么解决?电脑qt5cored.dll文件丢失修复技巧》在进行软件安装或运行程序时,有时会遇到由于找不到qt5core.dll,无法继续执行代码,这个问题可能是由于该文... 遇到qt5cored.dll文件错误时,可能会导致基于 Qt 开发的应用程序无法正常运行或启动。这种错

SpringBoot排查和解决JSON解析错误(400 Bad Request)的方法

《SpringBoot排查和解决JSON解析错误(400BadRequest)的方法》在开发SpringBootRESTfulAPI时,客户端与服务端的数据交互通常使用JSON格式,然而,JSON... 目录问题背景1. 问题描述2. 错误分析解决方案1. 手动重新输入jsON2. 使用工具清理JSON3.

MySQL 设置AUTO_INCREMENT 无效的问题解决

《MySQL设置AUTO_INCREMENT无效的问题解决》本文主要介绍了MySQL设置AUTO_INCREMENT无效的问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录快速设置mysql的auto_increment参数一、修改 AUTO_INCREMENT 的值。

关于跨域无效的问题及解决(java后端方案)

《关于跨域无效的问题及解决(java后端方案)》:本文主要介绍关于跨域无效的问题及解决(java后端方案),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录通用后端跨域方法1、@CrossOrigin 注解2、springboot2.0 实现WebMvcConfig

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Java死锁问题解决方案及示例详解

《Java死锁问题解决方案及示例详解》死锁是指两个或多个线程因争夺资源而相互等待,导致所有线程都无法继续执行的一种状态,本文给大家详细介绍了Java死锁问题解决方案详解及实践样例,需要的朋友可以参考下... 目录1、简述死锁的四个必要条件:2、死锁示例代码3、如何检测死锁?3.1 使用 jstack3.2

解决JSONField、JsonProperty不生效的问题

《解决JSONField、JsonProperty不生效的问题》:本文主要介绍解决JSONField、JsonProperty不生效的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑... 目录jsONField、JsonProperty不生效javascript问题排查总结JSONField

github打不开的问题分析及解决

《github打不开的问题分析及解决》:本文主要介绍github打不开的问题分析及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、找到github.com域名解析的ip地址二、找到github.global.ssl.fastly.net网址解析的ip地址三