优惠券秒杀(二)

库存超卖问题分析

库存超卖问题其本质就是多个线程操作共享数据产生的线程安全问题,即当一个线程在执行操作共享数据的多条代码的过程中,其他线程也参与了进来,导致了线程安全问题的产生。例如:线程1发送请求,查询库存,发现库存大于1,在还没来及扣除库存时,线程2甚至线程3等发送请求,发现这个数量也是大于1,那么这多个线程都会去扣除库存,最终多个线程都去扣除库存,此时就会出现库存的超卖问题。

image-20230707172612925
image-20230707172612925
  • 多线程安全问题常见的解决方案就是加锁

    • 悲观锁:认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行,synchronized、lock都属于悲观锁,属于同步锁,让线程串行执行,优点是简单粗暴,缺点是性能一般
    • 乐观锁:认为线程安全问题不一定会发生,只是在更新数据时,判断有没有其他线程对数据做了修改,优点是性能好,缺点是存在成功率问题
      • 如果没有修改,则认为是安全的,该线程进行数据更新
      • 如果已经被其他线程修改,则发生了线程安全问题,此时可以重试或异常
  • 使用乐观锁解决库存超卖问题

    • 乐观锁的关键是判断之前查询得到的数据是否被修改过,常见的方式有两种

      • 版本号法

        • 在数据库等中设置一个版本号字段,每次操作数据会对版本号进行加一操作,当每次读取数据时,读出版本号字段 ,在修改数据的时候,需要判断读出的版本号和数据库是否一致,如果一致,则表明在自己操作该数据的过程中,没有其他线程操作该数据,如果版本号不一致,则表明数据以及被别人修改过了。

          image-20230710111424101
          image-20230710111424101
      • CAS

        • 在扣减库存时,将现在库存和之前查询的库存做对比,如果一样,则说明没有其他线程在中间修改过库存数据,则认为是线程安全的,如果不一样,则说明有线程在中间修改过库存数据。

          image-20230710143222845
          image-20230710143222845

乐观锁解决超卖问题

  • 使用在扣减库存时,将现在库存和之前查询的库存做对比,如果一样,则表明没有人在此中间修改过库存,则认定线程安全,扣除库存

    @Override
    public Long seckillVoucher(Long voucherId) {

        // 查询秒杀优惠券信息
        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
        //判断秒杀是否开始和结束
        LocalDateTime beginTime = seckillVoucher.getBeginTime();
        LocalDateTime endTime = seckillVoucher.getEndTime();
        //如果当前时间 在开始时间之后 再结束时间之前 则表明秒杀能进行
        LocalDateTime localDateTime = LocalDateTime.now();
        if ( localDateTime.isBefore(beginTime) || localDateTime.isAfter(endTime) ){
            return null;
        }
        //获取库存量
        Integer stock = seckillVoucher.getStock();
        if (ObjectUtil.isNull(stock) || ObjectUtil.isNotNull(stock) && stock.intValue() <= 0){
            return null;
        }
        //扣减库存 将现在库存和之前查询的库存做对比,如果一样,则表明没有人在此中间修改过库存,则认定线程安全,扣除库存
        boolean update = seckillVoucherService.update(
                new LambdaUpdateWrapper<SeckillVoucher>().setSql("stock = stock - 1").eq(SeckillVoucher::getVoucherId, voucherId)
                        .eq(SeckillVoucher::getStock, stock)
        );
        if (!update){
            return null;
        }
        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //创建订单ID
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //获取用户ID
        Long id = UserHolder.getUser().getId();
        voucherOrder.setUserId(id);
        // 代金券id
        voucherOrder.setVoucherId(voucherId);
        this.save(voucherOrder);
        return orderId;
    }

    该方式通过测试能够发现,失败率很高,这主要是由于当多个人都拿到库存时,只有一个能够扣减成功,其他的由于.eq(SeckillVoucher::getStock, stock)无法完成扣减。

    其实在超卖问题中,我们需要保证的是在没用库存的情况下,不能再进行库存扣减,所有需要保证的是库存大于0,所有可以设置.gt(SeckillVoucher::getStock, 0)

    @Override
    public Long seckillVoucher(Long voucherId) {

        // 查询秒杀优惠券信息
        SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
        //判断秒杀是否开始和结束
        LocalDateTime beginTime = seckillVoucher.getBeginTime();
        LocalDateTime endTime = seckillVoucher.getEndTime();
        //如果当前时间 在开始时间之后 再结束时间之前 则表明秒杀能进行
        LocalDateTime localDateTime = LocalDateTime.now();
        if ( localDateTime.isBefore(beginTime) || localDateTime.isAfter(endTime) ){
            return null;
        }
        //获取库存量
        Integer stock = seckillVoucher.getStock();
        if (ObjectUtil.isNull(stock) || ObjectUtil.isNotNull(stock) && stock.intValue() <= 0){
            return null;
        }
        //扣减库存 将现在库存和之前查询的库存做对比,如果一样,则表明没有人在此中间修改过库存,则认定线程安全,扣除库存
        boolean update = seckillVoucherService.update(
                new LambdaUpdateWrapper<SeckillVoucher>().setSql("stock = stock - 1").eq(SeckillVoucher::getVoucherId, voucherId)
                        .gt(SeckillVoucher::getStock, 0)
        );
        if (!update){
            return null;
        }
        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();
        //创建订单ID
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);
        //获取用户ID
        Long id = UserHolder.getUser().getId();
        voucherOrder.setUserId(id);
        // 代金券id
        voucherOrder.setVoucherId(voucherId);
        this.save(voucherOrder);
        return orderId;
    }

本文由 mdnice 多平台发布

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

所有docker命令无效,解决办法

目录 ■前言 今天使用docker时&#xff0c;所有命令无效 ■解决办法如下 1.停止docker服务 2.查看状态 3.删除之前的docker相关的文件 4.再次查看状态 5.使用相关命令 &#xff08;好用了&#xff09; 6.重新下载镜像 ■前言 今天使用docker时&#xff0c;所有命令无…

MyBatis(简化数据库操作的持久层框架)--快速入门[上]

&#x1f600;前言 本篇博文是我在学习过程中的记录&#xff0c;分为上中下三个篇章&#xff0c;记录了我的一些学习心得&#xff0c;希望能够帮助到你&#x1f60a; &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&…

炎热夏天,VR全景让你宅家也能逛风景

夏天的快乐都是空调给的&#xff0c;进入三伏天气以来&#xff0c;连续的高温让人坐着都是一身汗&#xff0c;尤其是活泼好动的小朋友们&#xff0c;不出去玩那是不可能的。不妨改变下策略&#xff0c;让小朋友们转战线上&#xff0c;还能学习知识&#xff0c;是不是美滋滋呢~~…

FPGA XDMA 中断模式实现 PCIE3.0 AD7606采集 提供2套工程源码和QT上位机源码

目录 1、前言2、我已有的PCIE方案3、PCIE理论4、总体设计思路和方案AD7606数据采集和缓存XDMA简介XDMA中断模式QT上位机及其源码 5、vivado工程1--BRAM缓存6、vivado工程2--DDR4缓存7、上板调试验证8、福利&#xff1a;工程代码的获取 1、前言 PCIE&#xff08;PCI Express&am…

BMapGL -- 生成多个maker,获取指定标识的maker,并清除他们

需求描述: 在使用 Baidu Map JavaScript API 创建多个标记时,可以为每个标记设置一个唯一的标识符(identifier),以便在以后可以根据标识符获取特定的标记,并清除它们。 代码: // 创建地图实例 var map = new BMapGL.Map("container");// 创建标记1 var poin…

Jmeter配置起来太繁琐?试试RunnerGo

在用jmeter做性能测试时想看完整一点的测试报告&#xff0c;想配置阶梯模式来压测&#xff0c;想配置不同的接口并发这些都需要安装插件并且影响机器性能&#xff0c;想做自动化测试还得放到jenkins&#xff0c;这些配置起来太繁琐。今天给大家推荐一款测试平台RunnerGo&#x…

1334179-85-9,BTTAA,是各种化学生物学实验中生物偶联所需

资料编辑|陕西新研博美生物科技有限公司小编MISSwu​ BTTAA试剂 | 基础知识概述&#xff08;部分&#xff09;: 中文名称&#xff1a;2-[4-({双[(1-叔丁基-1H-1,2,3-三唑-4-基)甲基]氨基}甲基)-1H-1,2,3-三唑-1-基]乙酸 英文名称&#xff1a;BTTAA CAS号&#xff1a;1334179-8…

如何区分接口测试和功能测试

接口测试和功能测试的区别&#xff1a; 2023最新Jmeter接口测试从入门到精通&#xff08;全套项目实战教程&#xff09; 本文主要分为两个部分&#xff1a; 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者…

护网行动:ADSelfService Plus引领企业网络安全新纪元

随着信息技术的飞速发展&#xff0c;企业网络的重要性变得愈发显著。然而&#xff0c;随之而来的网络安全威胁也日益增多&#xff0c;网络黑客和恶意软件不断涌现&#xff0c;给企业的数据和机密信息带来巨大风险。在这个信息安全威胁层出不穷的时代&#xff0c;企业急需一款强…

微服务学习笔记-----Nacos安装教程(Windows和Linux版本)

Nacos安装教程 Nacos安装指南1.Windows安装1.1.下载安装包1.2.解压1.3.端口配置1.4.启动1.5.访问 2.Linux安装2.1.安装JDK2.2.上传安装包2.3.解压2.4.端口配置2.5.启动 3.Nacos的依赖 Nacos安装指南 1.Windows安装 开发阶段采用单机安装即可。 1.1.下载安装包 在Nacos的Git…

EAP设备自动化控制系统在设备数采和控制方面的优势

随着科技的不断进步和工业自动化的发展&#xff0c;EAP&#xff08;Equipment Automation Program&#xff09;设备自动化控制系统在各个行业中扮演着越来越重要的角色。作为连接MES&#xff08;Manufacturing Execution System&#xff09;和设备层的沟通桥梁&#xff0c;EAP系…

git stash 内容丢失找回【亲测好用】

直接将下列代码复制到 终端 会出现所有列表 也包括你删除/丢失的stash git log --graph --oneline --decorate $( git fsck --no-reflog | awk /dangling commit/ {print $3} ) 前面的黄色就是他的编号 例如我想回复 自己编辑修改项目 将编号复制重链即可 git stash apply …

Docker Compose 实现单机容器集群编排管理

目录 Docker ComposeDocker Compose 三大概念1. Docker Compose 环境安装2. YAML 文件格式及编写注意事项3. Docker Compose配置restart 设置重启策略&#xff0c;no&#xff0c;always&#xff0c;no-failure&#xff0c;unless-stopped 4. Docker Compose 常用命令选项5. Doc…

Llama 2 来袭 - 在 Hugging Face 上玩转它

&#x1f917; 宝子们可以戳 阅读原文 查看文中所有的外部链接哟&#xff01; 引言 今天&#xff0c;Meta 发布了 Llama 2&#xff0c;其包含了一系列最先进的开放大语言模型&#xff0c;我们很高兴能够将其全面集成入 Hugging Face&#xff0c;并全力支持其发布。Llama 2 的社…

香港二手电子产品分销商【创智环球】申请纳斯达克IPO上市

来源&#xff1a;猛兽财经 猛兽财经获悉&#xff0c;总部位于香港的二手电子产品分销商【创智环球】&#xff08;Creative Global Technology Holdings Limited&#xff09;近期已向美国证券交易委员会&#xff08;SEC&#xff09;提交招股书&#xff0c;申请在纳斯达克IPO上市…

Redis学习2--使用java操作Redis

1、java操作Redis库的比较 Redis有各种语言的客户端可以来操作redis数据库&#xff0c;其中java语言主要有Jedis与lettuce &#xff0c;Spring Data Redis封装了上边两个客户端&#xff0c;优缺点如下&#xff1a; 2、使用Jedis操作Redis Jedis使用的基本步骤&#xff1a; 引…

HANA学习笔记

1、安装 准备安装介质&#xff0c;我这儿用的是HANA2.00.059.00&#xff0c;注意会用到三个lib包和saptune&#xff0c;提前准备好。 执行./hdblcm开启数据库安装&#xff0c;过程中会涉及到需要用户设置一些参数&#xff0c;按照自己需求设置即可。 安装完成会生成一个安装日…

HCIA 第二课总结

配置网络设备的明文密钥实验组网 实验拓扑 将一个路由器使用配置口进行连接 sys #进入系统视图模式 sysname RTA #给设备命名 user-interface console 0 #进入用户接口配置界面 authentication-mode password #配置认证模式为密钥认证 set authentication password ciphe…

KubeVela篇07:terraform controller实现原理

terraform-controller是一个专门负责terraform一类的组件"安装"的Operator,通过打包成helm,再封装成kubevela的Addon,由kubevela安装到管控集群,为其它terraform provider插件提供模块定义支持。 从前面kubevela安装一个Application的原理我们了解到,当一个如a…

Spring 容器

Spring 容器【生产和管理bean】 BeanFactory【Spring容器的顶层接口】 public interface BeanFactory {String FACTORY_BEAN_PREFIX "&";Object getBean(String name) throws BeansException;Object getBean(String name, Object... args) throws BeansExcepti…