广告
返回顶部
首页 > 资讯 > 后端开发 > Python >Redis实现商品秒杀功能页面流程
  • 171
分享到

Redis实现商品秒杀功能页面流程

2024-04-02 19:04:59 171人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录全局唯一ID 业务逻辑分析代码实现优惠券秒杀业务逻辑分析代码实现定量商品多卖问题业务逻辑分析乐观锁与悲观锁乐观锁代码实现一个用户限买一单业务逻辑分析代码实现全局唯一ID

全局唯一ID 

业务逻辑分析

  全局唯一ID是针对销量比较大的一些商品而言的,这类商品的成交量比较多,用户购买成功就会生成对应订单信息并保存到一张表中,而订单表的id如果使用数据库自增ID就存在一些问题,比如说id的规律性太强导致安全性极低,还有如果订单数量太多一张表存不下分成多张表存储的话就会出现ID冲突问题,于是我们需要一个全局ID生成器,保证ID在全局中都是唯一的

  使用Redis即可完成这种全局ID生成器的功能,具体实现就是一种类雪花算法,也就是符号位、时间戳、序列号三部分拼接形成一个ID,逻辑就是符号位0代表整数,时间戳确定具体到下订单的时候是哪一秒,至于序列号就是用于区分这一秒的订单,序列号使用redis的值自增来保证所有序列号不一致,原则上一秒中最多可以有232个不同的ID

代码实现

@Component
public class RedisIdGenerator {
    
    private StringRedisTemplate stringRedisTemplate;
    public RedisIdGenerator(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }
    // 定义序列号的位数
    private static final int COUNT_BITS = 30;
    public long nextId(String keyPrefix) {
        // 生成从指定时间到现在的时间戳
        LocalDateTime beginTime = LocalDateTime.of(2022, 1, 1, 0, 0, 0);
        long beginTimeStamp = beginTime.toEpochSecond(ZoneOffset.UTC);
        long endTimeStamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
        long timeStamp = endTimeStamp - beginTimeStamp;
        
        String date = LocalDateTime.now().fORMat(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        Long sequenceId = stringRedisTemplate.opsForValue().increment("icr:" + keyPrefix + ":" + date);
        // 拼接生成全局唯一ID并返回 两个二进制的拼接可以使用前一个数左移一定位数 后一个数与位移后的进行或运算
        return timeStamp << COUNT_BITS | sequenceId;
    }
}

优惠券秒杀

业务逻辑分析

  用户对秒杀商品下单的时候,后台业务需要先完成对商品时间的判断,判断该商品的秒杀活动是否开始或者有没有结束,但凡还未开始或者已经结束都无法下单;时间信息正确的话就判断该商品的活动库存还有没有剩余,如果已经卖完的话也无法下单。时间和库存的判断都是通过前端传过来的优惠券id,查出来该优惠券的时间和库存信息,如果条件都满足的话,将该商品券的库存扣除,然后创建订单返回订单id

代码实现

  controller层主要就是调用service接口里的secKillVoucher方法,所以整个业务逻辑代码全部都在接口的实现类中完成

@Resource
private ISeckillVoucherService seckillVoucherService;
@Resource
private RedisIdGenerator generator;
@Override
@Transactional
public Result secKillVoucher(Long voucherId) {
    // 查询优惠券
    SeckillVoucher seckillVoucher = seckillVoucherService.getById(voucherId);
    // 获取时间 判断秒杀活动是否开始或者结束
    if (seckillVoucher.getBeginTime().isAfter(LocalDateTime.now())) {
        return Result.fail("活动暂未开始");
    } else if (seckillVoucher.getEndTime().isBefore(LocalDateTime.now())) {
        return Result.fail("活动已经结束");
    }
    // 判断库存是否充足
    if (seckillVoucher.getStock() < 1) {
        return Result.fail("库存不足,活动结束");
    }
    // 扣减库存
    seckillVoucherService.update()
            .setsql("stock = stock - 1")
            .eq("voucher_id", voucherId).update();
    // 创建订单 并返回id
    VoucherOrder order = new VoucherOrder();
    // 订单id(redis全局唯一id) 下单用户id(拦截器中做登录验证的用户id) 优惠券id(直接传过来的id)
    long orderId = generator.nextId("order");
    order.setId(orderId);
    order.setUserId(UserHolder.getUser().getId());
    order.setVoucherId(voucherId);
    save(order);
    return Result.ok(orderId);
}

定量商品多卖问题

业务逻辑分析

  像上面的优惠券秒杀的业务,优惠券或者商品的数量一般都是固定的,如果把这些数量都卖完之后应该就结束这个活动。但是现实中的秒杀业务都是多线程的,很多的用户同时等着活动开启一起点击下单,这样的话就极有可能出现线程安全问题也就是说最终成交的数量要多于活动商品的数量

  上述问题出现的原因就是多线程之间的执行顺序所引起,我们的秒杀业务里面是先查询库存数量大于1就产生订单,但是多线程之间的执行不会严格的按照这个顺序执行,而是交叉执行,如果最后只剩一张票的时候进来了两个线程AB,A查完B查AB查询结果都可以下单,A产生订单B再产生订单,此时就已经产生超卖

乐观锁与悲观锁

  解决线程问题的最好方法就是加锁,但是锁也分为悲观锁和乐观锁,悲观锁认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行,例如Synchronized、Lock等。乐观锁认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其它线程对数据做了修改,如果没有修改则更新数据,修改说明发生了安全问题

  很显然乐观锁的性能要显著高于悲观锁,因此采用乐观锁保证线程的原子性。乐观锁又有两种解决方案:版本号是指对修改的数据附带一个version字段值,每次更新的时候判断修改时的version与查询的时候是否一致,一致则修改。CAS机制全称为Compare And Swap译为先比较再交换,也就是将修改的数据本身作为版本号,每次更新的时候判断修改时的数据值与查询时的值是否相同,相同则修改,不同就说明发生了线程安全问题,在我们的这个售卖业务中,可以设置成只要库存大于0就可以执行成功

乐观锁代码实现

  乐观锁的核心就是,在更新数据的时候(也就是减少库存),判断一下库存是否大于0,如果判断失败的话也应该使该线程任务失败

// 扣减库存
boolean update = seckillVoucherService.update()
        .setSql("stock = stock - 1")
        .eq("voucher_id", voucherId)
        .gt("stock", 0)
        .update();
// 更新失败说明在扣除库存的时候 库存小于等于0
if (!update) {
    return Result.fail("库存不足!");
}

一个用户限买一单

业务逻辑分析

  按照正常的业务逻辑,秒杀应该限制一个用户只能购买一次该商品,最简单的方法就是对user_id使用唯一索引,如果user_id重复就会抛出相关异常,但是这需要修改表结构。如果不修改标结果的话就需要扣除库存之前根据voucher_id和user_id查询订单表,如果存在的话就返回错误,否则说明该用户还未购买

代码实现

  单机(服务部署在一台Tomcat服务器)的情况下,加synchronized 锁即可解决(查询判断用户是否下单和创建订单)业务的线程安全问题,但是这种情况就只能

// 单用户id(拦截器中做登录验证的用户id)
Long userId = UserHolder.getUser().getId();
// 根据user_id加锁  intern方法是去字符常量池中查找值相同的,不加的话字符串值一样的地址不一样也会加上锁
synchronized (userId.toString().intern()) {
	// 查询优惠券
	// 判断库存是否充足
    // user_id和voucher_id联合查询订单数
    Integer count = query().eq("user_id", userId)
            .eq("voucher_id", voucherId)
            .count();
    // 订单数为1 就说明已经下过单了
    if (count.equals(1)) {
        return Result.fail("您已经购买过该商品了");
    }
    // 扣减库存  创建订单
    return Result.ok(orderId);
}

  以上加synchronized 锁的解决方案只适用于单机模式下,此时所有的请求过来都会按照userId去常量池中查找是否一致,一致的话就锁在一起防止一个用户购买多单。但是集群模式下所有的请求会经过Nginx负载均衡轮询发送到集群上的所有服务器,如果一个用户的多个请求被分配到不同的服务器上的话,不同服务器中的JVM虚拟机里的静态常量池中的内容是不同步的,这样的话就会导致虽然userId一致但是各自所在的静态常量池中都没有,于是这个用户就可以在不同的服务器分别下单了。如果有用户使用脚本同时发送很多的下单请求,那么就会有极大的可能在每一个服务器中都下一单,那么如何解决这个问题呢?那就要学习分布式锁的内容了

到此这篇关于Redis实现商品秒杀功能页面流程的文章就介绍到这了,更多相关Redis商品秒杀内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Redis实现商品秒杀功能页面流程

本文链接: https://www.lsjlt.com/news/167624.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Redis实现商品秒杀功能页面流程
    目录全局唯一ID 业务逻辑分析代码实现优惠券秒杀业务逻辑分析代码实现定量商品多卖问题业务逻辑分析乐观锁与悲观锁乐观锁代码实现一个用户限买一单业务逻辑分析代码实现全局唯一ID...
    99+
    2022-11-13
  • SpringBoot+RabbitMQ+Redis实现商品秒杀的示例代码
    目录业务分析创建表功能实现1.用户校验2.下单3.减少库存4.支付总结业务分析 一般而言,商品秒杀大概可以拆分成以下几步: 用户校验 校验是否多次抢单,保证每个商品每个用户只能秒杀一...
    99+
    2022-11-12
  • php商城秒杀功能怎么实现
    本教程操作环境:windows7系统、PHP8.1版、DELL G3电脑php+redis实现秒杀功能,可缓解瞬时并发对mysql的压力场景:在某个时间点对商品goods_id=2的商品进行抢购,商品库存为10建立商品goods_id=2的...
    99+
    2022-10-18
  • 如何使用Redis实现秒杀功能
    这篇文章主要介绍如何使用Redis实现秒杀功能,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1. 怎样预防数据库超售现象设置数据库事务的隔离级别为Serializable(不可用)Serializable就是让数据库...
    99+
    2023-06-14
  • 使用PHP和Redis实现简单秒杀功能
    安装Redis 首先,需要在服务器上安装Redis。如果使用Linux系统,可以使用命令行安装。如果使用Windows系统,可以下载并安装Redis二进制文件。 创建Redis连接 在PHP中,可以使用Redis扩展来连接Redis服务器。...
    99+
    2023-10-20
    php redis 开发语言
  • 如何用Redis乐观锁实现秒杀功能
    在大流量程序开发中,必然会遇到高并发的应用的场景。解决方案大致分为两个方向,消息队列、锁.redis 实现消息队列核心简单版本 $key = 'quque'; ...
    99+
    2022-10-18
  • 使用Redis实现秒杀功能的简单方法
    1. 怎样预防数据库超售现象 设置数据库事务的隔离级别为Serializable(不可用) Serializable就是让数据库去串行化的去执行事务,一个事务执行完才能去执行下一个事...
    99+
    2022-11-12
  • 基于redis分布式锁如何实现秒杀功能
    这篇文章主要介绍了基于redis分布式锁如何实现秒杀功能,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。业务场景所谓秒杀,从业务角度看,是短时...
    99+
    2022-10-18
  • thinkphp中怎么利用redis实现秒杀缓存功能
    thinkphp中怎么利用redis实现秒杀缓存功能,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1,安装redis,根据自己的php版本安装对应的redis扩...
    99+
    2023-06-19
  • vue实现商品详情页功能之商品选项卡
    本文实例为大家分享了vue实现商品详情页功能之商品选项卡的具体代码,供大家参考,具体内容如下 用户点击商品进入商品详情页,默认显示第一个小图对应的大图,然后鼠标滑到小图上,大图也会发...
    99+
    2022-11-12
  • vue如何实现商品详情页功能
    这篇文章将为大家详细讲解有关vue如何实现商品详情页功能,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。vue是什么Vue是一套用于构建用户界面的渐进式JavaScript框架,Vue与其它大型框架的区别是...
    99+
    2023-06-14
  • Spring Boot 整合Redis 实现优惠卷秒杀 一人一单功能
    目录一、什么是全局唯一ID⛅全局唯一ID⚡Redis实现全局唯一ID二、环境准备三、实现秒杀下单四、库存超卖问题⏳问题分析⌚ 乐观锁解决库存超卖✅Jmeter 测试五、优惠卷秒杀 实现一人一单⛵小结一、什么是全局唯一ID...
    99+
    2022-09-19
  • 怎么在redis中使用watch实现一个秒杀抢购功能
    怎么在redis中使用watch实现一个秒杀抢购功能?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。具体内容如下1、使用watch,采用乐观...
    99+
    2022-10-18
  • Spring Boot 整合Redis 实现优惠卷秒杀 一人一单功能
    目录一、什么是全局唯一ID⛅全局唯一ID⚡Redis实现全局唯一ID二、环境准备三、实现秒杀下单四、库存超卖问题⏳问题分析⌚ 乐观锁解决库存超卖✅Jmeter 测试五、优惠卷秒杀 实...
    99+
    2022-11-13
  • 微信小程序如何实现商品列表跳转商品详情页功能
    目录引言1.1实现首页页面2.1实现调跳转到手机详情页总结引言 模仿京东小程序,实现下列功能 首页包含了手机图片,手机的描述,手机的价格,购物车图标首页显示两行文字,多余的文字隐藏,...
    99+
    2022-11-13
  • 微信小程序怎么实现商品列表跳转商品详情页功能
    这篇文章主要介绍“微信小程序怎么实现商品列表跳转商品详情页功能”,在日常操作中,相信很多人在微信小程序怎么实现商品列表跳转商品详情页功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”微信小程序怎么实现商品列表...
    99+
    2023-06-30
  • vue实现商品详情页放大镜功能
    本文实例为大家分享了vue实现商品详情页放大镜的具体代码,供大家参考,具体内容如下 templates中内容 <div class="productLeft"> ...
    99+
    2022-11-12
  • php结合redis实现高并发下的抢购、秒杀功能的实例
    抢购、秒杀是如今很常见的一个应用场景,主要需要解决的问题有两个: 1 高并发对数据库产生的压力 2 竞争状态下如何解决库存的正确减少("超卖"问题) 对于第一个问题,已经很容易想到用缓存来处理抢购,避免直接...
    99+
    2022-06-04
    实例 功能 秒杀
  • 基于Redis的List实现特价商品列表功能
    目录 1、场景分析2、分析3 、具体实现3.1 ProductListService类3.2 商品的数据接口的定义和展示及分页3.3 定时任务4、解决商品列表存在的缓存击穿...
    99+
    2022-11-12
  • Unity打开淘宝app并跳转到商品页面功能的实现方法
    最近碰到个需求,是希望在Unity有一个按钮,打开后直接跳转淘宝app,打开商品页面。百度了下没有相关的文章,于是我在此分享下。 之前开发游戏的时候就希望引导玩家到应用商店更新游戏,...
    99+
    2022-11-12
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作