Redis代金卷(优惠卷)秒杀案例-单应用版

优惠卷表:优惠卷基本信息,优惠金额,使用规则  包含普通优惠卷和特价优惠卷(秒杀卷)

优惠卷的库存表:优惠卷的库存,开始抢购时间,结束抢购时间.只有特价优惠卷(秒杀卷)才需要填写这些信息

优惠卷订单表

卷的表里已经有一条普通优惠卷记录

下面首先新增一条秒杀优惠卷记录

{
  "shopId": 1,
  "title": "100元代金券",
  "subTitle": "周一至周五均可使用",
  "rules": "全场通用\\n无需预约\\n可无限叠加\\n不兑现、不找零\\n仅限堂食",
  "payValue": 8000,
  "actualValue": 10000,
  "type": 1,
  "stock": 100,
  "beginTime": "2022-01-25T10:09:17",
  "endTime": "2022-01-26T12:09:04"
}
 

返回一个订单

实现秒杀下单

就是往下面这张表中添加数据  创建订单  

这里暂时只需要添加主键id  购买的代金卷id  

对应的还需要去扣减库存

关于订单的主键

使用Redis生成全局唯一ID示例-CSDN博客

控制器

业务层

传入的优惠卷id

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Autowiredprivate ISeckillVoucherService seckillVoucherService;@Autowiredprivate RedisIdWorker redisIdWorker;@Override@Transactional(rollbackFor = Exception.class)public Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVoucher byId = seckillVoucherService.getById(voucherId);if (byId.getBeginTime().isAfter(LocalDateTime.now())) {//2.判断秒杀是否开始return Result.fail("秒杀尚未开始");}if (byId.getEndTime().isBefore(LocalDateTime.now())) {//3.判断秒杀是否结束return Result.fail("秒杀已经结束");}if (byId.getStock() < 1) {//4.判断库存是否充足return Result.fail("库存不足");}//5.扣减库存boolean success = seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).update();if(!success){return Result.fail("库存不足");}//6.创建订单VoucherOrder voucherOrder = new VoucherOrder();//6.1.订单idlong order = redisIdWorker.nextId("order");voucherOrder.setId(order);voucherOrder.setUserId(1L);//登录用户先写死voucherOrder.setVoucherId(voucherId);save(voucherOrder);return Result.ok(order);}
}

以上代码,在高并发场景下会出现超卖现象

以下用乐观锁方式解决超卖问题

用乐观锁 CAS法解决超卖问题

就是在更新时候添加个条件

当200个并发线程打进去之后

虽然解决了并发安全问题,但是出现成功率低的问题   200个线程进来  没有卖完 很多都失败了

如何解决??

修改下条件  where id=? and stock>0 即可

这样就可以解决超卖问题

以上是用JMeter并发测试的,从结果看,系统还存在一个问题,例如对方开启并发访问的工具,这样会导致所有订单都是同一个用户购买的情况,或者说一个人购买了好几单

在订单表中出现如图情况

这样的漏洞,就好比黄牛了,那么系统如何实现一人一单,就是说一个用户最多下一个订单的需求

其实很简单  只要满足user_id 和 voucher_id唯一即可

就是做个查询,该用户有没有下单

如果这样写  并发情况下还是会出现问题

因为当你去判断count>0时候可能已经有10个线程都完成了查询   在判断count的时候  都查出来等于0的情况

@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {@Autowiredprivate ISeckillVoucherService seckillVoucherService;@Autowiredprivate RedisIdWorker redisIdWorker;@Overridepublic Result seckillVoucher(Long voucherId) {//1.查询优惠券SeckillVoucher byId = seckillVoucherService.getById(voucherId);if (byId.getBeginTime().isAfter(LocalDateTime.now())) {//2.判断秒杀是否开始return Result.fail("秒杀尚未开始");}if (byId.getEndTime().isBefore(LocalDateTime.now())) {//3.判断秒杀是否结束return Result.fail("秒杀已经结束");}if (byId.getStock() < 1) {//4.判断库存是否充足return Result.fail("库存不足");}Long id = UserHolder.getUser().getId();//表示获取用户id//id.toString()实际是new了一个String对象,然后调用intern()方法,这个方法会去常量池中查找有没有这个对象,如果有就返回这个对象,如果没有就添加到常量池中,然后返回这个对象synchronized (id.toString().intern()){//如果这样调用,前面有个this 事务使用代理对象去执行的//return createVoucherOrder(voucherId);IVoucherOrderService proxy = (IVoucherOrderService)AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);}}@Transactional(rollbackFor = Exception.class)public Result createVoucherOrder(Long voucherId) {Long id = UserHolder.getUser().getId();////一人一单的查询Integer count = query().eq("user_id", id).eq("voucher_id", voucherId).count();if(count > 0){return Result.fail("您已经购买过一次了");}//5.扣减库存boolean success = seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).gt("stock", 0).update();if(!success){return Result.fail("库存不足");}//6.创建订单VoucherOrder voucherOrder = new VoucherOrder();//6.1.订单idlong order = redisIdWorker.nextId("order");voucherOrder.setId(order);voucherOrder.setUserId(id);//登录用户先写死voucherOrder.setVoucherId(voucherId);save(voucherOrder);Result.ok(order);}
}

以上方案仅适用于单机版本

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/70119.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Notepad++消除生成bak文件

设置(T) ⇒ 首选项... ⇒ 备份 ⇒ 勾选 "禁用" 勾选禁用 就不会再生成bak文件了 notepad怎么修改字符集编码格式为gbk 如图所示

物联网领域的MQTT协议,优势和应用场景

MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;作为轻量级发布/订阅协议&#xff0c;凭借其低带宽消耗、低功耗与高扩展性&#xff0c;已成为物联网通信的事实标准。其核心优势包括&#xff1a;基于TCP/IP的异步通信机制、支持QoS&#xff08;服务质量&…

Node.js与嵌入式开发:打破界限的创新结合

文章目录 一、Node.js的本质与核心优势1.1 什么是Node.js&#xff1f;1.2 嵌入式开发的范式转变 二、Node.js与嵌入式结合的四大技术路径2.1 硬件交互层2.2 物联网协议栈2.3 边缘计算架构2.4 轻量化运行时方案 三、实战案例&#xff1a;智能农业监测系统3.1 硬件配置3.2 软件架…

【物联网】ARM核常用指令(详解):数据传送、计算、位运算、比较、跳转、内存访问、CPSR/SPSR

文章目录 指令格式&#xff08;重点&#xff09;1. 立即数2. 寄存器位移 一、数据传送指令1. MOV指令2. MVN指令3. LDR指令 二、数据计算指令1. ADD指令1. SUB指令1. MUL指令 三、位运算指令1. AND指令2. ORR指令3. EOR指令4. BIC指令 四、比较指令五、跳转指令1. B/BL指令2. l…

Redis基础(二)——通用命令与五大基本数据类型

目录 一、Redis数据结构基本介绍 二、Redis通用命令 1.查看通用命令 2.KEYS&#xff1a;查看符合模板的所有key 3.DEL&#xff1a;删除指定的Key 4.lEXISTS&#xff1a;判断key是否存在 5.lEXPIRE&#xff1a;给一个key设置有效期&#xff0c;有效期到期时该key会被自…

ComfyUI工作流 参考图像生成人像手办(SDXL版)

文章目录 参考图像生成人像手办SD模型Node节点工作流程效果展示开发与应用参考图像生成人像手办 此工作流旨在实现将图像生成高精度的3D手办风格效果,通过深度学习技术完成从图像处理、模型加载、提示词优化到图像生成和超分辨率处理的一系列操作。整个流程以SDXL模型为核心,…

c语言 程序计算圆的面积(Program to find area of a circle)

给定圆的半径&#xff0c;求该圆的面积。 可以使用以下公式简单地计算圆的面积。 其中 r 是圆的半径&#xff0c;它可能是浮点数&#xff0c;因为饼图的值为 3.14 方法&#xff1a;使用给定的半径&#xff0c;使用上述公式找到面积&#xff1a;&#xff08;pi * r * r&#…

解析PHP文件路径相关常量

PHP文件路径相关常量包括以下几个常量&#xff1a; __FILE__&#xff1a;表示当前文件的绝对路径&#xff0c;包括文件名。 __DIR__&#xff1a;表示当前文件所在的目录的绝对路径&#xff0c;不包括文件名。 dirname(__FILE__)&#xff1a;等同于__DIR__&#xff0c;表示当前…

蓝桥杯C语言组:暴力破解

基于C语言的暴力破解方法详解 暴力破解是一种通过穷举所有可能的解来找到正确答案的算法思想。在C语言中&#xff0c;暴力破解通常用于解决那些问题规模较小、解的范围有限的问题。虽然暴力破解的效率通常较低&#xff0c;但它是一种简单直接的方法&#xff0c;适用于一些简单…

基于STM32的智能安防监控系统

1. 引言 随着物联网技术的普及&#xff0c;智能安防系统在家庭与工业场景中的应用日益广泛。本文设计了一款基于STM32的智能安防监控系统&#xff0c;集成人体感应、环境异常检测、图像识别与云端联动功能&#xff0c;支持实时报警、远程监控与数据回溯。该系统采用边缘计算与…

【Linux系统】CPU指令集 和 Linux系统权限 ring 0 / ring 3

CPU 指令集 CPU 指令集&#xff1a;是 CPU 实现软件指挥硬件执行的媒介&#xff0c;具体来说每一条汇编语句都对应了一条CPU指令&#xff0c;而非常非常多的 CPU 指令在一起&#xff0c;可以组成一个、甚至多个集合&#xff0c;指令的集合叫CPU指令集。 CPU 指令集有权限分级&…

Slint的学习

Slint是什么 Slint是一个跨平台的UI工具包&#xff0c;支持windows,linux,android,ios,web&#xff0c;可以用它来构建申明式UI,后端代码支持rust,c,python,nodejs等语言。 开源地址&#xff1a;https://github.com/slint-ui/slint 镜像地址&#xff1a;https://kkgithub.com/…

互联网行业常用12个数据分析指标和八大模型

本文目录 前言 一、互联网线上业务数据分析的12个指标 1. 用户数据&#xff08;4个&#xff09; (1) 存量&#xff08;DAU/MAU&#xff09; (2) 新增用户 (3) 健康程度&#xff08;留存率&#xff09; (4) 渠道来源 2. 用户行为数据&#xff08;4个&#xff09; (1) 次数/频率…

九. Redis 持久化-RDB(详细讲解说明,一个配置一个说明分析,步步讲解到位)

九. Redis 持久化-RDB(详细讲解说明&#xff0c;一个配置一个说明分析&#xff0c;步步讲解到位) 文章目录 九. Redis 持久化-RDB(详细讲解说明&#xff0c;一个配置一个说明分析&#xff0c;步步讲解到位)1. RDB 概述2. RDB 持久化执行流程3. RDB 的详细配置4. RDB 备份&恢…

[权限提升] Windows 提权 维持 — 系统错误配置提权 - Trusted Service Paths 提权

关注这个专栏的其他相关笔记&#xff1a;[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01&#xff1a;Trusted Service Paths 提权原理 Windows 的服务通常都是以 System 权限运行的&#xff0c;所以系统在解析服务的可执行文件路径中的空格的时候也会以 System 权限进行解析&a…

通信易懂唠唠SOME/IP——SOME/IP-SD服务发现阶段和应答行为

一 SOME/IP-SD服务发现阶划分 服务发现应该包含3个阶段 1.1 Initial Wait Phase初始等待阶段 初始等待阶段的作用 初始等待阶段是服务发现过程中的一个阶段。在这个阶段&#xff0c;服务发现模块等待服务实例的相关条件满足&#xff0c;以便继续后续的发现和注册过程。 对…

【python】python基于机器学习与数据分析的手机特性关联与分类预测(源码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、测试技术。 python基于机器学习与数据分析的手机特性关联与分类…

测试csdn图片发布

测试csdn图片发布 ​​

JVM监控和管理工具

基础故障处理工具 jps jps(JVM Process Status Tool)&#xff1a;Java虚拟机进程状态工具 功能 1&#xff1a;列出正在运行的虚拟机进程 2&#xff1a;显示虚拟机执行主类(main()方法所在的类) 3&#xff1a;显示进程ID(PID&#xff0c;Process Identifier) 命令格式 jps […

51单片机 06 定时器

51 单片机的定时器属于单片机的内部资源&#xff0c;其电路的连接和运转均在单片机内部完成。 作用&#xff1a;1、用于计时&#xff1b;2、替代长时间的Delay&#xff0c;提高CPU 运行效率和处理速度。 定时器个数&#xff1a;3个&#xff08;T0、T1、T2&#xff09;&#xf…