Redis框架(十一):大众点评项目 乐观锁解决超卖问题 悲观锁解决一人一单问题

news2024/10/7 16:17:24

大众点评项目 基于Session的短信登录

  • 需求:乐观锁解决超卖问题 悲观锁解决一人一单问题
  • 业务代码
  • 总结

SpringCloud章节复习已经过去,新的章节Redis开始了,这个章节中将会回顾Redis实战项目 大众点评
主要依照以下几个原则

  1. 基础+实战的Demo和Coding上传到我的代码仓库
  2. 在原有基础上加入一些设计模式,stream+lamdba等新的糖
  3. 通过DeBug调试,进入组件源码去分析底层运行的规则和设计模式

代码会同步在我的gitee中去,觉得不错的同学记得一键三连求关注,感谢:
Redis优化-链接: RedisVoucherCASProject
在这里插入图片描述

在这里插入图片描述

需求:乐观锁解决超卖问题 悲观锁解决一人一单问题

超卖问题

当秒杀进行时,如果有大量用户同时点击,就容易出现一种情况
大量库存被减到负数,这肯定是不友好的
下面是通过JMX进行的压测,模拟并发实现
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一人一单
同样,我们不希望一个人拿到所有的秒杀商品

所以我们需要对这两种情况进行处理,锁机制就来了

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
版本号:就是通过定义版本Id,去进行判断,通过sql中的where条件就可以实现乐观锁
在这里插入图片描述
CAS:就是自旋,查看数据是否被改变,没有变就是ok的

业务代码

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author 张大树
 * @since 2022-12-12
 */
@Service
@Slf4j
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {
    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIDProductor redisIDProductor;

和上一节相同,可以参考上一节

    @Override
    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("库存不足");
        }

实现一人一单功能,
为了保证一人一单,使用了悲观锁synchronized,需要注意

  1. synchronized想要保证效率,不要直接加载类上,判断出,一人一单为了是保证每个人下一单,那个我们直接锁ID,就可以了,这样每个id都有各自的线程可以走,不是所有都锁住!
  2. userId.toString()本身返回的是一个new类型 return new String(buf, true);,所以我们必须保证每次拿到的ID的对象是同一个,这里就是用了intern方法,它回去常量池中查找对应的id,保证了唯一性
  3. 这里的 @Transactional也需要注意,如果直接 调用的this.createVoucherOrder方法,没有实际对象,是事务失效的几种情况之一,所以需要找代理对象来实现!
        //实现一人一单
        Long userId = UserHolder.getUser().getId();

        //intern()是去常量池中去找userId,处理锁失效,事务失效
        synchronized(userId.toString().intern()) {
            IVoucherOrderService currentProxy = (IVoucherOrderService) AopContext.currentProxy();
            return currentProxy.createVoucherOrder(voucherId);
            /*调用的this.create方法,没有实际对象,是事务失效的几种情况之一,所以需要找代理对象来实现
            return createVoucherOrder(voucherId);*/
        }
    }

    @Transactional
    public  Result createVoucherOrder(Long voucherId) {
     
        Long userId = UserHolder.getUser().getId();

        int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();

        if (count > 0) {
            return Result.fail("已经购买");
        }

为了防止超卖,其本身就是不断更新数量,所以使用了乐观锁,CAS方法,需要注意

  1. CAS: 通过设定判断库存数量来进行,适合更新数据使用
  2. 注意MP的使用seckillVoucherService.update() .setSql("stock = stock - 1") .eq("voucher_id", voucherId) .gt("stock", 0)//CAS: 通过设定判断库存数量来进行,适合更新数据使用 .update();
        //验证结束,扣减库存
        boolean flag = seckillVoucherService.update()
                .setSql("stock = stock - 1")
                .eq("voucher_id", voucherId)
                .gt("stock", 0)//CAS: 通过设定判断库存数量来进行,适合更新数据使用
                .update();

        if (!flag) {
            return Result.fail("库存不足");
        }

封装订单

        //封装订单
        VoucherOrder voucherOrder = new VoucherOrder();
        long orderId = redisIDProductor.nextId("order");
        //订单Id、用户Id、优惠券Id
        voucherOrder.setId(orderId).setUserId(userId).setVoucherId(voucherId);
        save(voucherOrder);


        return Result.ok(orderId);

    }
}

总结

如果引入代理对象 IVoucherOrderService currentProxy = (IVoucherOrderService) AopContext.currentProxy(); return currentProxy.createVoucherOrder(voucherId);
一定要注意加依赖,开启扫描注解

@EnableAspectJAutoProxy(exposeProxy = true)
@MapperScan("com.hmdp.mapper")
@SpringBootApplication
public class HmDianPingApplication {

    public static void main(String[] args) {
        SpringApplication.run(HmDianPingApplication.class, args);
    }

}

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

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

数字IC后端设计如何快速入门?(内附学习视频)

虽然2022年IC行业门槛有所提高&#xff0c;但这也抵挡不住同学们对转行IC行业的热情&#xff0c;数字后端设计的发展前景和高薪也在众多岗位中脱颖而出&#xff0c;那么数字IC后端设计如何快速入门&#xff1f;下面IC修真院就带大家来了解一下。 数字后端工程师是做什么的&…

Docker:自定义镜像上传阿里云

目录 一.jdkv.1.0的制作 启动虚拟机&#xff0c;进入centos 创建文件夹上传jdk的安装包,和在同级目录下编写Dockerfile文件 执行Dockerfile文件&#xff0c;初次依赖镜像的时候会下载相应镜像​​​​​​​ 二.jdk2.0的制作 三.jdk3.0的制作 四.将制作好的镜像上传阿里云…

一文解读机密容器的崛起和发展

在 2022 云栖大会龙蜥峰会云原生专场上&#xff0c;来自阿里云操作系统技术专家冯世舫和Intel 系统软件工程部高级研发经理朱江云分享了《机密容器的崛起和发展》技术演讲&#xff0c;以下为本次演讲内容&#xff1a; 机密容器是 CNCF 的 一个 Sandbox 项目&#xff0c;用于解…

第一章 linux的发展

第一章 linux的发展一、操作系统的出现二、linux的出现三、linux的发展一、操作系统的出现 大部分先进产品的出现必定是为了军事服务的&#xff0c;起初的大型计算机也同样是为了军事服务的&#xff0c;而操作计算机的人也不是程序员&#xff0c;而是科学家。二战时期&#xf…

DVWA靶场中SQL注入

DVWA靶场中SQL注入1.DVWA靶场中SQL注入1.1.SQL Injection1.1.1.Low级别1.1.2.Medium级别1.1.3.High级别1.2.SQL Injection(Blind)1.2.1.方式1.2.2.Low级别1.2.3.Medium级别1.2.4.High级别1.DVWA靶场中SQL注入 1.1.SQL Injection 1.1.1.Low级别 1&#xff09;判断注入类型当输…

高中数理化杂志高中数理化杂志社高中数理化编辑部2022年第21期目录

高考全关注《高中数理化》投稿&#xff1a;cn7kantougao163.com 直线与圆的方程高考热点赏析 廖永福; 1-4 一道课本例题到一道高考试题的衍变之路 高磊; 4-8 圆的多种定义形式在解题中的应用 李光彬;邵建凤; 9-10 从2021年全国新高考Ⅰ卷第21题说起 王菊;张琥;…

碳交易机制下考虑需求响应的综合能源系统优化运行(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

接口测试(五)—— PyMySQL增删改查、数据库工具类封装

目录 数据库操作应用场景 一、PyMySQL操作数据库 1、安装PyMySQL 2、PyMySQL操作步骤 3、事务的概念 4、PyMySQL连接数据库 4.1 建立连接方法 4.2 入门案例 5、PyMySQL操作数据库 5.1 SQL 语法 5.2 数据库查询 5.3 案例&#xff08;查询&#xff09; 5.4 数据库UI…

代码随想录训练营第七天

专题&#xff1a;哈希表 题目&#xff1a;四数相加 题目简单&#xff1a;把四个数组分成两队&#xff0c;然后用map&#xff0c;保存前两个数组的元素之和&#xff0c;&#xff08;key,val&#xff09;key保存的是前两个数组的元素之和的数值&#xff0c;val保存的是数值对应…

PDF设置密码保护的两种方法

PDF文件可以根据需要&#xff0c;设置两种密码来保护文件。 需要保护文件内容&#xff0c;不想PDF被随意打开&#xff0c;我们可以设置打开密码&#xff0c;这样只有输入正确的密码才能打开文件。 在编辑器中打开PDF后&#xff0c;找到菜单中【保护】选项下的【密码加密】&am…

SpringCloud01

1.认识微服务 随着互联网行业的发展&#xff0c;对服务的要求也越来越高&#xff0c;服务架构也从单体架构逐渐演变为现在流行的微服务架构。这些架构之间有怎样的差别呢&#xff1f; 1.0.学习目标 了解微服务架构的优缺点 1.1.单体架构 单体架构&#xff1a;将业务的所有功能集…

33-98-spark-核心编程-RDD算子和任务阶段等

33-spark-核心编程-RDD&#xff1a; 1、RDD的创建&#xff0c;4中方式。分别是从内存中创建&#xff0c;从文件中创建&#xff0c;从其他RDD创建和new RDD&#xff0c;后两者不常用。 创建&#xff1a;big-data-study\Spark-demo\src\main\java\spark\core\com\zh\rdd\builde…

PTA-基础编程题目集(函数题)

个人主页&#xff1a;平行线也会相交 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【[PTA刷题训练营]】 目录6-1 简单输出整数6-2 多项式求值&#xff08;重点掌握&#xff09;6-3 简单求和6-4 求自定类型元素的平均6-5 求…

医院陪诊小程序怎么开发-医院陪诊小程序源码功能

目前医院陪诊新型行业已经占据了很大的市场所在&#xff0c;我们去医院看病找医生挂号帮忙取药有时候去外地人生地不熟的 自己转半天摸索不过来浪费时间 而且有时候一个人需要陪同比较放心&#xff0c;所以呢衍生出来了 热门的陪诊师 如何开发&#xff1f; 开发无非就是几种 …

如何为 Longhorn 扩展对象存储能力

作者&#xff1a; 王海龙&#xff0c;Rancher 中国社区技术经理&#xff0c;Linux Foundation APAC Evangelist&#xff0c;负责 Rancher 中国技术社区的维护和运营。拥有 8 年的云计算领域经验&#xff0c;经历了 OpenStack 到 Kubernetes 的技术变革&#xff0c;无论底层操作…

软件包管理器RPM与yum

1、RPM安装软件包 安装单个rpm软件包 下载JDK8u221的rpm软件包 链接&#xff1a;https://pan.baidu.com/s/1fYKNNM02GBh-cOUuajkBIg 提取码&#xff1a;yg53 上传JDK8u221的rpm软件包到虚拟机/opt目录 命令&#xff1a;rpm -ivh jdk-8u221-linux-x64.rpm 查看JDK版本 命令&a…

C语言split分割字符串

C语言split分割字符串。 //以下解法的前提是&#xff0c;先把所有环变成1.无环路&#xff0c;2.一个环没有扣住3个及以上的其他环 voidmain(){ intarray[16]{0}; //init,array[1]xxx;根据输入初始化数组&#xff0c;如1-2&#xff0c;则&#xff0c;array[1]2,... intHash…

在线人事管理系统

开发工具(eclipse/idea/vscode等)&#xff1a;idea 数据库(sqlite/mysql/sqlserver等)&#xff1a;mysql 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a;本系统按功能分为以下几个模块: “简易云”是这个系统的名字 &#xff08;1&#xff09;登录页面&#xff1a;实…

【HMS Core】华为统一扫码服务ScanKit如何获取具体条码的类型?

1、问题描述 项目中接入了华为的统一扫码服务SDK&#xff0c;识别过程正常&#xff0c;但是目前有个需求&#xff0c;需要在扫码完成之后根据条码的具体类型处理接下来的业务。 问题是&#xff1a;识别完条形码后&#xff0c;如何拿到具体的条形码和二维码类型&#xff0c;比…

从零搭建本地pypi镜像源1:快速体验

前言&#xff1a; 许多公司&#xff0c;出于数据安全与知识产权的原因&#xff0c;在公司内部搭建局域网进行算法开发。配置一个本地的pypi镜像源对工程开发十分重要。搭建本地pypi镜像源的工具有多种&#xff0c;本文主要介绍pip2pi方法。 第一步&#xff1a;新建项目&#…