Redis实战篇(三)秒杀

news2025/7/14 17:56:28

一、全局唯一ID

(1)定义

全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一半满足下列特性:

  • 唯一性
  • 高可用
  • 高性能
  • 递增性
  • 安全性

为了增加ID的安全性,我们不直接使用Redis自增的数值,而是拼接一些其他的信息。
ID的组成部分:

  • 符号位:1bit,永远为0
  • 时间戳:31bit,以秒为单位,可以使用69年
  • 序列号:32bit,秒内计数器,支持每秒产生2ⁿ32个不同的ID

(2)代码实现


@Component
public class RedisIdWorker {
    /**
     * 开始时间戳
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;
    /**
     * 序列号的位数
     */
    private static final int COUNT_BITS = 32;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;


    public long nextId(String keyPrefix) {
        // 1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP;

        // 2.生成序列号
        // 2.1.获取当前日期,精确到天
        String date = now.format(DateTimeFormatter
        							.ofPattern("yyyy:MM:dd"));
        // 2.2.自增长
        long count = stringRedisTemplate.opsForValue()
        					.increment("icr:" + keyPrefix + ":" + date);

        // 3.拼接并返回
        return timestamp << COUNT_BITS | count;
    }
}

(3)总结

全局唯一ID生成策略:

  • UUID
  • Redis自增
  • 雪花算法
  • 数据库自增

Redis自增ID策略:

  • 每天一个key,方便统计
  • ID构造是时间戳 + 计数器

二、超卖问题

1、解决办法

超卖问题是典型的多线程安全问题,针对这一问题的常见解决方案就是加锁:

  • 悲观锁
    认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。例如Synchronized、Lock都属于悲观锁
  • 乐观锁
    认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其他线程对数据进行了修改。如果没有修改则认为是安全的,自己才更新数据;如果已经被其他线程修改,说明了安全问题,此时可以重试或异常。

2、乐观锁

乐观锁的关键是判断之前查询得到的数据是否有被修改过,常见的方式有两种:

(1)版本号法

在这里插入图片描述

(2)CAS法

在这里插入图片描述

(3)总结

悲观锁乐观锁
方案添加同步锁,让线程串行执行不加锁,在更新时判断是否有其他线程在修改
优点简单粗暴性能好
缺点性能一般存在成功率低的问题

四、分布式锁

五、Reids优化秒杀—异步执行

1、思路

(1)Lua脚本逻辑

判断库存是否充足:利用String类型
判断用户是否下单:利用Set类型
在这里插入图片描述

(2)java执行Lua脚本逻辑

在这里插入图片描述

(3)代码

-- 1.参数列表
-- 1.1.优惠券id
local voucherId = ARGV[1]
-- 1.2.用户id
local userId = ARGV[2]

-- 2.数据key
-- 2.1.库存key
local stockKey = 'seckill:stock:' .. voucherId
-- 2.2.订单key
local orderKey = 'seckill:order:' .. voucherId

-- 3.脚本业务
-- 3.1.判断库存是否充足 get stockKey
if(tonumber(redis.call('get', stockKey)) <= 0) then
    -- 3.2.库存不足,返回1
    return 1
end
-- 3.2.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call('sismember', orderKey, userId) == 1) then
    -- 3.3.存在,说明是重复下单,返回2
    return 2
end
-- 3.4.扣库存 incrby stockKey -1
redis.call('incrby', stockKey, -1)
-- 3.5.下单(保存用户)sadd orderKey userId
redis.call('sadd', orderKey, userId)
return 0
    private static final DefaultRedisScript<Long> SECKILL_SCRIPT;

    static {
        SECKILL_SCRIPT = new DefaultRedisScript<>();
        SECKILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));
        SECKILL_SCRIPT.setResultType(Long.class);
    }
    @Override
    public Result seckillVoucher(Long voucherId) {
        Long userId = UserHolder.getUser().getId();
        long orderId = redisIdWorker.nextId("order");
        // 1.执行lua脚本
        Long result = stringRedisTemplate.execute(
                SECKILL_SCRIPT,
                Collections.emptyList(),
                voucherId.toString(), userId.toString()
        );
        int r = result.intValue();
        // 2.判断结果是否为0
        if (r != 0) {
            // 2.1.不为0 ,代表没有购买资格
            return Result.fail(r == 1 ? "库存不足" : "不能重复下单");
        }
        // 3.发送消息队列 异步
        // 4.返回订单id
        return Result.ok(orderId);
    }

六、消息队列

消息队列(Message Queue),字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色:

  • 消息队列:存储和管理消息,也被成为消息代理。
  • 生产者:发送消息导消息队列
  • 消费者:从消息队列获取消息并处理消息。

Redis提供了三种不同的方式来实现消息队列:

  • list结构:基于Liist结构模拟消息队列
  • PubSub:基本的点对点消息模型
  • Stream:比较完善的消息队列模型

1、基于List结构模拟消息队列

消息队列就是存放消息的队列。而Redis的List数据结构是一个双向链表,很容易模拟。
队列是入口和出口不在一边,因此可以利用LPUSH结合RPOP来实现。
实现阻塞效果,应该使用BRPOP。

描述
优点1、利用Redis存储,不受限于JVM内存上限; 2、基于Redis的持久化机制,数据安全性有保障 3、可以满足消息有序性
缺点1、无法避免消息丢失 2、只支持单消费者

2、PubSub

发布订阅模式,消费者可以订阅一个或多个channel,生产者向对应channel发送消息后,所有订阅者都能收到相关消息。

  • SUBSCRIBE channel [channel] :订阅一个或多个频道
  • PUBLISH channel msg: 向一个频道发送消息
  • PSUBSCRIBE pattern [pattern] :订阅与pattern格式匹配的所有频道
    在这里插入图片描述
描述
优点1、采用发布订阅模式,支持多生产、多消费
缺点1、不支持数据持久化 2、无法避免消息丢失 3、消息堆积有上限,超出时数据丢失

3、Stream

(1)基本用法

是Redis 5.0引入的新数据
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
特点:

  • 消息可回溯
  • 一个消息可以被多个消费者读取
  • 可以阻塞读取
  • 有消息漏读的风险

(2)消费者组

消费者组(Consumer Group):将多个消费者划分到一个组中,监听同一个队列。特点如下:
在这里插入图片描述

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

确认pending-list
在这里插入图片描述
查看pendingList
在这里插入图片描述
特点:

  • 消息可回溯
  • 可以多消费者争抢消息,加快消费速度
  • 可以阻塞读取
  • 没有消息漏读的风险
  • 有消息确认机制,保证消息至少被消费一次

4、比较

ListPubSubStream
消息持久化支持不支持支持
阻塞读取支持支持支持
消息堆积处理受限于内存空间,可以利用多消费者加快处理受限于消费者缓冲区受限于队列长度,可以利用消费者组提高消息速度,减少堆积
消息回溯不支持不支持支持

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

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

相关文章

OpenCV-Python快速入门(十五):霍夫变换

OpenCV-Python快速入门&#xff08;十五&#xff09;&#xff1a;霍夫变换前言前提条件实验环境霍夫变换基本原理霍夫直线变换&#xff08;cv2.HoughLines()&#xff09;概率霍夫变换&#xff08;cv2.HoughLinesP()&#xff09;霍夫圆变换&#xff08;cv2.HoughCircles()&#…

移动端测试理论

App测试基础 App功能测试及专项测试 前言: 对于APP项目的测试&#xff0c;一般是进行系统测试。 测试主要从业务功能和非业务功能两个方面考虑。业务功能测试 根据软件说明&#xff0c;设计文档或用户需求验证App的各个功能的实现。 专项测试 兼容性测试 兼容性测试的关注点…

阿里Java研发面经(已拿offer)

一、自我总结&#xff1a; 1&#xff09;首先最重要的一点。对自己的要求高点。不要以简单的实习生来要求自己。你要想 你会的别人都会 你的核心竞争力是什么呢。所以楼主建议以Java高级工程师来要求自己。不会的就学嘛。人面对未知的事物 本能反应是恐惧与退缩。可当你尝试去…

Xxl-Job 初次体验

Xxl-Job 初次体验一、定时任务-前置知识二、演变机制三、xxl-Job 设计思想四、xxl-job 实战1. 调度中心部署2. 编写执行器简单使用一下2.1. 让执行器run起来&#xff01;2.2. 在调度中心配置任务&#xff0c;调度一下&#xff01;3. XxlJob 任务的生命周期4. 路由策略5. 父子任…

汇川PLC编程软件AutoShop的使用

文章目录一、数据类型二、系统参数.1、内存容量设置2、“掉电保持范围”设置3、系统设置三、符号表1、编辑符号表2、符号表的打印四、元件监控表1、新建元件监控表2、编辑元件监控表3、复制元件监控表4、快速监控表五、元件使用信息表六、交叉引用表七、软元件内存表1、新建和复…

windows的小米11真机appium微信爬虫

1、下载appium 仓库地址 2、下载python的包 pip install Appium-Python-Client -i https://pypi.tuna.tsinghua.edu.cn/simple 3、下载android-sdk 先下SDK Tools 国内一个镜像网站 参考这个教程 安装好后&#xff0c;运行这个SDK Manager.exe 然后install&#xff0c;同意协…

Alphalens使用方法细节判断

首先alphalens的数据格式&#xff1a; factor: MultiIndex&#xff08;用stack()方法来转换&#xff09; prices: DataFrame #转换成MultiIndex factor alpha_mom.stack() print (factor.tail()) datetime 2017-11-20 15:00:00 601857.XSHG 1…

小学生python游戏编程arcade----烟花粒子

小学生python游戏编程arcade----烟花粒子前言烟花粒子1、Vector向量类1.1 arcade中的向量类1.2 应用2、绘制粒子所有纹理图片2.1 给定直径和颜色的圆的纹理2.2 arcade.make_circle_texture函数原码2.3 make_soft_circle_texture 函数原码2.4 公共纹理代码3 效果图4 代码源码获取…

【读点论文】Densely Connected Convolutional Networks用残差连接大力出奇迹,进一步叠加特征图,以牺牲显存为代价

Densely Connected Convolutional Networks Abstract 如果卷积网络在靠近输入的层和靠近输出的层之间包含较短的连接&#xff0c;则卷积网络可以训练得更深入、更准确和有效。在本文中&#xff0c;接受了这种观察&#xff0c;并介绍了密集卷积网络(DenseNet)&#xff0c;它以…

Linux - Linux下Java安装路径查找;配置Java环境变量

一、查看Java的安装路径 1、已经安装好了JDK&#xff0c;也配置了环境变量 1、执行 java -version java -version 出现了版本号&#xff0c;表示安装过了JDK&#xff0c;配置了环境变量 2、在配置过jdk的情况下&#xff0c;执行java -verbose指令&#xff0c;在打印出的文本…

java stream中的peek()用法

文章目录前言最终操作&#xff08;terminal operation&#xff09;peek() vs forEach()peek() 的典型用法&#xff1a;协助调试总结前言 最近看到一段代码&#xff1a; aeFormList.stream().peek(object -> saveInfomation(object, params)).collect(Collectors.toList())…

std::shared_ptr(基础、仿写、安全性)

目录 一、c参考手册 1、解释说明 2、代码示例 3、运行结果 二、对std::shared_ptr分析 1、shared_ptr基础 2、创建shared_ptr实例 3、访问所指对象 4、拷贝和赋值操作 5、检查引用计数 三、仿写std::shared_ptr代码 1、单一对象 2、数组对象 四、shared_ptr遇到问…

MyBatis 环境搭建

MyBatis 环境搭建步骤 1.创建一张表和表对应的实体类 2.创建一个 maven 项目&#xff0c;把项目添加到 git 仓库 创建maven项目 教程见&#xff1a;Maven[项目构建工具]_chen☆的博客-CSDN博客 添加到git仓库&#xff1a; 3.在文件 pom.xml 添加 mybiatis 相关依赖(导入 MyBa…

Java - 利用Nacos做一个动态开关配置功能

Java - 利用Nacos做一个动态开关配置功能前言一. Nacos配置类编写二. 测试三. 展望前言 我公司里有一个Config配置功能&#xff08;我相信这是很普遍的一个功能&#xff09;。简单来说就是&#xff1a; 将相关的键值对放到这个Config配置系统里面。代码里通过这个Config配置系…

博客项目(前台功能实现)

博客项目(前台接口实现) 文章目录博客项目(前台接口实现)1.前置知识1.1Controller1.1.1ResponseResult类1.1.2该类的方法1.2Service1.3ServiceImpl1.4Mapper1.5Vo的理解1.6可能会用到的相关插件1.7设置字面量1.8后端接口测试工具2.热门文章接口分析2.1热门文章接口位置2.2接口的…

Internet Download Manager2023最新版下载器功能介绍

说到下载器在国内就不得不提迅雷&#xff0c;迅雷真是伟大&#xff0c;几乎垄断了国内的下载市场&#xff0c;的确&#xff0c;有的时候用迅雷可以下载到很不错的资源&#xff0c;但在没有VIP的情况下&#xff0c;迅雷是不友好的&#xff0c;相信使用迅雷的各位都有被限速过的经…

三、Eureka

文章目录一、认识服务提供者和服务调用者二、Eureka 的工作流程三、服务调用出现的问题及解决方法四、搭建 eureka-server五、注册 user-service、order-service六、在 order-service 完成服务拉取&#xff08;order 模块能访问 user 模块&#xff09;七、配置远程服务调用八、…

分布式锁:不同实现方式实践测评

Hello读者朋友们&#xff0c;今天打算分享一篇测评实践类的文章&#xff0c;用优雅的代码与真实的数据来讲述在分布式场景下&#xff0c;不同方式实现的分布式锁&#xff0c;分别探究每一种方式的性能情况与最终的优劣分析。 开门见山&#xff0c;我们先看一张表格&#xff0c…

fiddler安卓模拟器与ios手机抓包

一.安卓模拟器(雷电模拟器)抓包 1.1fiddler基本配置 1.2导出Fiddler证书 Tools -> Options -> HTTPS -> Actions -> Export Root Certificate to Desktop 在桌面上看到导出的FiddlerRoot.cer证书文件 1.3下载和安装openssl openssl下载地址 git终端输入 open…

蜂鸟E203学习(一)--RISC的前世今生

第一章 CPU之前世今生 1.1、CPU众生相 1.1.1 处理器&#xff08;cpu&#xff09;和处理器内核&#xff08;core&#xff09;的区分 处理器严格意义上是soc&#xff0c;包含了内核和其他设备或者存储器. 1.1.2 不同CPU架构的诞生时间 CPU架构诞生时间Intel 80861978年ARM19…