Redis——实现优惠券秒杀

全局唯一ID生成器 当数据规模达到一定量级的时候会影响到数据库的性能,那么这个时候我们一般会沿着AKF的Z轴使用分库分表的策略,以降低单表的数据量,从而提高数据库的性能。但是分库分表后,我们怎么保证ID的全局唯一性呢?这个时候ID生成器就登场了。 ID应该遵循的原则 1、ID应该是按时间有序的,因为在某些场景上可能会用到,比如获取商品的评论,一般需要按照评论的倒叙显示,如果评论ID是无序的那边就需要添加额外的字段排序。另外ID如果是有序,可以提升数据库的性能,因为有序的ID,对于关系型数据库来说可以有效的实现插入数据的顺序写磁 阅读详情

目录

全局ID生成器

ID应该遵循的原则

Redis实现全局唯一id(采用上图第一种)

总结:

实现优惠券秒杀下单

大致流程

乐观锁的实现方法

总结

一人一单

模拟集群下的并发安全问题

后续链接:


全局ID生成器

        当数据规模达到一定量级的时候会影响到数据库的性能,那么这个时候我们一般会沿着AKF的Z轴使用分库分表的策略,以降低单表的数据量,从而提高数据库的性能。但是分库分表后,我们怎么保证ID的全局唯一性呢?这个时候ID生成器就登场了。

ID应该遵循的原则

1、ID应该是按时间有序的,因为在某些场景上可能会用到,比如获取商品的评论,一般需要按照评论的时间倒序显示,如果评论ID是无序的那边就需要添加额外的字段排序。另外ID如果是有序,可以提升数据库的性能,因为有序的ID,对于关系型数据库来说可以有效的实现插入数据的顺序写磁盘,如果ID是无序的,那么每次写入的位置都不一样是随机写,更严重的是可能需要移动数据与页分裂,从而导致页空洞,不能高效的利用磁盘空间;
2、从ID中应该能够反解出ID所属的业务,这样在排查问题的过程中可能有帮助;
3、ID占用内存应该足够小,最好能够用一个64bit的整数表示,从而能够提升数据库的性能,节约内存与磁盘空间。


        基于如上的原因,UUID虽然很高效且唯一,但显然是不适合作为全局ID的,因为其不是有序的,没有业务含义,同时还需要占用比较多的空间。
        为此我们一般用snowflake算法(雪花算法)或者通过数据库生成ID做为全局ID。

snowflake算法(雪花算法)
snowflake算法生成id的结果是一个64bit大小的整数,它的结构如下图:

第一种:

第二种:

在这里插入图片描述

下面简单介绍一下第二种的:
        1位,不用。二进制中最高位为1的都是负数,但是我们生成的id一般都使用整数,所以这个最高位固定是0;
        41位,用来记录时间戳(毫秒)。41位可以表示2^41个数字,也就是说41位可以表示2^41个毫秒的值,转化成单位年则是2^41/(1000*60*60*24*365)约等于69年;
        10位,用来记录工作机器id。可以部署在2^10=1024个节点,包括5位IDC Id和5位workerId,可以表示的最大正整数是2^5−1=31;
        12位,序列号,用来记录同毫秒内产生的不同id。 12位(bit)可以表示的最大正整数是2^12−1=4095,即同一机器同一时间截(毫秒)内产生的4096个ID序号。


在实际使用过程中,我们一般会做如下的调整:
1、适当调整不同位数的含义:如时间戳字段可以以10ms为单位,可以用40bit;工作机器id可以做一些拆分把业务id也放进去或者IDC id与workerId的位数做一些调整;序列号的位数也可以适当做一些调整;
2、如果系统的规模不大,ID生成可以内嵌到业务代码中,这样ID生成就会非常的高效,也不会对系统的性能产生影响,但是系统主机的个数一般还是比较多的,这时就得引入第三方组件来管理主机id;
3、如果系统的规模比较大,我们可以在一个机房中部署2个或更多个ID生成服务,每个机房尽量调用本机房的ID生成服务。这样每次获取ID多增加的时间成本也很有限,如果还想省却这个时间,业务可以批量获取,不过一般不建议批量获取。


不过 snowflake算法也有如下的问题:
1、我们一般需要打开时钟同步功能,这样ID才能够最大化的保证按照时间有序,但是时钟同步打开后,就可能会时钟回拨了,如果时钟回拨了,那么生成的ID就会重复,为此我们一般打开时钟同步的同时关闭时钟回拨功能;
2、序列号的位数有限,能表示的ID个数有限,时钟同步的时候,如果某台服务器快了很多,虽然关闭了时钟回拨,但是在时间追赶上前,ID可能已经用完,当自增序列号用完了,我们可以做如下的工作:停止ID生成服务并告警、如果时钟回拨小于一定的阈值则等待、如大于一定的阈值则通过第三方组件如ZK重新生成一个workerid或者自增时间戳借用下一个时间戳的ID;
3、服务重启后,ID可能会重复,为此我们一般需要定期保存时间戳,重启后的时间戳必须大于保存的时间戳+几倍保存间隔时间(如3倍),为什么要几倍呢,主要是考虑到数据丢失的情况,但是如果保存到本地硬盘且每次保存都fsync,此时1倍即可。重启后如果小于可以像第二点那样类似处理;
4、如果请求ID的QPS不高,比如每毫秒一个,那么每次获取的ID的尾号都是0,那么基于ID做分库分表,可能数据分布就会不均,此时我们可以增加时间戳的时间间隔或者序列号每次从随机一个值开始自增。

更多方案介绍参见:
链接:https://tech.meituan.com/2017/04/21/mt-leaf.html


Redis实现全局唯一id(采用上图第一种)

@Component
public class RedisIdWorker {
    /**
     * 时间戳
     */
    private static final long BEGIN_TIMESTAMP = 1640995200L;

    /**
     * 序列号的位置
     */
    private static final int COUNUT_BITS = 32;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    //keyPrefix区分业务逻辑
    public long nextId(String keyPrefix){
        //1.生成时间戳
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timeStamp = nowSecond - BEGIN_TIMESTAMP;

        //2.生成序列号
        //不能永远使用一个key,不然可能会超过上限(2^32),所以我们可以拼上时间戳
        //2.1获取当前日期
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        //2.2自增长
        Long count = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);

        //3.拼接并返回
        return timeStamp<<32|count;
    }

    public static void main(String[] args) {
        LocalDateTime time = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
        long second = time.toEpochSecond(ZoneOffset.UTC);
        //看看开始的时间戳
        System.out.println("Second = "+second);
    }
}

总结:

UUID是16进制的字符串结构,而且并不是单调递增,不满足上述全局Id特性,但并不友好

Redis自增实现较为简单

雪花算法,多了个机器Id,性能理论上会比Redis,但对时钟敏感

数据库自增,是指重新拿一张某一张特定的表代替自增

实现优惠券秒杀下单

两个表的结构展示

大致流程

 

涉及多表之间的操作,一般需要加上事务(@Transactional)

实现代码:

    @Override
    @Transactional
    public Result seckillVoucher(Long voucherId) {
        //1.查询优惠券信息
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2.判断秒杀是否开始
        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }

        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }

        //3.判断库存是否足够
        if(voucher.getStock()<1){
            return Result.fail("已售空");
        }

        seckillVoucherService.update()
                .eq("voucher_id",voucherId)
                .setSql("stock = stock - 1")
                .update();

        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();

        //获取用户Id
        UserDTO user = UserHolder.getUser();
        Long userId = user.getId();
        voucherOrder.setUserId(userId);

        //设置优惠券Id
        voucherOrder.setVoucherId(voucherId);

        //设置订单Id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);

        save(voucherOrder);

        //返回订单Id
        return Result.ok(orderId);
    }


在高并发场景下,可能会出现“超卖“现象 ” 

正常情况:

"超卖" 情况:

解决多线程安全问题:悲观锁与乐观锁

乐观锁的实现方法

版本号法:通过添加版本号,根据每次查询的版本号是否已经更新,来判断数据是否已经更新

 CAS法:在版本号的基础上进行了优化,我们可以通过对之前查询得到的数据是否更新,来判断数据是否已经更新,充当了版本号的作用

        

虽然保证了不会出现“超卖”现象,但是假设如果有100个线程在同一时间都购入该消费券,仅有第一个才能成功,所以我们需要对以上的乐观锁进行一定的修改,不一定要将之前查询的数据相等于现在的库存才可以进行购买,只需库存大于0的时候就可以通过

实现代码:

    @Override
    @Transactional
    public Result seckillVoucher(Long voucherId) {
        //1.查询优惠券信息
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2.判断秒杀是否开始
        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }

        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }

        //3.判断库存是否足够
        if(voucher.getStock()<1){
            return Result.fail("已售空");
        }

        seckillVoucherService.update()
                .setSql("stock = stock - 1")
                .eq("voucher_id",voucherId).gt("stock",0)//仅在这里添加新的判断条件即可
                .update();

        //创建订单
        VoucherOrder voucherOrder = new VoucherOrder();

        //获取用户Id
        UserDTO user = UserHolder.getUser();
        Long userId = user.getId();
        voucherOrder.setUserId(userId);

        //设置优惠券Id
        voucherOrder.setVoucherId(voucherId);

        //设置订单Id
        long orderId = redisIdWorker.nextId("order");
        voucherOrder.setId(orderId);

        save(voucherOrder);

        //返回订单Id
        return Result.ok(orderId);
    }

总结


一人一单

一人一单同时也要考虑高并发的情况,添加synchronized关键字上锁,不在方法上添加(因为这样锁的范围大,任何一个用户来都要加上锁,一人一单所以只需对该用户上锁,即Id上锁,把锁的范围缩小,提升效率)而是对userId上锁,同时也需要注意,每次请求的userId对象都是一个全新的Id对象,因此对象变了锁就变了,所以要求值一样,用了userId.toString(),但toString方法内是返回一个new出来的对象,还会在变,所以再后面添加intern()方法,返回字符串的规范表示(如果池中已经包含一个等于这个String对象(由equals(Object)方法确定的字符)则返回池中的字符串;否则将String添加到池内并返回该对象的引用)

   @Override
    public Result seckillVoucher(Long voucherId) {
        //1.查询优惠券信息
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //2.判断秒杀是否开始
        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }

        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
            return Result.fail("秒杀尚未开始");
        }

        //3.判断库存是否足够
        if(voucher.getStock()<1){
            return Result.fail("已售空");
        }
        
        //创建订单
        return createVoucherOrder(voucherId);
    }
    
    @Transactional
    public Result createVoucherOrder(Long voucherId) {
        //获取用户Id
        UserDTO user = UserHolder.getUser();
        Long userId = user.getId();

        synchronized (userId.toString().intern()){
            //一人一单
            Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            if(count>0){
                //说明用户之前买过了
                return Result.fail("用户已经购买过一次");
            }

            VoucherOrder voucherOrder = new VoucherOrder();

            //扣减库存
            seckillVoucherService.update()
                    .setSql("stock = stock - 1")
                    .eq("voucher_id",voucherId).gt("stock",0)
                    .update();

            //设置订单Id
            long orderId = redisIdWorker.nextId("order");
            voucherOrder.setId(orderId);
            //设置优惠券Id
            voucherOrder.setVoucherId(voucherId);
            //设置用户Id
            voucherOrder.setUserId(userId);

            save(voucherOrder);

            //返回订单Id
            return Result.ok(orderId);
        }
    }

模拟集群下的并发安全问题

模拟集群

同一个用户,两个请求同时到达,锁没有锁住,有两个订单产生;原因是因为在集群模式下或在一些分布式系统下,有多个JVM的存在,每个JVM都有自己的锁,导致每一个锁都可以由一个线程获取,就出现了并行运行的状况,出现了问题,这就要求我们实现多个JVM的锁得是同一把锁

后续链接

基于Redis的分布式锁实现(秒杀优惠券的优化)_486过于烦躁的博客-优快云博客

Redis实现优惠券秒杀业务 优惠券分为两种,一种是普通优惠券,优惠力度较小,另一种是秒杀优惠券,秒杀优惠券优惠力度大,但是数量有限并且限定时间抢购。用户购买优惠券时首先会看优惠券是否有库存,如果有还会判断优惠券是否开始抢购。当用户抢购时,就会生成订单并保存到order表中,而订单表如果使用数据库自增ID就会存在一些问题:id的规律性太明显受单表数据量的限制场景分析一:如果我们的id具有太明显的规则,用户或者说商业对手很容易猜测出来我们的一些敏感信息,比如商城在一天时间内,卖出了多少单,这明显不合适。 阅读详情

相关推荐

阿里云百炼平台创建智能体-上传文档

阿里云百炼智能体-上传文档文件

weixin_43930851的博客 1358

maven Unknown lifecycle phase “.hsa.hsaf.core“.

maven报错Unknown lifecycle phase “.hsa.hsaf.core“.

yxb_xb的博客 1018

亚马逊SP-API授权实战:从OAuth流程到Refresh Token获取

本文详细解析了亚马逊SP-API授权流程,从OAuth认证到Refresh Token获取的全过程。通过实战经验分享,帮助开发者快速掌握Marketplace Appstore、Website Workflow和自行授权三种方式,解决常见错误如MD5100和MD9999,并提供了安全存储Refresh Token的最佳实践。

weixin_30379531的博客 519

IdGenerator 唯一Id生成器

有些时候我们可能会遇见数据库生成的ID过长,超过了前端 JS Number 类型最大值,须把 Long 型转换为 String 型,如果不转换就会出现报错现象。这个时候我们就可以使用。来生成ID且不会重复。

m0_53434091的博客 2534

鸿蒙之HML语法参考

HML(HarmonyOS Markup Language)是一套类HTML的标记语言,通过组件,事件构建出页面的内容。页面具备数据绑定、事件绑定、条件渲染和逻辑控制等高级能力。

2401_82772199的博客 1649

【文本分析学习】Anaconda+pytorch虚拟环境下的pycharm文本分析(含分词、词性标注、词形归一化、停用词、文本情感分析、词频、文本相似度,附数据代码) NLTK

文本分析学习之 Anadconda+pytorch虚拟环境下的pycharm文本分析(含分词、词性标注、词形归一化、停用词、文本情感分析、词频、文本相似度,附数据代码)

littlexmj的博客 2333

java集群环境下redis分布式锁实现

背景 在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等。大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对Redis的连接并不存在竞争关系。其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制 使用redis相关命令介绍 stringRedisTemplate.opsForValue().setIfAbsent(key, value) 如果key存在就返回0,不存在则设置

qq_38730338的博客 634

Ubuntu22.04下ROS2与PX4仿真环境搭建全流程解析

本文详细解析了在Ubuntu22.04系统下搭建ROS2与PX4仿真环境的全流程,包括系统准备、ROS2 Humble安装、PX4飞控环境部署、ROS2与PX4通信桥梁搭建等关键步骤。针对常见问题提供了解决方案,并分享了性能优化和开发调试技巧,帮助开发者快速构建无人机仿真开发环境。

weixin_29248653的博客 607

基于Redis自增实现全局ID生成器(详解)

本文详细介绍了基于Redis自增实现全局ID生成器的思路,同时给出了实现代码。

向上的咸鱼blueblue的博客 6564

RK3568 RGA硬件加速详解:图像缩放、旋转、格式转换10倍提速

RGA(Raster Graphics Acceleration)是瑞芯微(Rockchip)芯片系列中的 2D 图形加速引擎,专门用于处理像素级的图像操作。它通过硬件实现常见的图像处理算法,解放 CPU,降低功耗。RGA 是 RK3568 平台上一颗强大的 2D 图形加速引擎,能够:✅10-20 倍性能提升:大幅减少图像处理时间✅降低 CPU 占用:释放 CPU 资源给其他任务✅丰富的功能:缩放、旋转、裁剪、格式转换一应俱全✅易于集成:librga 提供简洁的 C API。

小灰灰的博客 1697

RagFlow专题三、RagFlow 关键技术(向量数据库、文档分块、Prompt 设计与召回排序优化)

FAISS 是由 Meta(Facebook)开源的高效向量检索库,适用于大规模向量搜索任务。,并了解了如何通过 RagFlow 机制优化信息检索和生成质量。(FAISS、Milvus、ES)用于高效存储和检索语义向量。提高 LLM 生成效果,增强 RagFlow 任务质量。(分块)策略,将文档拆分成更小的片段,提高检索效率。提高检索精准度,避免文本过长导致信息丢失。提升检索结果的相关性,提高生成质量。当多个检索结果被返回时,我们需要一个。在前一篇文章中,我们详细探讨了。在 RagFlow 任务中,

xiaoqi270620903的专栏 3041

Redis实现优惠券秒杀功能

例如,使用Long类型,占8个字节,64bit位,1bit符号位,前31bit使用时间戳,支持69年使用时间,后32位使用序列号,每秒产生2^32个不同ID。tb_voucher包含平价券和特价券,tb_seckill_voucher只描述特价券,包含库存,生效时间,失效时间。优点是性能好,缺点是有可能出现少买。缺点,用户还是能下多单,因为多个线程判断成功后可以继续购买,和后续扣减库存,生成订单不是原子性操作。2、修改lua脚本,向stream.orders中添加消息,包含优惠券id,用户id,订单id。

qq_41523340的博客 1309

【AutoCAD中实现双击文本编辑功能】

例如:以下是官方博客例子浅析先加入相关的头文件和库文件。在AutoCAD中有一个很重要的功能,就是双击图形尺寸或文本后会出现一个文本编辑框,用户可在该编辑框中修改文字。本文介绍了如何使用AcEdInplaceTextEditor来实现该功能。实现cad的消息监听。创建一个消息监听的类并在cad中注册来实现消息监听。主要是监听键盘输入。提示:以下是本篇文章正文内容,下面案例可供参考提示:这里对文章进行总结:例如:以上就是今天要讲的内容,本文仅仅简单介绍了的函数和方法。

我的博客 1149

基于Redis实现优惠券秒杀——第4期(秒杀优化)

如果充足,则在Redis中判断用户是否可以下单,如果set集合中没有该用户的下单数据,则可以下单,并将userId和优惠券存入到Redis中,并且返回0,整个过程需要保证是原子性的,所以我们要用Lua来操作,同时由于我们需要在Redis中查询优惠券信息,所以在我们新增秒杀优惠券的同时,需要将优惠券信息保存到Redis中。在这六个步骤中,有很多操作都是要去操作数据库的,而且还是一个线程串行执行,这样就会导致我们的程序执行很慢,所以我们需要异步程序执行,那么如何加速呢?但是这里还存在两个难点。

m0_74016429的博客 1230

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

SpringBoot整合Redis实现优惠券秒杀服务

Springboot整合Redis实现优惠券秒杀服务

qq_45949914的博客 1865

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

redis实现优惠券秒杀

现在我们的数据库中根本没有订单,现有一百个线程并发的查询之前是否有订单,查询到的都是0,然后就插入了n多个数据,也是多线程问题,常见的方式是用锁解决,因为现在数据不存在,没法判断数据是否被修改过,所以不能用乐观锁,可以加悲观锁解决

PiggyOne123的博客 271

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

redis实战-实现优惠券秒杀&解决超卖问题

使用redis实现优惠券秒杀功能,解决优惠券超卖问题,一人一单问题

weixin_64133130的博客 3362

【Redis实战篇】Redis的擅长实现的功能 优惠券秒杀、分布式锁、分布式锁Redission、Redis消息队列、秒杀优化

基于setnx实现的分布式锁存在下面的问题重入问题:可重入锁(Reentrant Lock)是一种多线程同步机制,允许线程在持有锁的情况下再次请求相同的锁,而不会被自己所持有的锁所阻塞。这种锁的主要特性是它允许一个线程多次获取同一把锁而不会造成死锁。比如HashTable这样的代码中,他的方法都是使用synchronized修饰的,假如他在一个方法内,调用另一个方法,那么此时如果是不可重入的,不就死锁了吗?所以可重入锁他的主要意义是防止死锁,我们的synchronized和Lock锁都是可重入的。

2301_77358195的博客 961

基于Redis实现优惠券秒杀——第2期(分布式锁)

分布式锁的核心思想就是让大家共用同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路。注意:这里说的可见性并不是并发编程中指的内存可见性,只是说多个进程之间都能感知到变化的意思。分布式锁:满足分布式系统或集群模式下多线程课件并且可以互斥的锁。那么分布式锁应该满足一些什么条件呢?常见的分布式锁有三种。

m0_74016429的博客 726

基于Redis实现优惠券秒杀——第1期(解决超卖问题、一人一单问题)

失败原因分析:由于我们部署了多个Tomcat,每个Tomcat都有一个属于自己的jvm,那么假设在服务器A的Tomcat内部,有两个线程,即线程1和线程2,这两个线程使用的是同一份代码,那么他们的锁对象是同一个,是可以实现互斥的。但是如果在Tomcat的内部,又有两个线程,但是他们的锁对象虽然写的和服务器A一样,但是锁对象却不是同一个,所以线程3和线程4可以实现互斥,但是却无法和线程1和线程2互斥。发送两次请求,header携带同一用户的token,尝试用同一账号抢两张优惠券,发现是可行的。

m0_74016429的博客 984

【Redis实战篇】秒杀优惠券优化(基于Lua脚本,阻塞队列,异步线程实现)

static {

MonKingWD的博客 740

基于 Redis 实现优惠券秒杀和一人一单

基于 Redis 实现优惠券秒杀和一人一单

快乐的小三菊的博客 1088

Redis实现优惠券秒杀

全局ID生成器,是一种在分布式系统下用来生成全局唯一ID的工具,一般要满足下列特性:为了增加ID的安全性,我们可以不直接使用Redis自增的数值,而是拼接一些其它信息: ID的组成部分:类似于雪花算法 全局唯一ID生成策略:每个店铺都可以发布优惠券,分为平价券和特价券。平价券可以任意购买,而特价券需要秒杀抢购: 超卖问题是典型的多线程安全问题,针对这一问题的常见解决方案就是加锁: 乐观锁的关键是判断之前查询得到的数据是否有被修改过,常见的方式有两种: 解决办法 使用lua脚本实现Redis命令的原

black____的博客 1140

基于Redis实现高并发优惠券秒杀系统:全局ID、超卖控制与一人一单

本文介绍了如何利用Redis实现高并发环境下的优惠券秒杀系统。主要内容包括: 全局唯一ID生成方案:通过Redis自增特性配合时间戳,实现分布式系统的唯一ID生成,满足高并发需求。 优惠券管理实现: 区分平价券和特价券 特价券需记录库存、抢购时间等额外信息 将秒杀库存同步到Redis 秒杀下单流程: 校验秒杀时间有效性 检查库存充足性 扣减库存 创建订单 系统设计解决了高并发环境下的ID唯一性问题,并通过Redis预存库存提高秒杀性能。后续可进一步优化超卖问题和一人一单限制。该方案利用Redis的原子操作特

weixin_45919016的博客 978

【Redis实战篇】秒杀实现方案(以优惠券秒杀为例)

本文介绍了分布式系统中全局唯一ID的几种生成策略,重点分析了Redis自增ID的实现方案及其代码示例。针对优惠券秒杀场景,详细阐述了超卖问题的解决方案(悲观锁和乐观锁),并通过流程图说明了一人一单的业务逻辑。文章还提供了乐观锁的两种实现方式(版本号法和CAS法)及其优缺点比较,最后给出了基于Spring AOP的代码实现示例,帮助开发者解决高并发下的订单重复提交问题。

YL20040426的博客 452

基于Redis实现优惠券秒杀——第3期(分布式锁-Redisson)

为了保证原子性,所以流程图中的业务逻辑也是需要我们用Lua来实现的。2.配置Redisson客户端,在config包下新建。3.使用Redisson的分布式锁。4.替换我们上一期自己写的分布式锁。

m0_74016429的博客 1421
上一篇: ThreadLocal的三种使用场景(转发)
下一篇: 基于Redis的分布式锁实现(秒杀优惠券的优化)
486过于烦躁
博客等级 码龄6年 17粉丝 34原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值