当前位置: 首页 > news >正文

【Redis实战篇】秒杀优化

1. 秒杀优化-异步秒杀思路

我们来回顾一下下单流程

当用户发起请求,此时会请求nginxnginx会访问到tomcat,而tomcat中的程序,会进行串行操作,分成如下几个步骤

  • 1、查询优惠卷

  • 2、判断秒杀库存是否足够

  • 3、查询订单

  • 4、校验是否是一人一单

  • 5、扣减库存

  • 6、创建订单

在这里插入图片描述
在这六步操作中,由于有很多操作是要去操作数据库的,而且还是一个线程串行执行,添加了分布式锁, 这样就会导致我们这段程序执行耗时比较长,并发能力变弱了,所以我们需要异步程序执行,那么如何优化呢?

在这里笔者想给大家分享一下课程内没有的思路,看看有没有小伙伴这么想,比如,我们可以不可以使用异步编排来做,或者说我开启N多个线程,一个线程执行查询优惠卷,一个执行判断扣减库存,一个去创建订单等等,然后再统一做返回,这种做法和课程中有哪种好呢?答案是课程中的好,因为如果你采用我刚说的方式,如果访问的人很多,那么线程池中的线程可能一下子就被消耗完了,而且你使用上述方案,最大的特点在于,你觉得时效性会非常重要,但是你想想是吗?并不是,比如我只要确定他能做这件事,然后我后边慢慢做就可以了,我并不需要他一口气做完这件事,所以我们应当采用的是课程中,类似消息队列的方式来完成我们的需求,而不是使用线程池或者是异步编排的方式来完成这个需求。

在这里插入图片描述

优化方案:我们将耗时比较短的逻辑判断放入到redis中,比如是否库存足够,比如是否一人一单,这样的操作,只要这种逻辑可以完成,就意味着我们是一定可以下单完成的,我们只需要进行快速的逻辑判断,根本就不用等下单逻辑走完,我们直接给用户返回成功, 再在后台开一个线程,后台线程慢慢的去执行阻塞queue里边的消息,这样程序不就超级快了吗?而且也不用担心线程池消耗殆尽的问题,因为这里我们的程序中并没有手动使用任何线程池,当然这里边有两个难点:

  • 第一个难点是我们怎么在redis中去快速校验一人一单,还有库存判断

  • 第二个难点是由于我们校验和tomcat下单是两个线程,那么我们如何知道到底哪个单他最后是否成功,或者是下单完成,为了完成这件事我们在redis操作完之后,我们会将一些信息返回给前端,同时也会把这些信息丢到异步queue中去,后续操作中,可以通过这个id来查询我们tomcat中的下单逻辑是否完成了。

在这里插入图片描述

我们现在来看看整体思路:

  1. 选择Redis存储结构: 用户下单我们只需要存储库存变量即可,可以直接使用string类型结构,而一人一单问题,我们是需要存储很多用户的购买记录,并且用户不能重复下单,所以这里我们选择set存储结构再适合不过。
  2. 逻辑:当用户下单之后,判断库存是否充足只需要到redis中去根据key找对应的value是否大于0即可,如果不充足,则直接结束,如果充足,继续在redis中判断用户是否可以下单,如果set集合中没有这条数据,说明他可以下单,并扣减库存,再将userId存入当前优惠券的set中,并且返回0,整个过程需要保证是原子性的,我们可以使用lua来操作。当以上判断逻辑走完之后,我们可以判断当前redis中返回的结果是否是0 ,如果是0,则表示可以下单,则将之前说的信息存入到阻塞queue中去,此时开启单独的线程异步写入数据库中,最后返回订单id,前端可以通过返回的订单id来判断是否下单成功。

2. 秒杀优化-Redis完成秒杀资格判断

需求:

  • 新增秒杀优惠券的同时,将优惠券信息保存到Redis中

  • 基于Lua脚本,判断秒杀库存、一人一单,决定用户是否抢购成功

  • 如果抢购成功,将优惠券id和用户id封装后存入阻塞队列

  • 开启线程任务,不断从阻塞队列中获取信息,实现异步下单功能

在这里插入图片描述
VoucherServiceImpl👇 将用户资格判断放入redis中,用户响应信息只有和redis操作,性能大幅提高,新增秒杀优惠券的同时,将优惠券库存同步到redis中

@Override
@Transactional
public void addSeckillVoucher(Voucher voucher) {// 保存优惠券save(voucher);// 保存秒杀信息SeckillVoucher seckillVoucher = new SeckillVoucher();seckillVoucher.setVoucherId(voucher.getId());seckillVoucher.setStock(voucher.getStock());seckillVoucher.setBeginTime(voucher.getBeginTime());seckillVoucher.setEndTime(voucher.getEndTime());seckillVoucherService.save(seckillVoucher);// 保存秒杀库存到Redis中//SECKILL_STOCK_KEY 这个变量定义在RedisConstans中//private static final String SECKILL_STOCK_KEY ="seckill:stock:"stringRedisTemplate.opsForValue().set(SECKILL_STOCK_KEY + voucher.getId(), voucher.getStock().toString());
}

seckill.lua脚本👇 对redis的查询、判断、数据操作写入一个lua脚本,防止线程并发安全问题

-- 1.参数列表
-- 1.1.优惠券id
local voucherId = ARGV[1]
-- 1.2.用户id
local userId = ARGV[2]
-- 1.3.订单id
local orderId = ARGV[3]-- 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.库存不足,返回1return 1
end
-- 3.2.判断用户是否下单 SISMEMBER orderKey userId
if(redis.call('sismember', orderKey, userId) == 1) then-- 3.3.存在,说明是重复下单,返回2return 2
end
-- 3.4.扣库存 incrby stockKey -1
redis.call('incrby', stockKey, -1)
-- 3.5.下单(保存用户)sadd orderKey userId
redis.call('sadd', orderKey, userId)
return 0

写完lua脚本,剩下的就是执行lua脚本,根据脚本拿到的结果,为非0,根据情况返回"库存不足" 或者 "不能重复下单;为0,返回全局id生成器生成的订单id。至于订单保存到阻塞队列中的逻辑,咱们稍后实现。

VoucherOrderServiceImpl
在这里插入图片描述

3. 秒杀优化-基于阻塞队列实现秒杀优化

创建阻塞队列,保存数据类型为VoucherOrder,并设置大小为 1024 * 1024
在这里插入图片描述

在这里插入图片描述
处理订单操作的逻辑:👇
在这里插入图片描述

4. 总结

秒杀业务的优化思路是什么?

  • 先利用Redis完成库存余量、一人一单判断,完成抢单业务
  • 再将下单业务放入阻塞队列,利用独立线程异步下单

基于阻塞队列的异步秒杀存在哪些问题?

  • 内存限制问题(使用jdk中的阻塞队列,以后如果有大量的订单需要创建,很容易出现OOM问题)
  • 数据安全问题(数据是存储在内存里的,若出现服务宕机了,任务还没执行完毕,导致用户的订单数据丢失)

针对此问题,我们将会再下一篇解决。

相关文章:

  • 深入理解指针(1)
  • 养猪场巡检机器人的设计与应用研究
  • 哈希表的实现01
  • JAVA异常体系
  • 【node】如何把包发布到npm上
  • PaddleNLP框架训练模型:使用SwanLab教程
  • 虚拟机安装CentOS7网络问题
  • 数字化转型 - 标准化
  • docker 端口映射 docker run -p <宿主机端口>:<容器端口> <镜像名> ssh连不上的原因
  • Kafka进阶指南:从原理到实战
  • 认识Docker/安装Docker
  • 因为工作需要,做了一个简单的FunASR语音引擎训练材料标注工具
  • 【Linux】git
  • 时源芯微|扩频IC如何减少电磁干扰(EMI)
  • 什么是SparkONYarn模式?
  • 喜报!3N获批首个创新医疗器械三类证—「镜净GP」硬性接触镜护理消毒仪
  • JDK 命令行工具大全与学习方法总结 —— 从帮助文档到高效实践
  • 【2025.5.12】视觉语言模型 (更好、更快、更强)
  • 「Mac畅玩AIGC与多模态37」开发篇32 - 基于工作流的双插件信息整合与展示优化
  • QFileDialog文件选择框
  • 英国收紧移民政策,技术工作签证、大学招生面临更严要求
  • 中国人民抗日战争暨世界反法西斯战争胜利80周年纪念活动标识发布
  • 人民日报访巴西总统卢拉:“巴中关系正处于历史最好时期”
  • 英国首相斯塔默住所起火,警方紧急调查情况
  • 朝着解决问题的正确方向迈进——中美经贸高层会谈牵动世界目光
  • 世贸组织欢迎中美经贸高层会谈取得积极成果