Redis队列实现Java版秒杀系统(无脚本、可用于生产)

2023-10-31

需求是做一个秒杀系统,比如大家来抢100台手机,先到先得。

查阅了网上很多用redis实现秒杀的demo(java语言),竟然没一个能用的!!!

有些是php的,没闲心研究了,现在说说为什么不能用:

  • 绝大多数的DEMO都是基于redis的watch特性的事务实现①,

  • 个别是基于redis分布式锁实现②。

  • 当然还有些用了脚本的,我也没仔细看是lua还是调用redis指令,哪有那个闲心去研究哇。

照顾一下小白,正文分析一下为什么这几种实现不行

正文


1.基于watch特性的 不靠谱 实现

其实这两种实现方式,完全可以理解为乐观锁(watch)和悲观锁(加分布式锁)

watch事务,相当于是乐观锁,这种方法在并发情况下极为不靠谱,假设有100个人同时尝试秒杀,那么极端情况下,有99个人都会失败,只有一个能修改成功。

然而demo里甚至没写如果修改失败了就重试这个功能,那显然这失败的99个人,已经提示失败了,过一会回来,发现还剩了90多。那我是怎么失败的?我替他们问问了。

并且使用这种方式实现呢,在并发量较大的时候,过多的重试线程应该会严重影响服务器性能。

2.基于用redis做个分布式锁的 不靠谱 实现

这种实现方式相当于一个悲观锁,每次执行减减操作之前,在redis中存入一个k,v键值对,使用特定的名称,并且使用setNX特性,确保抢锁没有安全问题,并在使用完成后释放锁。那么问题是,在100个人秒杀时,只有一个人抢到锁,剩下99个人怎么办?

demo里同样没写个重试,抢不到锁就失败,醉了,不过就算写重新抢锁的机制,那么几十个上百个线程不断抢锁,想想是个挺恐怖的事,更别提高并发了。

基于脚本的实现 不靠谱 实现

作为一个C系语言开发,我看不太懂,看不懂就是不靠谱,出了问题都不知道改哪里,你说靠不靠谱

正题:使用spring操作redis的list队列实现

我用的是springboot的StringRedisTemplate,至于如何整合jedis到spring等等,去查阅其他文章吧,我就不重复写了。

贴工具类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.Collection;

@Service
public class RedisServiceImpl<T> implements RedisService<T> {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //添加字符串并设置过期时间
    @Override
    public void addString(String key, String value, Duration duration) {
        stringRedisTemplate.opsForValue().set(key, value, duration);
    }

    //查找字符串
    @Override
    public String findString(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    //根据Key删除
    @Override
    public Boolean deleteByKey(String key) {
        return stringRedisTemplate.delete(key);
    }

 //在队列尾部减少一个对象
    @Override
    public String removeOneEntryOnListRight(String listName) {
        return stringRedisTemplate.opsForList().rightPop(listName);
    }

 //在队列头部新增对象
    @Override
    public Long addEntriesOnListLeft(String listName, Collection<String> args) {
        return stringRedisTemplate.opsForList().leftPushAll(listName, args);
    }

}

解释一下哈 这个类的父类是我自己写的service层,不是提供好的

主要使用的是最后两个方法,最后一个方法,在队列头部新增对象,如果没有这个队列,他会创建出来这个队列,然后将一个集合统统塞到这个redis队列中。倒数第二个方法每调用一次,会删除队列中最后一个元素,然后返回这个元素的值,如果队列中已经没有元素了(队列已经没了)那么他会返回null,他们都是原子操作。

如此,每个请求都无需经过加锁操作,直接利用redis的单线程特性,即可实现高并发下的秒杀:请求到达redis,redis会逐个执行,每一次执行要么返回一个值,要么返回null。很显然,返回值的就是抢到了,返回null的就是没抢到。而且可以灵活的为这个队列新加入一些元素(老板发话再加100台)或者直接把这个队列删了(老板说不行,不卖了)都不会对代码产生任何影响。

其中对应的redis操作指令分别是:

  • 在队列左侧新增:lpush

  • 在队列右侧消费:rpop

老板不卖了:del (笑)

接下来贴出十分简单的使用方法

先贴在任务开始时向redis中插入一个大队列

List<String> entriesList = new LinkedList<>();
   for (int i = 0; i < 100; i++){
       entriesList.add("某个商品");
   }
   redisService.addEntriesOnListLeft("队列名",entriesList);

突然想到这个实现即使秒杀100台不同型号的手机(并且在秒到时就通知用户秒到的是啥),也不用改代码。

每次秒杀执行:

String redisResult = redisService.removeOneEntryOnListRight("队列名");
    if (null == redisResult) {
        //说明没抢到
    }else{
 //说明抢到了 执行抢到逻辑
}

突然发现这个实现看起来甚至比那些所谓的秒杀demo还简单

但他既没有并发问题,也没有为了解决并发问题而衍生的性能问题。

虽然没经过测试,不过我认为就算秒杀10万台,放到redis队列里,应该也占用不了多少内存。

赶工分布式秒杀,没想到如此基本的内容,竟没找到一个靠谱的实现,从上午写到现在(周末晚8点)一顿饭没吃,被网上过于demo的资源逼的,忍饿怒出此文。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis队列实现Java版秒杀系统(无脚本、可用于生产) 的相关文章

随机推荐

  • SSRF漏洞(原理、挖掘点、漏洞利用、修复建议)

    一 介绍SSRF漏洞 SSRF Server Side Request Forgery 服务器端请求伪造 是一种由攻击者构造请求 由服务端发起请求的安全漏洞 一般情况下 SSRF攻击的目标是外网无法访问的内部系统 正因为请求是由服务端发起的
  • matlab中的strfind和findstr函数

    一 strfind函数 函数用法说明 strfind s1 s2 or strfind s1 pattern 说明 在s1中搜索pattern 例子 相关解答 function r myfun2 rand seed 2301 c 97 fi
  • PS如何将图片处理成特定像素(以标准的2寸照片为例)

    生活中我们在网站上注册信息需要上传个人照片时 常常遇到照片不符合网站要求等情况 今天我们以2寸照片 即626 高 413 宽 像素为例 来教大家如何用PS将照片处理成特定像素 1 裁剪照片 首先 拿到照片第一步 先裁剪照片至相应规格 打开P
  • mfc中添加按钮对应的处理函数四种方法

    方法一 双击按钮自动添加处理函数 自动以 OnBnClicked 开头 ID 结尾命名 这种方法 最简单 但无法修改函数名称 只能生成默认的按钮按下消息的函数 方法二 类向导 Ctrl p z 对按钮右键选择 类向导 在消息栏中选择默认的
  • 牛客sql练习二

    11获取所有员工当前的manager 题目描述 获取所有员工当前的manager 如果当前的manager是自己的话结果不显示 当前表示to date 9999 01 01 结果第一列给出当前员工的emp no 第二列给出其manager对
  • NepCTF 2022 MISC <签到题>(极限套娃)

    题目链接 CTFm 这道题融合了图片隐写 py脚本编写和usb流量分析 下载题目附件 是个gz压缩包 无加密直接打开 里面是一张图片 图片长这样 盲猜图片隐写 打开虚拟机使用binwalk工具康康 确实有多文件包含 binwalk e xx
  • 大数据技术之Spark——Spark SQL

    一 SparkSQL 概述 1 1 SparkSQL是什么 Spark SQL是Spark用于结构化数据处理的Spark模块 1 2 Hive and SparkSQL 我们之前学习过hive hive是一个基于hadoop的SQL引擎工具
  • Hadoop001-combiner作用。

    1 combiner函数可以帮助减少mapper和reducer之间的数据传输量 2 map到reduce需要不同map上的数据跨节点传输数据 数据传输受集群上可用带宽的影响
  • Scala学习(十二)---模式匹配

    文章目录 1 基本语法 2 模式守卫 3 匹配常量和类型 4 匹配对象和样例类 4 1 匹配对象 4 2 匹配样例类 5 偏函数中的模式匹配 1 基本语法 在Scala中的模式匹配类似于Java中的switch语法 模式匹配基本语法 val
  • python 网页自动化框架_Python+Selenium+Unittest实现PO模式web自动化框架

    1 效率高 PO模式的逻辑层方法有具体定义 元素发生变化修改逻辑层 业务层不变 这样看来结构简单清晰 舒服更符合人类习惯 普通方式就是继续堆case 2 复用多收益大 同样这里如果逻辑复用越多 PO模式收益越大 因为对于PO模式来说都只需要
  • stm32 CUBEIDE pritnf函数不立即输出问题

    今天在调试mavlink 有一段解析打印函数如下 while serial available serial3 uint8 t byte serial readChar serial3 if mavlink parse char seria
  • 力扣刷题笔记,8. 字符串整数转换

    针对力扣第8题 重点学习JAVA实现自动机的写法 1 自动机 字符串中的数字为结果的条件是 这个数字 包含符号 的前面只有空格 每次输入的字符包括四种情况 空格 符号 数字 其他 同时对应有四种状态 开始状态 start 即尚未读取到符号或
  • VBA快速入门学习笔记

    NOTE 1 一 VBA对象 工作簿 工作表 单元格 批注 透视表 自选图形 名称等等都是对象 VBA正是用于处理这些对象的语言 下图是一个比较经典的VBA对象类别图 在VBE VB编辑器 里也可以看到对象的浏览器 F2键 对象可以相互包含
  • lib库中允许存在全局变量

    Linux动态库so 静态库a 都是可以允许存在全局变量的 但是全局变量最好加上自己特有的属性 也就是需要注意不能重名 否则很有可能会因为被覆盖然后导致出错
  • VUE3中使用pinia

    Vue3中使用pinia 安装依赖 yarn add pinia 下面这个取决你需不需要数据持久化安装 yarn add pinia plugin persist 挂载 import createPinia from pinia 数据持久化
  • mysql练习:经典50道基础题

    目录 一 环境准备 50道题目练习 1 查询 01 课程比 02 课程成绩高的学生的信息及课程分数 2 查询学生选课存在 01 课程但可能不存在 02 课程的情况 不存在时显示为 null 3 查询平均成绩大于等于 60 分的同学的学生编号
  • jdbc连接超时解决

    这两天在测试Hive权限控制代码Hamza 发现每天来的时候第一次老是会报出以下错误 2015 03 26 09 40 25 956 ERROR GroupPrivController 119 Error querying database
  • spark实验总结

    4 5实验的问题在于spark保存和读取json的时候列名容易不是本来需要的字段名而是c1 c2这样的列名 解决办法是不要用建议读取方法而要指定读取表头 不用spark read csv 而是 spark read format json
  • c++ 使用 math库笔记

    目录 win10系统 cmakelist txt linux gcc方式 c 使用pi win10系统 这个头文件在visual studio的 sdk中 引用方法 include
  • Redis队列实现Java版秒杀系统(无脚本、可用于生产)

    需求是做一个秒杀系统 比如大家来抢100台手机 先到先得 查阅了网上很多用redis实现秒杀的demo java语言 竟然没一个能用的 有些是php的 没闲心研究了 现在说说为什么不能用 绝大多数的DEMO都是基于redis的watch特性