SpringBoot秒杀系统
秒杀系统库存
今天带来一套秒杀库存扣减
涉及到单体架构和集群架构 希望能给你们带来帮助 我也不想学 但是bgs不教
首先讲一下大致的扣减库存的思路
@RequestMapping("deduct_stock")public CommonResult deductStock(){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("shop_stock"));if (stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("shop_stock",realStock + "");System.out.println("库存扣减成功 剩余库存"+realStock);return CommonResult.ok();}else{System.out.println("库存扣除失败 ,库存不足");return CommonResult.fail(888,"库存扣除失败 ,库存不足");}}
// 这大致就是一个扣减库存的思路 先去判断是否还有库存 然后再去扣减一个库存 替换掉原有的库存数量
// 下面开始说一下利弊
// 利:这样不会出现库存为负数的情况
// 弊:如果高并发的情况下 多用户可能会造成同时下单同一个编号的商品 (仅次于超卖)
下面说一下这种情况的解决方案
@RequestMapping("deduct_stock")public CommonResult deductStock(){synchronized (this){ // 我们在这里加上一条同步锁 当一个请求执行完毕之后 下一个请求才能进入int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("shop_stock"));if (stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("shop_stock",realStock + "");System.out.println("库存扣减成功 剩余库存"+realStock);return CommonResult.ok();}else{System.out.println("库存扣除失败 ,库存不足");return CommonResult.fail(888,"库存扣除失败 ,库存不足");}}}
// 这样虽然可以解决重复下单 解决超卖问题
// 但是也仅限于单体服务器架构
// 如果是集群架构 就避免不了同时下单的问题
那我们再次优化一下多集群架构重复下单的问题
@RequestMapping("deduct_stock")public CommonResult deductStock(){String lockKey = "lock_key";Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "lock"); //setnx分布式锁if (!result){return CommonResult.fail(999,"拥挤中-稍后重试");}else{int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("shop_stock"));if (stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("shop_stock",realStock + "");System.out.println("库存扣减成功 剩余库存"+realStock);stringRedisTemplate.delete(lockKey); //把锁删除return CommonResult.ok();}else{System.out.println("库存扣除失败 ,库存不足");stringRedisTemplate.delete(lockKey); //把锁删除return CommonResult.fail(888,"库存扣除失败 ,库存不足");}}}
// 虽然多集群不在单机 但是共用一个redis的情况下就不会出现重复下单 使用缓存中间件对其进行一个约束
// 虽然代码到这里已经可以实现了分布式锁 但是还是有问题 下面我们再次优化一下
再次优化代码
@RequestMapping("deduct_stock")public CommonResult deductStock(){String lockKey = "lock_key";// 设置一个同步锁过期时间 防止系统挂掉Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "lock",10,TimeUnit.SECONDS);if (!result){return CommonResult.fail(999,"拥挤中-稍后重试");}else{try{ // 防止中级出现异常 int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("shop_stock"));if (stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("shop_stock",realStock + "");System.out.println("库存扣减成功 剩余库存"+realStock);return CommonResult.ok();}else{System.out.println("库存扣除失败 ,库存不足");return CommonResult.fail(888,"库存扣除失败 ,库存不足");}}finally { // 不管是否有异常都必须执行stringRedisTemplate.delete(lockKey);}}}
到目前为止 代码已经比较完善了 但是还是会有部分问题 在高并发的场景下 如果自己的锁被别人释放的时候 也可能会出现重复下单问题 这个问题也就是出现在finally里面 所以我们最好还是多加一个判断 并且将唯一标识存起来
@RequestMapping("deduct_stock")public CommonResult deductStock(){String lockKey = "lock_key";String clientId = String.valueOf(UUID.randomUUID()); // 创建下单人的唯一标识Boolean result = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, clientId,10,TimeUnit.SECONDS);if (!result){return CommonResult.fail(999,"拥挤中-稍后重试");}else{try{int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("shop_stock"));if (stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("shop_stock",realStock + "");System.out.println("库存扣减成功 剩余库存"+realStock);return CommonResult.ok();}else{System.out.println("库存扣除失败 ,库存不足");return CommonResult.fail(888,"库存扣除失败 ,库存不足");}}finally {if (clientId.equals(stringRedisTemplate.opsForValue().get(lockKey))){ // 如果是这个人结束stringRedisTemplate.delete(lockKey); // 才去释放这个锁 如果不是这个人的时候 不释放这个锁}}}
当到这里的时候 代码量已经比刚开始多了很多了 并且也解决了一个又一个问题 但是在这里还是会有一个问题 当我们代码执行到finally的时候 如果前面的时间已经过了9秒 并且finally中又卡顿了1秒或者2秒 在高并发的情况下 会造成自己的锁已经超时释放了 然后finally执行之后删除了后面那个人的锁 这种问题也是会影响到订单重复的问题
这里就介绍一下Redisson了 如名 这也就是redis的son
依赖部分
<!-- https://mvnrepository.com/artifact/org.redisson/redisson --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.16.8</version></dependency>
启动类部分
@Beanpublic Redisson redisson(){Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);return (Redisson) Redisson.create(config);}
// 其他部分通过@Autowired注入就行了。。
当注入了Redisson之后 我们的代码又可以减少了
@RequestMapping("deduct_stock")public CommonResult deductStock(){String lockKey = "lock_key";// 获取锁对象RLock redissonLock = redisson.getLock(lockKey);try{//加锁redissonLock.lock();int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("shop_stock"));if (stock>0){int realStock = stock -1;stringRedisTemplate.opsForValue().set("shop_stock",realStock + "");System.out.println("库存扣减成功 剩余库存"+realStock);return CommonResult.ok();}else{System.out.println("库存扣除失败 ,库存不足");return CommonResult.fail(888,"库存扣除失败 ,库存不足");}}finally {// 解锁redissonLock.unlock();}}
下图也就是加上redisson之后的流程图
~~~~~~~~~~~~~~~~~~~~~~~ 如果看完对你有帮助 还望一键三连~~~~~~~~~~~~~~~~~~~~~~~
SpringBoot秒杀系统相关推荐
- SpringBoot + 秒杀系统
本书源码 https://github.com/huangwenyi10/spring-boot-book-v2 目录 创建 秒杀系统 雏形 小结 详情 高并发优化 流量削峰 创建 解决 Cannot ...
- springboot 秒杀系统(一)
秒杀系统应该是很检验一个人的能力的项目.包括从前端 到运营商到nginx到后端等等,很多地方可以优化. 前端的页面控制,运营商的CDN加速,nginx的动静分离等 下面我来一步一步实现后端的秒杀功能的 ...
- Springboot秒杀系统(乐观锁+RateLimiter令牌+Redis缓存)
本文主要是利用springboot,实现一个单机版秒杀demo,通过单机版实现,可以对基本并发秒杀的知识有一定的了解. 首先先提供秒杀业务实现类: /*** spring 注解加在实现类*/ @Ser ...
- springboot 秒杀系统(二)redis
上一步我们做的秒杀虽然在操作上没问题, 但性能上能有很大的提升空间. 我们可以先把秒杀数据加载到内存中,考虑到以后服务集群化, 所以加载的数据不存放在JVM中,而存在放redis 首先,我们都知道,r ...
- 商城模块java_Java商城秒杀系统实战系列~构建SpringBoot多模块项目
摘要:本篇博文是"Java秒杀系统实战系列文章"的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项 ...
- SpringBoot开发案例从0到1构建分布式秒杀系统
前言 最近,被推送了不少秒杀架构的文章,忙里偷闲自己也总结了一下互联网平台秒杀架构设计,当然也借鉴了不少同学的思路.俗话说,脱离案例讲架构都是耍流氓,最终使用SpringBoot模拟实现了部分秒杀场景 ...
- SpringBoot实现Java高并发秒杀系统之DAO层开发(一)
SpringBoot实现Java高并发秒杀系统之DAO层开发(一) 秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统( ...
- java 模块 分工_Java秒杀系统实战系列~构建SpringBoot多模块项目
摘要:本篇博文是"Java秒杀系统实战系列文章"的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项 ...
- 2019最新 Java商城秒杀系统的设计与实战视频教程(SpringBoot版)
第一章 1-1课程整体介绍.mp4 1-2核心技术列表.mp4 1-3课程要求与收益.mp4 1-4系统的整体演示.mp4 https://www.jianshu.com/writer#/notebo ...
最新文章
- c语言bim的题目,求助:几道简单C语言程序小题
- 学计算机去大工中大,厦门大学和大连理工大学你 选哪个?哪个好。厦门大学和东南大学、天津大学、武汉大学、中山大学去那个...
- oracle 取整点的数据,Oracle SQL语句操作数字:取整、四舍五入及格式化
- JSP Servlet Mysql学生成绩管理系统
- 计算机国二笔试试题,全国计算机等考试二笔试试题(2).ppt
- 使用Pycharm运行TensorFlow,Virtualenv安装TensorFlow
- 31. 连续子数组的最大和
- 使用JAVA命令查看JVM参数
- JavaScript高级程序设计学习笔记(一)
- 如何在 MAC 电脑上查找 IP 地址
- 计算机的外围设备找不到,bluetooth外围设备,教您bluetooth外围设备找不到驱动程序怎么解决...
- Golang开发环境LiteIDE的设置
- ACM中关于Output Limit Exceeded和Time Limit Exceeded
- JavaScirpt 与 ECMAScript 的关系
- Android 2020年最新保活方案 保活90% 已适配8.0 ,9.0, 10.0(酷狗音乐)
- 如何在win7 64下安装ipython notebook
- Vue2使用记录(待续)
- 我心中的linux,和我如何用GNU linux工作!【强帖,精彩,真精彩】
- 通俗易懂的科普解读:什么是量子态?什么是粒子自旋?
- HTML搜索结果显示,百度搜索结果如何显示你的网站logo及官网?
热门文章
- 大型电厂IP互联无线对讲通信解决方案
- 【Linux 内核 内存管理】物理内存组织结构 ④ ( 内存区域 zone 简介 | zone 结构体源码分析 | zone 结构体源码 )
- python实现12306验证和登录
- SQL语句中:UNION与UNION ALL的区别
- 分支策略——TBD Workflow(五)
- 法律诊所模拟仿真操作软件QY-RJ12
- mysql 根据经纬度查询规定范围内符合坐标的店铺并优化查询的sql语句
- RTS和CTS是什么意思?[转贴]
- 寒假集训 最短路(I - Heavy Transportation)dijkstra+堆优化
- Excel链接mysql语句查询_Excel表中连接sql并使用sql语句引用excel单元格数据作为查询条件...