redis实战-黑马点评-优惠卷秒杀

news2025/2/26 5:50:43
全局ID生成器:

全局ID生成器:是一种再分布式系统下生成全局唯一的ID的工具。唯一性,高可用,高性能,安全性,递增性。

封装工具类生成全局ID:


@Component
public class RedisIDWorker {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    //2024年3月19日的时间戳
    private static final long BEGIN_TIME = 1710806400l;
    //时间戳的位数
    private static final int COUNT_BITS=32;
    public long nextId(String keyPrefix){
        //获取当前时间戳
        LocalDateTime now = LocalDateTime.now();
        long curSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timeStamp = curSecond - BEGIN_TIME;
        //生成序列号
        //获取当前日期,精确到天
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        //每天以不同的key做自增,避免超过2^32次方,获取序列号
        long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        //拼接时间戳和序列号返回
        return timeStamp<<COUNT_BITS|count;
    }
}

测试工具类:

@Resource
    RedisIDWorker redisIDWorker;
    private ExecutorService es= Executors.newFixedThreadPool(500);
    @Test
    void testIDWorker() throws InterruptedException {
        CountDownLatch latch=new CountDownLatch(300);
        Runnable task=()->{
            for (int i = 0; i < 100; i++) {
                long id= redisIDWorker.nextId("order");
                System.out.println("id:"+id);
            }
            latch.countDown();
        };
        long start = System.currentTimeMillis();
        for (int i = 0; i < 300; i++) {
            es.submit(task);
        }
        latch.await();
        long end = System.currentTimeMillis();
        System.out.println("time:"+(end-start));
    }
CountDownLatch详解:
ExecutorService详解:

全局唯一ID生成策略:

  • UUID
  • Redis自增
  • snowflake算法
  • 数据库自增。

redis自增ID策略:

  • 每天一个key,方便统计订单量
  • ID的构造是:时间戳+计数器。
实现优惠劵的秒杀下单:

下单时需要判断的两点:

  • 秒杀是否开始或结束,如果未开始或已结束则无法下单
  • 库存是否充足,不足则无法下单。

优惠劵下单功能实现:

 @Autowired
    ISeckillVoucherService iSeckillVoucherService;
    @Autowired
    RedisIDWorker redisIDWorker;
    @Override
    @Transactional
    public Result seckillOrder(Long voucherId) {
        //查询当前优惠劵信息
        SeckillVoucher voucher = iSeckillVoucherService.getById(voucherId);
        if (voucher.getBeginTime().isAfter(LocalDateTime.now())) {
            return Result.fail("秒杀尚未开始");
        }
        if (voucher.getEndTime().isBefore(LocalDateTime.now())) {
            return Result.fail("秒杀已经结束");
        }
        //判断库存是否足够
        Integer stock = voucher.getStock();
        if (stock<1){
            return Result.fail("库存不足");
        }
        //扣减库存
        boolean flag = iSeckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).update();
        //判断扣减库存是否失败
        if (!flag) {
            return Result.fail("库存不足");
        }
        //扣减库存成功,生成订单
        VoucherOrder voucherOrder=new VoucherOrder();
        //订单id,使用全局ID生成器
        long orderId = redisIDWorker.nextId("order");
        voucherOrder.setId(orderId);
        //下单人id
        voucherOrder.setVoucherId(UserHolder.getUser().getId());
        //优惠卷id
        voucherOrder.setVoucherId(voucherId);
        //保存订单到数据库
        save(voucherOrder);
        return Result.ok(orderId);
    }

//扣减库存
        boolean flag = iSeckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).update();

修改sql语句为,使用CAS方式实现乐观锁。在更新库存时查询库存是否已经被修改。

boolean flag = iSeckillVoucherService.update()
        .setSql("stock=stock-1").eq("voucher_id", voucherId)
        .eq("stock",voucher.getStock())
        .update();

以上方案的失败率高,当100个线程查询到stock为100,然后一个线程修改了stock,其他线程就全部失败,因此需要改为,当stock>0即可。

即:

boolean flag = iSeckillVoucherService.update()
        .setSql("stock=stock-1").eq("voucher_id", voucherId)
        .gt("stock",voucher.getStock())
        .update();

或者也可以使用分段锁提高成功率。

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

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

相关文章

深入了解 Postman 请求头的使用方法

当你在使用 Postman 发送请求时&#xff0c;请求头&#xff08;Headers&#xff09;是你可以包含在 HTTP 请求中的重要部分之一。请求头包含了关于请求的元数据信息&#xff0c;这些信息对于服务器来处理请求是非常重要的。下面是一份详细的图文介绍&#xff0c;说明了如何在 P…

电商行业的下一个风口是什么?2024的蝴蝶号平台,决不能错过

我是王路飞。 蝴蝶号或者说视频号小店项目&#xff0c;一定是2024年你不可错过的电商项目&#xff0c;也会是下一个电商风口&#xff0c;参考20年的抖音小店。 今天给你们详细聊下蝴蝶号/视频号小店这个项目&#xff0c;感兴趣的可以文末找我领取项目介绍。 内容来源于【醒醒…

达梦数据库SQL

达梦JSON函数技术文档 SQL中关键词处理 -- 必须要使用双引号包裹 select id,"comment" from t_cmp_rd_process;select id,"commit" from t_cmp_rd_gjj_eva;JSON_EXTRACT函数 -- party_sup_other_json 是包含JSON数据的列名。 -- $.content_abstract 是J…

Ubuntu双系统/home分区扩容

一、Windows系统中利用磁盘管理分出空闲区域&#xff0c;如果多就多分一些 二、插入安装Ubuntu的U盘启动盘&#xff0c;lenovo电脑F12&#xff08;其他电脑可选择其他类似方式&#xff09;选择U盘启动项&#xff0c;然后选择ubuntu&#xff0c;出现安装界面&#xff0c;再选择t…

什么是CPQ?CPQ报价管理软件面向制造企业快速报价解决方案

什么是CPQ&#xff1f; CPQ生产配置报价系统是面向生产加工企业&#xff0c;为企业市场部门提供快速报价的辅助系统&#xff0c;和配置报价系统不同的是生产报价系统成本的组成并不是标准的产品成本&#xff0c;其成本可能包含原材料成本、原材料损耗、制造成本、包装成本、运…

darkdown语法详解(readme语法)

1.简介 Markdown就是一种文档的格式&#xff0c;文件名的末尾是.md&#xff0c;是一种轻量级的标记语言&#xff0c;目前已经在Github、csdn等多个网站使用。 1.1.优点 排版语法简单可在任何支持markdown语法的地方无缝迁移&#xff0c;避免了 我们常见的迁移后需要重新排版…

【动态规划】算法例题

目录 一维动态规划&#xff1a; 137. 爬楼梯 ① 138. 打家劫舍 ② 139. 单词拆分 ② 140. 零钱兑换 ② 141. 最长递增子序列 ② 多维动态规划&#xff1a; 142. 三角形最小路径和 ② 143. 最小路径和 ② 144. 不同路径 II ② 145. 最长回文子串 ② 146. 交错字符串…

讯鹏智能边缘计算网关主机,支持MQTT协议主动上传上位机软件平台

随着科技的不断发展&#xff0c;智能化设备在我们的生活中扮演着越来越重要的角色。而智能边缘计算网关作为连接智能设备与互联网的重要纽带&#xff0c;其远程运维功能为管理者带来了极大的便利。特别是像OkEdge边缘计算网关这样的产品&#xff0c;不仅可以替代传统的Windows系…

Vmware虚拟机配置虚拟网卡

背景 今天同事咨询了我一个关于虚拟机的问题&#xff0c;关于内网用Vmware安装的虚拟机&#xff0c;无法通过本机访问虚拟上的Jenkins的服务。   验证多次后发现有如下几方面问题。 Jenkins程序包和JDK版本不兼容&#xff08;JDK1.8对应Jenkins不要超过2.357&#xff09;虚拟…

【Cesium】波纹圆效果

效果图&#xff1a; 代码&#xff1a; 需要扩展源码&#xff1a;circleRippleMaterialProperty.js /** Description: 波纹圆效果&#xff08;和水波纹扩散类似&#xff0c;参考开源代码&#xff09;* Version: 1.0*/ class CircleRippleMaterialProperty {constructor(option…

C语言例3-27:使用条件运算符的例子

条件运算符的优先级&#xff1a; 其他运算符 优先于 条件运算符 优先于 赋值和复合赋值运算符 优先于 逗号运算符条件运算符的结合性是从右至左 条件运算符的一般使用形式&#xff1a; 表达式1&#xff1f;表达式2&#xff1a;表达式3 条件表达式的执行过程是先表达式1&…

C++ Qt开发:QUdpSocket网络通信组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QUdpSocket组件实现基于UDP的网络通信…

Spring Bean的生命周期是怎么样的?

一、问题解析 一个Spring的Bean从出生到销毁的全过程就是他的整个生命周期&#xff0c;那么经历以下几个阶段&#xff1a; ​ 整个生命周期可以大致分为3个大的阶段&#xff0c;分别是&#xff1a;创建、使用、销毁。还可以进一步分为5个小的阶段&#xff1a;实例化、初始化、…

计算机生物科技在基因编辑中的应用及其前景

一、引言 基因编辑&#xff0c;作为一种能够精准修改生物体基因组的技术&#xff0c;近年来受到了广泛的关注。 而计算机生物科技作为连接计算机科学与生物学的桥梁&#xff0c;为基因编辑技术的快速发展提供了强大的支持。通过利用计算机算法和数据分析方法&#xff0c;研究人…

Java基础学习笔记三

环境变量CLASSPATH classpath环境变量是隶属于java语言的&#xff0c;不是windows操作系统的&#xff0c;和PATH环境变量完全不同classpath环境变量是给classloader&#xff08;类加载器&#xff09;指路的java A 。执行后&#xff0c;先启动JVM&#xff0c; JVM启动classload…

反射 Reflection

反射 反射的概念 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量&#xff0c;构造器&#xff0c;成员方法等等)&#xff0c;并能操作对象的属性及方法。反射在设计模式和框架底层都会用到加载完类之后&#xff0c;在堆中就产生了一个Class类型…

集合Python开发环境搭建

目录 PyCharm搭建Python环境_非虚拟环境 Pycharm的优点 Pycharm的缺点 Pycharm的下载 Pycharm环境配置 VSCode搭建Python环境_非虚拟环境 VSCode的优点 VSCode的缺点 VSCode的下载 VSCode环境配置 虚拟环境使用 虚拟环境介绍 虚拟环境安装 创建虚拟环境 切换虚拟…

使用 Vue CLI 创建一个 Vue2 项目

全局安装 Vue CLI 参考官网 Vue CLI&#xff0c;安装命令如下 npm install -g vue/cli 目前 Vue CLI 的最新版本为 v5.0.8 创建 Vue2 项目 在希望创建项目的目录下打开命令行&#xff0c;键入命令 vue create my-project 其中 my-project 更改为自己需要的项目名 随后&a…

R语言程序设计(零基础速通R语言语法和常见函数的使用)

目录 1.Rstudio中的一些快捷键 2.R对象的属性 3.R语言中常用的运算符​编辑 4.R的数据结构 向量 如何建立向量&#xff1f; 如何从向量里面提取元素&#xff1f; 矩阵 如何建立矩阵&#xff1f; 如何从矩阵里面提取元素&#xff1f; 数据框 如何建立数据框&#xf…

行业逆行者倪张根的十数年

在2015年的一场发布会上,梦百合家居董事长倪张根接受完全国80多家媒体的群访后,突然起身深深鞠了一躬,把在场的记者们吓了一跳。 对此,倪张根直接、坦率地承认“就是想讨好在座的各位”,这种不够柔和、不够世故的直球表达方式,在这个向来讲究中庸的社会,有种让人避之不及却又惊…