秒杀系统库存

今天带来一套秒杀库存扣减

涉及到单体架构和集群架构 希望能给你们带来帮助 我也不想学 但是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秒杀系统相关推荐

  1. SpringBoot + 秒杀系统

    本书源码 https://github.com/huangwenyi10/spring-boot-book-v2 目录 创建 秒杀系统 雏形 小结 详情 高并发优化 流量削峰 创建 解决 Cannot ...

  2. springboot 秒杀系统(一)

    秒杀系统应该是很检验一个人的能力的项目.包括从前端 到运营商到nginx到后端等等,很多地方可以优化. 前端的页面控制,运营商的CDN加速,nginx的动静分离等 下面我来一步一步实现后端的秒杀功能的 ...

  3. Springboot秒杀系统(乐观锁+RateLimiter令牌+Redis缓存)

    本文主要是利用springboot,实现一个单机版秒杀demo,通过单机版实现,可以对基本并发秒杀的知识有一定的了解. 首先先提供秒杀业务实现类: /*** spring 注解加在实现类*/ @Ser ...

  4. springboot 秒杀系统(二)redis

    上一步我们做的秒杀虽然在操作上没问题, 但性能上能有很大的提升空间. 我们可以先把秒杀数据加载到内存中,考虑到以后服务集群化, 所以加载的数据不存放在JVM中,而存在放redis 首先,我们都知道,r ...

  5. 商城模块java_Java商城秒杀系统实战系列~构建SpringBoot多模块项目

    摘要:本篇博文是"Java秒杀系统实战系列文章"的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项 ...

  6. SpringBoot开发案例从0到1构建分布式秒杀系统

    前言 最近,被推送了不少秒杀架构的文章,忙里偷闲自己也总结了一下互联网平台秒杀架构设计,当然也借鉴了不少同学的思路.俗话说,脱离案例讲架构都是耍流氓,最终使用SpringBoot模拟实现了部分秒杀场景 ...

  7. SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

    SpringBoot实现Java高并发秒杀系统之DAO层开发(一) 秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统( ...

  8. java 模块 分工_Java秒杀系统实战系列~构建SpringBoot多模块项目

    摘要:本篇博文是"Java秒杀系统实战系列文章"的第二篇,主要分享介绍如何采用IDEA,基于SpringBoot+SpringMVC+Mybatis+分布式中间件构建一个多模块的项 ...

  9. 2019最新 Java商城秒杀系统的设计与实战视频教程(SpringBoot版)

    第一章 1-1课程整体介绍.mp4 1-2核心技术列表.mp4 1-3课程要求与收益.mp4 1-4系统的整体演示.mp4 https://www.jianshu.com/writer#/notebo ...

最新文章

  1. c语言bim的题目,求助:几道简单C语言程序小题
  2. 学计算机去大工中大,厦门大学和大连理工大学你 选哪个?哪个好。厦门大学和东南大学、天津大学、武汉大学、中山大学去那个...
  3. oracle 取整点的数据,Oracle SQL语句操作数字:取整、四舍五入及格式化
  4. JSP Servlet Mysql学生成绩管理系统
  5. 计算机国二笔试试题,全国计算机等考试二笔试试题(2).ppt
  6. 使用Pycharm运行TensorFlow,Virtualenv安装TensorFlow
  7. 31. 连续子数组的最大和
  8. 使用JAVA命令查看JVM参数
  9. JavaScript高级程序设计学习笔记(一)
  10. 如何在 MAC 电脑上查找 IP 地址
  11. 计算机的外围设备找不到,bluetooth外围设备,教您bluetooth外围设备找不到驱动程序怎么解决...
  12. Golang开发环境LiteIDE的设置
  13. ACM中关于Output Limit Exceeded和Time Limit Exceeded
  14. JavaScirpt 与 ECMAScript 的关系
  15. Android 2020年最新保活方案 保活90% 已适配8.0 ,9.0, 10.0(酷狗音乐)
  16. 如何在win7 64下安装ipython notebook
  17. Vue2使用记录(待续)
  18. 我心中的linux,和我如何用GNU linux工作!【强帖,精彩,真精彩】
  19. 通俗易懂的科普解读:什么是量子态?什么是粒子自旋?
  20. HTML搜索结果显示,百度搜索结果如何显示你的网站logo及官网?

热门文章

  1. 大型电厂IP互联无线对讲通信解决方案
  2. 【Linux 内核 内存管理】物理内存组织结构 ④ ( 内存区域 zone 简介 | zone 结构体源码分析 | zone 结构体源码 )
  3. python实现12306验证和登录
  4. SQL语句中:UNION与UNION ALL的区别
  5. 分支策略——TBD Workflow(五)
  6. 法律诊所模拟仿真操作软件QY-RJ12
  7. mysql 根据经纬度查询规定范围内符合坐标的店铺并优化查询的sql语句
  8. RTS和CTS是什么意思?[转贴]
  9. 寒假集训 最短路(I - Heavy Transportation)dijkstra+堆优化
  10. Excel链接mysql语句查询_Excel表中连接sql并使用sql语句引用excel单元格数据作为查询条件...