【Redis】-Redis实现高并发下秒杀系统

news2024/11/24 9:02:50

文章目录

    • 前言
  • 一、场景
  • 二、商品超卖的场景
  • 三、使用分布式锁解决超卖
  • 四、使用Redis事务乐观锁解决超卖


**

前言

Redis事务
  Redis事务是一种将多个命令打包执行的机制,确保这些命令要么全部执行成功,要么全部执行失败。Redis事务通过MULTI、EXEC、DISCARD和WATCH这四个命令来实现。

MULTI:该命令标志着事务的开始,之后的命令会被缓存起来而不会立即执行。
EXEC:该命令用于执行之前缓存的所有命令,如果执行成功,则事务中的所有命令都会被执行。如果其中有任何一条命令执行失败(例如语法错误),那么事务将中止,但不会影响其他客户端的事务。
DISCARD:该命令用于取消事务,清除之前缓存的所有命令。
WATCH:该命令用于在事务开始之前监视一个或多个键。如果在执行EXEC之前,被监视的键被其他客户端修改,那么这个事务会中止。
使用事务可以确保原子性操作,但是不支持回滚,即使其中某些命令执行失败,其他命令仍然会被执行。因此,在使用事务时,需要确保所有命令都能正确执行,或者在失败时进行适当的处理。

Lua脚本
  基本原理为使脚本相当于一个 redis 命令,可以结合 redis 原有命令,自定义脚本逻辑。

相同点
  很好的实现了一致性、隔离性和持久性,但没有实现原子性,无论是 redis 事务,还是 lua 脚本,如果执行期间出现运行错误,之前的执行过的命令是不会回滚的。

不同点
(1)redis 事务是基于乐观锁,lua 脚本是基于 redis 的单线程执行命令。
(2)redis 事务的执行原理就是一次命令的批量执行,而 lua 脚本可以加入自定义逻辑。
**

一、场景

  秒杀系统存在高并发的场景,在对商品进行秒杀时,由于并发过高可能会导致库存超卖的情况,那么可以通过Redis提供的事务机制超卖问题;Redis事务实际就是将所有命令都按顺序地执行。事务在执行时不会被其他的命令所打断。

二、商品超卖的场景

代码复现

@RestController
@RequestMapping("/second-kill")
@Slf4j
public class SecondKillController {
    @Resource
    private RedisTemplate redisTemplate;




    //记录实际卖出的商品数量
    private AtomicInteger successNum = new AtomicInteger(0);

    @GetMapping(value = "/init")
    public String init() {
        // 初始化库存数量,模拟库存只要5个商品,写入到redis中
        redisTemplate.opsForValue().set("stock", 5);
        successNum.set(0);
        log.info("===>>>库存初始化成功,库存数为" + 5);
        return "初始化库存成功";
    }

    @GetMapping(value = "/reduce")
    public String reduce() {
        int stock = (Integer) redisTemplate.opsForValue().get("stock");
        log.info("===>>>当前数量" + stock);
        // 模拟只减少一个库存
        stock = stock - 1;
        if (stock < 0) {
            log.info("===>>>库存不足");
            return "库存不足";
        }
        // 将剩余数量回写到redis
        redisTemplate.opsForValue().set("stock", stock);
        // 记录实际卖出的商品数量(线程安全每个请求都会记录)
        log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());
        return "减少库存成功";
    }
    }

执行结果:

这里我们使用JMeter并发进行测试
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
我们的库存总共只有5,而这里就共出售了12件商品,出现了超卖的情况。

三、使用分布式锁解决超卖

代码实现:

  @GetMapping(value = "/distributedLocksReduce")
    public String distributedLocksReduce() {
        int stock = (Integer) redisTemplate.opsForValue().get("stock");
        if (stock <= 0) {
            log.info("===>>>库存不足");
            return "库存不足";
        }
        String LOCK_KEY = "lockKey";
        String value = UUID.randomUUID().toString();
        // value值任意即可,秒杀设置锁的时间为1秒(根据实际情况更多)
        boolean absent = redisTemplate.opsForValue().setIfAbsent(LOCK_KEY, value, 1, TimeUnit.SECONDS);
        if (absent) {
            // 当前key没有锁,加锁成功
            log.info("===>>>加锁成功,获取并扣减库存");

            Integer sku = (Integer) redisTemplate.opsForValue().get("stock");
            //模拟只减少一个库存
            sku = sku - 1;
            if (sku < 0) {
                log.info("===>>>库存不足");
                // 执行脚本 删除锁
                redisTemplate.delete(LOCK_KEY);

                return "库存不足";
            }
            // 将扣减后的数量写入redis
            redisTemplate.opsForValue().set("stock", sku);

            log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());

            // 执行脚本 删除锁
            List<String> lockKeys = Collections.singletonList(LOCK_KEY);
            String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) return 1 else return 0 end";
            RedisScript<Long> luaScript = RedisScript.of(lua, Long.class);
            // 删除锁
            Object execute = redisTemplate.execute(luaScript, lockKeys, value);

            log.info("===>>>抢购成功");
            return "抢购成功";
        } else {
            log.info("===>>>抢购失败");
            return "抢购失败";
        }
    }

执行结果:
这里我们使用JMeter并发进行测试
在这里插入图片描述

我们可以看到这里总共只出售了五件商品

四、使用Redis事务乐观锁解决超卖

代码实现:

  @GetMapping(value = "/optimisticReduce")
    public String optimisticReduce() {
        // 开启事务
        redisTemplate.setEnableTransactionSupport(true);
        List<Object> results = (List<Object>) redisTemplate.execute(new SessionCallback<List<Object>>() {
            @Override
            public List<Object> execute(RedisOperations operations) throws DataAccessException {
                // 监视key
                operations.watch("stock");
                Integer stock = (Integer) operations.opsForValue().get("stock");
                operations.multi();
                stock = stock - 1;
                if (stock < 0) {
                    log.info("===>>>库存不足");
                    return null;
                }
                operations.opsForValue().set("stock", stock);
                return operations.exec();
            }
        });
        if (results != null && results.size() > 0) {
            log.info("===>>>减少库存成功,共出售" + successNum.incrementAndGet());
            return "减少库存成功";
        }
        log.info("库存不足");
        return "库存不足";
    }

执行结果:
在这里插入图片描述

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

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

相关文章

机器视觉检测设备的组成要素

机器视觉检测设备是一种先进的自动化检测技术工具&#xff0c;它利用光学、图像处理和计算机硬件及软件技术模拟并扩展人类的视觉功能&#xff0c;以实现对产品或目标物体进行自动化的尺寸测量、缺陷检测、表面质量评估、颜色识别、形状匹配以及位置判断等功能。这种设备通常包…

【Rockchip android7.1 平台rtl8821cs wifi移植调试】

Rockchip 平台rtl8821cs wifi移植调试 问题描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip rk3128 OS:Android 7.1.2 Kernel: 3.10 问题描述 客户需要在现在的板子上调一款RTL882…

【惠友精术】你见过给膝关节“打补丁”吗?单髁置换术,微创保膝真有一套

有什么方法可以让“换膝盖”既没那么痛&#xff0c;苦头又吃得少呢&#xff1f;你别说&#xff0c;还真有&#xff01; 近日&#xff0c;关节外科团队顺利为一例膝关节退变的患者完成了“膝关节单髁置换术”&#xff0c;实施了微小切口下对膝关节病变部位的精准治疗。该手术的成…

SV-7045网络草坪音箱安装说明 景区园区背景音乐广播石头音箱

SV-7045网络草坪音箱安装说明 景区园区背景音乐广播石头音箱 在做室外公共广播/背景音乐系统时&#xff0c;对于草坪音箱的安装方法,大家可能不甚了解&#xff0c;所以将具体安装方法作简要说明。 音箱安装步骤如下&#xff1a; 1.测量草地音箱的底座尺寸大小&#xff0c;最…

07-java基础-锁之AQSReentrantLockBlockingQueueCountDownLatchSemapho

文章目录 0&#xff1a;AQS简介-常见面试题AQS具备特性state表示资源的可用状态AQS定义两种资源共享方式AQS定义两种队列自定义同步器实现时主要实现以下几种方法&#xff1a;同步等待队列条件等待队列 1&#xff1a;AQS应用之ReentrantLockReentrantLock如何实现synchronized不…

【数据结构C++】线性表/顺序表-数组与vector

系列文章目录 第一章 线性表/顺序表-数组与vector 文章目录 系列文章目录前言一、数据结构主要研究的内容&#xff1f;二、数据结构三要素三、数组1.C中数组基本语法1.1 数组的声明、初始化1.2 vector的底层结构 2.数据结构之数组详解2.1 数据结构之数组操作——增、删、改、查…

55寸oled曲面拼接屏的特性

55寸OLED曲面拼接屏具有一系列引人注目的特性&#xff0c;使其在高端展示、商业广告、会议室和舞台演出等场合具有广泛的应用价值。以下是关于55寸OLED曲面拼接屏特性的详细解析&#xff1a; 高亮度与高对比度&#xff1a;OLED技术使得屏幕能够在各种光线条件下呈现出明亮且清晰…

【构建部署_Docker介绍与安装】

构建部署_Docker介绍与安装 构建部署_Docker介绍与安装Docker介绍Docker安装CentOS安装DockerCompose 构建部署_Docker介绍与安装 Docker介绍 Docker 是一个基于go语言开发的开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#x…

算法---滑动窗口练习-2(无重复字符的最长子串)

无重复字符的最长子串 1. 题目解析2. 讲解算法原理3. 编写代码 1. 题目解析 题目地址&#xff1a;无重复字符的最长子串 2. 讲解算法原理 首先定义了变量 left、right 和 len&#xff0c;分别表示当前无重复子串的左边界、右边界和最大长度。 获取输入字符串 s 的长度 n。 定…

nginx有几种启动方式

Nginx 通常可以以两种主要的方式启动&#xff1a;作为前台进程运行或作为守护进程&#xff08;后台&#xff09;运行。 前台运行&#xff1a; 当Nginx以前台模式运行时&#xff0c;它会在命令行保持活动状态&#xff0c;所有的日志输出都会直接显示在命令行上。这种模式通常用于…

2022-6 青少年软件编程(图形化) 等级考试试卷(一级)

第1题:【 单选题】 广场中有声控喷泉, 当声音的音量大于 60 的时候, 喷泉就会喷出水, 现在的音量为30,下列哪个选项可以让喷泉喷出水? ( ) A: B: C: D: 【正确答案】: B 【试题解析】 : 当前声音的音量为 30, 需要将声音增加到 60 以上就可以让喷泉喷出水, …

Unity开发中Partial 详细使用案例

文章目录 **1. 分割大型类****2. 与 Unity 自动生成代码协同工作****3. 团队协作****4. 共享通用逻辑****5. 自定义编辑器相关代码****6. 配合 Unity 的 ScriptableObjects 使用****7. 多人协作与版本控制系统友好** 在 Unity 开发中&#xff0c; partial 关键字是 C# 语言提供…

STM32的GPIO初始化配置-学习笔记

简介&#xff1a; 由于刚开始没有学懂GPIO的配置原理&#xff0c;导致后面学习其它外设的时候总是产生阻碍&#xff0c;因为其它外设要使用前&#xff0c;大部分都要配置GPIO的初始化&#xff0c;因此这几天重新学习了一遍GPIO的配置&#xff0c;记录如下。 首先我们要知道芯片…

ideaSSM教师评价管理系统springMVC开发mysql数据库web结构java编程计算机网页源码

一、源码特点 SSM 教师评价管理系统是一套完善的完整信息系统&#xff0c;结合SSM框架和bootstrap完成本系统SpringMVC spring mybatis &#xff0c;对理解JSP java编程开发语言有帮助系统采用SSM框架&#xff08;MVC模式开发 &#xff09;&#xff0c;系统具有完整的源代码和…

Yakit爆破模块应用

yakit介绍 一款集成了各种渗透测试功能的集成软件。&#xff08;类似于burp&#xff0c;但我感觉他功能挺强大&#xff09; 爆破模块位置 按照下面图标点击 界面就是如下。 左侧可以选择爆破的类型&#xff0c;各种数据库http&#xff0c;ssh等都支持。 爆破参数 可以选择…

基于可穿戴式多模态人机接口的机械臂运动控制方法

随着科技的不断进步&#xff0c;人机接口在机械臂等辅助设备中的应用不仅为上肢运动障碍人群带来了显著的生活质量提升&#xff0c;同时也揭示出传统人机接口系统存在的一系列问题。这些问题包括指令较少、操作困难等方面&#xff0c;限制了机械臂等辅助设备在运动控制方面的多…

RocketMQ 面试题及答案整理,最新面试题

RocketMQ的消息存储机制是如何设计的&#xff1f; RocketMQ消息存储机制的设计原理&#xff1a; 1、CommitLog文件&#xff1a; 所有的消息都存储在一个连续的CommitLog文件中&#xff0c;保证了消息的顺序写入&#xff0c;提高写入性能。 2、消费队列&#xff1a; 为每个主…

3915A/B/C/D/E/F/G/H EMI测试接收机

3915A/B/C/D/E/F/G/H EMI测试接收机 产品综述 <<<<频率范围&#xff1a;10MHz~50GHz>>> 3915 系列EMI测试接收机是针对国家及国家电磁兼容标准测试需求推出的一款高性能接收机产品。它具有高灵敏度、高精度、大动态范围、低相位噪声等特点&#xff0c;支…

【spring】@Bean注解学习

Bean介绍 Bean用于指示一个方法应该产生一个Bean对象&#xff0c;并将其交给Spring容器管理。 当使用Bean注解的方法被Spring容器调用时&#xff0c;它只会执行一次&#xff0c;随后该方法返回的Bean对象会被添加到Spring的IOC&#xff08;Inversion of Control&#xff0c;控…

linux解析域名指令 nslookup 或者 host

host www.baidu.com 第二种方法 nslookup www.baidu.com 注意&#xff1a;ns是name server之意