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);}
}
以上方案仅适用于单机版本
相关文章:
Redis代金卷(优惠卷)秒杀案例-单应用版
优惠卷表:优惠卷基本信息,优惠金额,使用规则 包含普通优惠卷和特价优惠卷(秒杀卷) 优惠卷的库存表:优惠卷的库存,开始抢购时间,结束抢购时间.只有特价优惠卷(秒杀卷)才需要填写这些信息 优惠卷订单表 卷的表里已经有一条普通优惠卷记录 下面首先新增一条秒杀优惠卷记录 { &quo…...
51单片机 01 LED
一、点亮一个LED 在STC-ISP中单片机型号选择 STC89C52RC/LE52RC;如果没有找到hex文件(在objects文件夹下),在keil中options for target-output- 勾选 create hex file。 如果要修改编程 :重新编译-下载/编程-单片机重…...
MusicFree-开源的第三方音乐在线播放和下载工具, 支持歌单导入[对标落雪音乐]
MusicFree 链接:https://pan.xunlei.com/s/VOI0RrVLTTWE9kkpt0U7ofGBA1?pwd4ei6#...
消息队列篇--原理篇--常见消息队列总结(RabbitMQ,Kafka,ActiveMQ,RocketMQ,Pulsar)
1、RabbitMQ 特点: AMQP协议:RabbitMQ是基于AMQP(高级消息队列协议)构建的,支持多种消息传递模式,如发布/订阅、路由、RPC等。多语言支持:支持多种编程语言的客户端库,包括Java、P…...
nacos 配置管理、 配置热更新、 动态路由
文章目录 配置管理引入jar包添加 bootstrap.yaml 文件配置在application.yaml 中添加自定义信息nacos 配置信息 配置热更新采用第一种配置根据服务名确定配置文件根据后缀确定配置文件 动态路由DynamicRouteLoaderNacosConfigManagerRouteDefinitionWriter 路由配置 配置管理 …...
程序代码篇---Numpyassert迭代器
文章目录 前言第一部分:Numpy1. 创建数组2. 数组索引和切片3. 数组形状操作4. 数组运算5. 数学函数6. 随机数生成7. 数组排序 第二部分:assert基本语法1.condition2.error_message 示例注意事项断言的用途 第三部分:迭代器迭代器协议1.__iter…...
(笔记+作业)书生大模型实战营春节卷王班---L0G2000 Python 基础知识
学员闯关手册:https://aicarrier.feishu.cn/wiki/QtJnweAW1iFl8LkoMKGcsUS9nld 课程视频:https://www.bilibili.com/video/BV13U1VYmEUr/ 课程文档:https://github.com/InternLM/Tutorial/tree/camp4/docs/L0/Python 关卡作业:htt…...
分布式微服务系统架构第90集:现代化金融核心系统
#1.1 深化数字化转型,核心面临新挑战 1、架构侧:无法敏捷协同数字金融经营模式转型。 2、需求侧:业务需求传导低效始终困扰金融机构。 3、开发侧:创新产品上市速度低于期望。 4、运维侧:传统面向资源型监控体系难以支撑…...
浅谈Linux 权限、压缩、进程与服务
概述 放假回家,对Linux系统的一些知识进行重新的整理,做到温故而知新,对用户权限管理、文件赋权、压缩文件、进程与服务的知识进行了一次梳理和总结。 权限管理 Linux最基础的权限是用户和文件,先了解基础的用户权限和文件权限…...
SpringBoot中Excel表的导入、导出功能的实现
文章目录 一、easyExcel简介二、Excel表的导出2.1 添加 Maven 依赖2.2 创建导出数据的实体类4. 编写导出接口5. 前端代码6. 实现效果 三、excel表的导出1. Excel表导入的整体流程1.1 配置文件存储路径 2. 前端实现2.1 文件上传组件 2.2 文件上传逻辑3. 后端实现3.1 文件上传接口…...
JavaScript前后端交互-AJAX/fetch
摘自千峰教育kerwin的js教程 AJAX 1、AJAX 的优势 不需要插件的支持,原生 js 就可以使用用户体验好(不需要刷新页面就可以更新数据)减轻服务端和带宽的负担缺点: 搜索引擎的支持度不够,因为数据都不在页面上…...
动态规划DP 背包问题 完全背包问题(题目分析+C++完整代码)
概览检索 动态规划DP 概览(点击链接跳转) 动态规划DP 背包问题 概览(点击链接跳转) 完全背包问题 原题链接 AcWiing 3. 完全背包问题 题目描述 有 N种物品和一个容量是 V的背包,每种物品都有无限件可用。 第 i种物…...
【cocos creator】【模拟经营】餐厅经营demo
下载:【cocos creator】模拟经营餐厅经营...
JavaScript系列(52)--编译优化技术详解
JavaScript编译优化技术详解 🚀 今天,让我们深入探讨JavaScript的编译优化技术。通过理解和应用这些技术,我们可以显著提升JavaScript代码的执行效率。 编译优化基础概念 🌟 💡 小知识:JavaScript引擎通常…...
【深度学习】softmax回归的从零开始实现
softmax回归的从零开始实现 (就像我们从零开始实现线性回归一样,)我们认为softmax回归也是重要的基础,因此(应该知道实现softmax回归的细节)。 本节我们将使用Fashion-MNIST数据集,并设置数据迭代器的批量大小为256。 import torch from IP…...
【Redis】set 和 zset 类型的介绍和常用命令
1. set 1.1 介绍 set 类型和 list 不同的是,存储的元素是无序的,并且元素不允许重复,Redis 除了支持集合内的增删查改操作,还支持多个集合取交集,并集,差集 1.2 常用命令 命令 介绍 时间复杂度 sadd …...
程序诗篇里的灵动笔触:指针绘就数据的梦幻蓝图<3>
大家好啊,我是小象٩(๑ω๑)۶ 我的博客:Xiao Xiangζั͡ޓއއ 很高兴见到大家,希望能够和大家一起交流学习,共同进步。 今天我们来对上一节做一些小补充,了解学习一下assert断言,指针的使用和传址调用…...
代码随想录-训练营-day16
530. 二叉搜索树的最小绝对差 - 力扣(LeetCode) /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode…...
Vue 3 中的父子组件传值:详细示例与解析
在 Vue 3 中,父子组件之间的数据传递是一个常见的需求。父组件可以通过 props 将数据传递给子组件,而子组件可以通过 defineProps 接收这些数据。本文将详细介绍父子组件传值的使用方法,并通过优化后的代码示例演示如何实现。 1. 父子组件传值…...
谈谈你所了解的AR技术吧!
深入探讨 AR 技术的原理与应用 在科技飞速发展的今天,AR(增强现实)技术已经悄然改变了我们与周围世界互动的方式。你是否曾想象过如何能够通过手机屏幕与虚拟物体进行实时互动?在这篇文章中,我们将深入探讨AR技术的原…...
神经网络的数据流动过程(张量的转换和输出)
文章目录 1、文本从输入到输出,经历了什么?2、数据流动过程是张量,如何知道张量表达的文本内容?3、词转为张量、张量转为词是唯一的吗?为什么?4、如何保证词张量的质量和合理性5、总结 🍃作者介…...
爬取鲜花网站数据
待爬取网页: 代码: import requestsfrom lxml import etree import pandas as pdfrom lxml import html import xlwturl "https://www.haohua.com/xianhua/"header {"accept":"image/avif,image/webp,image/apng,image/sv…...
vue框架技术相关概述以及前端框架整合
vue框架技术概述及前端框架整合 1 node.js 介绍:什么是node.js Node.js就是运行在服务端的JavaScript。 Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎。 作用 1 运行java需要安装JDK,而Node.js是JavaScript的运行环…...
深入解析 C++ 字符串处理:提取和分割的多种方法
在 C 编程中,字符串处理是一个常见的任务,尤其是在需要从字符串中提取特定数据时。本文将详细探讨如何使用 C 标准库中的工具(如 std::istringstream 和 std::string 的成员函数)来提取和分割字符串,并分析不同方法的适…...
数据结构 树2
文章目录 前言 一,二叉搜索树的高度 二,广度优先VS深度优先 三,广度优先的代码实现 四,深度优先代码实现 五,判断是否为二叉搜索树 六,删除一个节点 七,二叉收索树的中序后续节点 总结 …...
NeetCode刷题第19天(2025.1.31)
文章目录 099 Maximum Product Subarray 最大乘积子数组100 Word Break 断字101 Longest Increasing Subsequence 最长递增的子序列102 Maximum Product Subarray 最大乘积子数组103 Partition Equal Subset Sum 分区等于子集和104 Unique Paths 唯一路径105 Longest Common Su…...
Google Chrome-便携增强版[解压即用]
Google Chrome-便携增强版 链接:https://pan.xunlei.com/s/VOI0OyrhUx3biEbFgJyLl-Z8A1?pwdf5qa# a 特点描述 √ 无升级、便携式、绿色免安装,即可以覆盖更新又能解压使用! √ 此增强版,支持右键解压使用 √ 加入Chrome增强…...
[EAI-027] RDT-1B,目前最大的用于机器人双臂操作的机器人基础模型
Paper Card 论文标题:RDT-1B: a Diffusion Foundation Model for Bimanual Manipulation 论文作者:Songming Liu, Lingxuan Wu, Bangguo Li, Hengkai Tan, Huayu Chen, Zhengyi Wang, Ke Xu, Hang Su, Jun Zhu 论文链接:https://arxiv.org/ab…...
什么是Rust?它有什么特点?为什么要学习Rust?
什么是Rust?它有什么特点?为什么要学习Rust? 如果你是一名编程初学者,或者已经有一些编程经验但对Rust感兴趣,那么这篇文章就是为你准备的!我们将用简单易懂的语言,带你了解Rust是什么、它有什…...
[EAI-028] Diffusion-VLA,能够进行多模态推理和机器人动作预测的VLA模型
Paper Card 论文标题:Diffusion-VLA: Scaling Robot Foundation Models via Unified Diffusion and Autoregression 论文作者:Junjie Wen, Minjie Zhu, Yichen Zhu, Zhibin Tang, Jinming Li, Zhongyi Zhou, Chengmeng Li, Xiaoyu Liu, Yaxin Peng, Chao…...
