【遇见青山】项目难点:解决超卖问题

news2024/9/23 23:31:11

【遇见青山】项目难点:解决超卖问题

  • 1.乐观锁方案
  • 2.悲观锁方案

1.乐观锁方案

原始实现下单功能的方法:

/**
 * 秒杀实现
 *
 * @param voucherId 秒杀券的ID
 * @return Result
 */
@Override
@Transactional
public Result seckillVoucher(Long voucherId) {
    // 查询优惠券
    SeckillVoucher voucher = seckillVoucherService.getById(voucherId);

    // 判断秒杀是否已经开始
    if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
        return Result.fail("秒杀尚未开始呢!");
    }
    // 判断秒杀是否已经结束
    if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
        return Result.fail("秒杀已经结束啦!");
    }

    // 判断库存是否充足
    // 库存不足!
    if (voucher.getStock() < 1) {
        return Result.fail("库存不足啦!");
    }
    // 扣减库存
    boolean isDeduction = seckillVoucherService.update()
            .setSql("stock = stock - 1")
            .eq("voucher_id", voucherId).update();
    if (!isDeduction) {
        return Result.fail("库存不足啦!");
    }

    // 创建订单
    VoucherOrder voucherOrder = new VoucherOrder();
    long orderId = redisIdWorker.nextId("order");
    voucherOrder.setId(orderId);
    voucherOrder.setUserId(UserHolder.getUser().getId());
    voucherOrder.setVoucherId(voucherId);
    save(voucherOrder);

    // 返回订单id
    return Result.ok(orderId);
}

这种方案会发生超卖问题,我们尝试使用CAS方法改进,来解决超卖问题

在这里插入图片描述

CAS方法要求我们在执行扣减库存的同时判断此时的库存数量是否大于0

// 扣减库存
boolean isDeduction = seckillVoucherService.update()
        .setSql("stock = stock - 1")
        .eq("voucher_id", voucherId)
        .gt("stock", 0)
        .update();

这样可以完美解决超卖问题!


2.悲观锁方案

上述的程序依然存在一个问题:同一个用户可以抢多张优惠券:

基于这个问题,我们对代码进行改进,加入判断用户是否已经秒杀过的逻辑:

// 得到用户ID
Long userId = UserHolder.getUser().getId();
// 判断用户是否已经抢过该优惠券
int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
if (count > 0) {
    return Result.fail("已经抢过啦!");
}

使用JMeter测试,发现多线程依然存在并发安全问题,但由于此时我们的目标是新增数据,新增数据无法使用乐观锁来实现,所以我们只得被迫使用悲观锁!

首先,封装创建订单的类:

/**
 * 创建新订单,基于悲观锁实现一人一单功能
 *
 * @param voucherId 优惠券id
 * @return Result
 */
@Transactional
public Result createVoucherOrder(Long voucherId) {
    // 得到用户ID
    Long userId = UserHolder.getUser().getId();

    // 判断用户是否已经抢过该优惠券
    int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
    if (count > 0) {
        return Result.fail("已经抢过啦!");
    }

    // 扣减库存
    boolean isDeduction = seckillVoucherService.update()
            .setSql("stock = stock - 1")
            .eq("voucher_id", voucherId)
            .gt("stock", 0)
            .update();
    if (!isDeduction) {
        return Result.fail("库存不足啦!");
    }

    // 创建订单
    VoucherOrder voucherOrder = new VoucherOrder();
    long orderId = redisIdWorker.nextId("order");
    voucherOrder.setId(orderId);
    voucherOrder.setUserId(userId);
    voucherOrder.setVoucherId(voucherId);
    save(voucherOrder);

    return Result.ok(orderId);
}

给该方法添加悲观锁,同时要注意防止事务失效的问题(需要使用代理对象调用方法):

// 创建新订单,基于悲观锁实现一人一单功能
synchronized (userId.toString().intern()) {
    // 为了防止事务失效,这里使用代理对象调用方法
    IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();
    // 返回订单id
    return proxy.createVoucherOrder(voucherId);
}

添加动态代理的支持:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

启动类上暴漏代理对象:

@EnableAspectJAutoProxy(exposeProxy = true)

解决!

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

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

相关文章

备战蓝桥杯【高精度加法和高精度减法】

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

IDEA中使用自定义的maven

步骤 1.下载maven maven下载 2.配置maven 2.1设置环境变量 1.在“环境环境“–“系统环境“–“path”中加入&#xff08;设置到bin目录下&#xff09; 2.测试环境变量是否成功 C:\Users>mvn -v //在控制台输入mav -v,看是否输出以下结果 Apache Maven 3.9.0 (9b…

《MySQL系列-InnoDB引擎23》文件-InnoDB存储引擎文件-重做日志文件

InnoDB存储引擎文件 之前介绍的文件都是MySQL数据库本身的文件&#xff0c;和存储引擎无关。除了这些文件外&#xff0c;每个表存储引擎都有其自己独有的文件。本节将具体介绍与InnoDB存储引擎密切相关的文件&#xff0c;这些文件包括重做日志文件、表空间文件。 重做日志文件…

Docker的资源控制管理

目录 一、CPU控制 1、设置CPU使用率上限 2、设置CPU资源占用比&#xff08;设置多个容器时才有效&#xff09; 3、设置容器绑定指定的CPU 二、对内存使用进行限制 1、创建指定物理内存的容器 2、创建指定物理内存和swap的容器 3、 对磁盘IO配额控制&#xff08;blkio&a…

使用Docker容器部署java运行环境(java8 + mysql5.7 + redis5.0 + nginx1.14.1

环境&#xff1a;阿里云ECS服务器一.Docker环境安装1.1 安装工具sudo yum install -y yum-utils device-mapper-persistent-data lvm21.2 为yum源添加docker仓库位置yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo1.3 将软件…

【微信小游戏开发笔记】第二节:Cocos开发界面常用功能简介

Cocos开发界面常用功能简介 本章只介绍微信小游戏开发时常用的功能&#xff0c;其他功能不常用&#xff0c;写多了记不住&#xff08;其实是懒 -_-!&#xff09;&#xff1a; 层级管理器&#xff0c;用于操作各个节点。资源管理器&#xff0c;用于操作各种文件资源。场景编辑…

SpringMVC--简介和入门案例

SpringMVC简介 什么是MVC MVC是一种软件架构的思想&#xff0c;将软件按照模型、视图、控制器来划分 M:Model&#xff0c;模型层&#xff0c;指工程中的JavaBean&#xff0c;作用是处理数据 JavaBean分为两类: 一类称为实体类Bean:专门存储业务数据的&#xff0c;如 Studen…

西湖论剑初赛web wp

Node Magical Login 简单的js代码审计。 Flag分成了两部分。 第一部分&#xff1a; 这里就简单的判断了一下user是否等于admin&#xff0c;直接绕过。 第二部分&#xff1a; checkcode ! “aGr5AtSp55dRacer”&#xff0c;让其为真&#xff0c;利用数组绕过。 Flag为&#x…

家政服务小程序实战教程03-创建自定义应用

我们上一篇讲解了创建模型应用&#xff0c;模型应用是给管理员使用的。普通用户日常办理业务还是在小程序完成。 微搭中的小程序需要通过创建自定义应用来创建&#xff0c;进入控制台&#xff0c;点击应用&#xff0c;点击新建应用&#xff0c;选择新建自定义应用 输入应用的名…

微信小程序 java家校通Springboot中小学家校联系电子作业系统

小程序前端框架&#xff1a;uniapp 小程序运行软件&#xff1a;微信开发者 后端技术:javaSsm(SpringSpringMVCMyBatis)vue.js 后端开发环境:idea/eclipse 数据库:mysql 通过对各种资料的收集&#xff0c;了解到“校讯通”是联系社会的窗口&#xff0c;是实现家校联系工作和学校…

【参加CUDA线上训练营】零基础cuda—矩阵转置实现及其优化

【参加CUDA线上训练营】零基础cuda—矩阵转置实现及其优化1.不使用Shared Memory2.使用Shared Memory3.使用Shared Memory&#xff0c;并加入No Bank Conflicts4.效果对比参考文献本文参考Nvidia官方blog[An Efficient Matrix Transpose in CUDA C/C及其对应的github代码transp…

可视化图表的思路

数据表达 excel — 小量级一次性的数据处理 Tableau等BI — 批量的数据读取与分析 python — 复杂的数据清洗、爬虫和算法建模 图表展示原则&#xff1a;客观&#xff0c;高效&#xff0c;直观 表达格式&#xff1a;观点数据补充信息图表 图表选择思路 规模、趋势、占比、关…

RabbitMQ-延迟队列

一、介绍延迟队列&#xff0c;队列内部是有序的&#xff0c;最重要的特性就体现在他的延迟属性上&#xff0c;延时队列中的元素是希望在指定时间到了或之前取出和处理&#xff0c;简单来说&#xff0c;延时队列就是用来存放需要在指定时间被处理的元素的队列。 二、sprin…

TCP的协议格式 --- 20字节固定长度 + 40字节可选数据

目录 一、 20字节的固定长度 16位源端口和16位目的端口号&#xff0c;32位序号&#xff0c;32位确认属序号&#xff0c;4位首部长度&#xff08;需要乘4&#xff09; 保留&#xff08;6位&#xff09; 16位窗口大小 16位的校验和16位的紧急指针 二、40字节可选数据 1.2.1、…

软件设计师教程(六)计算机系统知识-操作系统知识

软件设计师教程 软件设计师教程&#xff08;一&#xff09;计算机系统知识-计算机系统基础知识 软件设计师教程&#xff08;二&#xff09;计算机系统知识-计算机体系结构 软件设计师教程&#xff08;三&#xff09;计算机系统知识-计算机体系结构 软件设计师教程&#xff08;…

最新中文版FL Studio21水果软件下载安装图文教程

FL Studio是目前流行广泛使用人数最多音乐编曲制作软件&#xff0c;这款软件相信广大网友并不陌生&#xff0c;今天带来的是FL中文版本&#xff0c;所有的功能都能在线编辑&#xff0c;用户直接就能操作&#xff0c;同时因为是21水果是最新版&#xff0c;所以增加了新的功能&am…

【Spring Cloud总结】1、服务提供者与服务消费者快速上手

目录 文件结构 代码 1、api 1.1实体类&#xff08;Dept &#xff09; 1.2数据库 2、provider 2.1 DeptController 2.2 DeptDao 2.3 DeptService 2.4 DeptServiceImpl 2.5 application.yml 3、consumer 3.1 ConfigBean 3.2 DeptConsumerController 测试 1.启动…

创建阿里云物联网平台

创建阿里云物联网平台 对云平台设备创建过程做记录&#xff0c;懒得再看视频 文章参考视频&#xff1a;https://www.bilibili.com/video/BV1jP4y1E7TJ?p26&vd_source50694678ae937a743c59db6b5ff46c31 阿里云&#xff1a;https://www.aliyun.com 1&#xff0e;物联网平…

基于jsp的网络电子相册的设计与实现

技术&#xff1a;Java、JSP等摘要&#xff1a;随着科学技术的不断进步&#xff0c;云技术以及大数据的不断完善&#xff0c;越来越多的网络忠实用户告别了冲洗相片的时代&#xff0c;他们更喜欢将相片上传至网络&#xff0c;这样就省去了携带和查找的麻烦&#xff0c;随时随地只…

大数据技术之Hudi

Hudi概述 1.1 Hudi简介 Apache Hudi&#xff08;Hadoop Upserts Delete and Incremental&#xff09;是下一代流数据湖平台。Apache Hudi将核心仓库和数据库功能直接引入数据湖。Hudi提供了表、事务、高效的upserts/delete、高级索引、流摄取服务、数据集群/压缩优化和并发&a…