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

通过异步使用消息队列优化秒杀

通过异步使用消息队列优化秒杀

  • 同步秒杀流程
  • 异步优化秒杀
    • 异步秒杀流程
    • 基于lua脚本保证Redis操作原子性
    • 代码实现
    • 阻塞队列的缺点


同步秒杀流程

    public Result seckillVoucher(Long voucherId) throws InterruptedException {SeckillVoucher seckillVoucher = iSeckillVoucherService.getById(voucherId);LocalDateTime beginTime = seckillVoucher.getBeginTime();if (LocalDateTime.now().isBefore(beginTime)) {return Result.fail("秒杀还未开始");}LocalDateTime endTime = seckillVoucher.getEndTime();if (LocalDateTime.now().isAfter(endTime)) {return Result.fail("秒杀已经结束");}
//        查看库存Integer stock = seckillVoucher.getStock();if (stock < 1) {return Result.fail("库存不足");}Long userId = UserHolder.getUser().getId();
//        redis集群实现RLock lock = redissonClient.getLock("order:" + userId);boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS);if (!isLock) {return Result.fail("你已经购买优惠卷");}try {IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId, userId);} finally {
//            redisLock.releaseLock();lock.unlock();}}@Transactionalpublic Result createVoucherOrder(Long voucherId, Long userId) {Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();if (count > 0) {return Result.fail("已经购买优惠卷");}
//        基于乐观锁避免超卖问题,在乐观锁的基础上做出了改进,当要修改库存时判断库存是否大于0,但是也给数据库带来了巨大的压力boolean success = iSeckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).gt("stock", 0).update();if (!success) {return Result.fail("库存不足");}VoucherOrder voucherOrder = new VoucherOrder();long orderId = redisWorker.getGlobalId("order");voucherOrder.setId(orderId);voucherOrder.setVoucherId(voucherId);voucherOrder.setUserId(userId);save(voucherOrder);return Result.ok(orderId);}

以上是我们同步秒杀的流程,首先会从数据库中查询优惠卷,检查库存和是否在秒杀阶段,然后使用redisson基于Redis实现分布式锁解决一人一单问题,如果都通过后就会修改库存并且生成订单,整体的流程如下:在这里插入图片描述
在这个流程中,我们发现整个步骤都是同步,都是交给主线程来执行,负责检验库存保证一人一单然后来修改数据库数据,我们是否可以再请一个人来优化这个流程呢?比如A来负责检验,检验成功后交给B来完成数据库的修改,这样是不是能够优化秒杀的业务

异步优化秒杀

我们可以这样做,让主线程来负责判断这个人能不能抢杀,如果抢杀成功交给另一个人来负责修改数据库,主线程直接返回订单id。
那怎样让主线程快速的库存和一人一单的判断呢?Redis
我们可以将库存保存在Redis的字符串类型中,Redis的性能高于直接查询数据库的性能,而一人一单的校验我们可以使用Redis的set集合,校验是否一人一单就看Redis的set集合中是否含有用户id,而当主线程通过校验后让Redis中的库存-1并且将用户id添加到set集合中。
而我们怎样让修改数据库的任务交给另一个人来处理呢?使用阻塞队列或者消息队列,为了实现简单,暂且使用阻塞队列,我们将要修改数据库的任务放入阻塞队列中,创建一个线程池,让线程池来负责执行修改数据库的任务。

异步秒杀流程

在这里插入图片描述
从流程中看出我们主线程只负责进行库存校验和确保一人一单后,生成订单添加到阻塞队列或消息队列中就直接返回,大大提高了业务的性能。

基于lua脚本保证Redis操作原子性

如果我们使用以上的操作来实现代码,会发生并发安全问题,因为校验Redis操作和更新Redis操作不具有原子性,就可能发生问题,例如一个线程通过校验Redis库存等还没有进行修改,另一个线程直接进入比较没有修改的库存,就发生了线程安全问题,我们需要保证这些操作具有原子性

代码实现

lua脚本:

--优惠卷id
local voucherId = ARGV[1];
--用户id
local userId = ARGV[2];
--库存键
local stockKey = "voucher:stock:" + voucherId;
--下过订单的用户键
local orderKey = "voucher:order:" + voucherId;
--查看卷库存是否足够
if (tonumber(redis.call("get", stockKey)) <= 0) then
return 1;
end
if (redis.call("sismember",orderKey,userId)==1) then
return 2
end
--扣库存
redis.call("incrby",stockKey,-1);
--添加用户id
redis.call("sadd",orderKey,userId)
return 0
/*** 异步秒杀*/@Overridepublic Result seckillVoucher(Long voucherId) throws InterruptedException {Long userId = UserHolder.getUser().getId();Long result = redisTemplate.execute(SECkILL_SCRIPT, Collections.emptyList(), voucherId.toString(), userId.toString());int re = result.intValue();if (re != 0) {return re == 1 ? Result.fail("库存不足") : Result.fail("不能重复下单");}long orderId = redisWorker.getGlobalId("voucher:order");VoucherOrder voucherOrder = new VoucherOrder();voucherOrder.setVoucherId(voucherId);voucherOrder.setId(orderId);voucherOrder.setUserId(userId);VOUCHER_PROXY = (IVoucherOrderService) AopContext.currentProxy();BLOCKING_QUEUE.add(voucherOrder);return Result.ok(orderId);}
// 初始化lua脚本环境private static final DefaultRedisScript<Long> SECkILL_SCRIPT;static {SECkILL_SCRIPT = new DefaultRedisScript<>();SECkILL_SCRIPT.setLocation(new ClassPathResource("seckill.lua"));SECkILL_SCRIPT.setResultType(Long.class);}//    在类初始化完之后执行@PostConstructprivate void init() {EXECUTOR_SERVICE.execute(new VoucherHandler());}
// 阻塞队列获取任务处理private class VoucherHandler implements Runnable {@Overridepublic void run() {while (true){try {
//                从阻塞队列中获取订单信息VoucherOrder voucherOrder = BLOCKING_QUEUE.take();
//                  更新数据库库存以及创建优惠卷订单VOUCHER_PROXY.createVoucherOrder(voucherOrder.getVoucherId(), voucherOrder.getUserId());} catch (InterruptedException e) {e.printStackTrace();}}}}@Transactionalpublic Result createVoucherOrder(Long voucherId, Long userId) {Integer count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();if (count > 0) {return Result.fail("已经购买优惠卷");}
//        基于乐观锁避免超卖问题,在乐观锁的基础上做出了改进,当要修改库存时判断库存是否大于0,但是也给数据库带来了巨大的压力boolean success = iSeckillVoucherService.update().setSql("stock=stock-1").eq("voucher_id", voucherId).gt("stock", 0).update();if (!success) {return Result.fail("库存不足");}VoucherOrder voucherOrder = new VoucherOrder();long orderId = redisWorker.getGlobalId("order");voucherOrder.setId(orderId);voucherOrder.setVoucherId(voucherId);voucherOrder.setUserId(userId);save(voucherOrder);return Result.ok(orderId);}

阻塞队列的缺点

在我们进行数据库的修改任务时我们使用了阻塞队列来实现,在实际的业务中,我们需要使用一些消息队列来代替阻塞队列,阻塞队列使用的是JVM中的内存,当消息过多时会造成JVM内存爆满,并且功能不够强大,我们可以使用Redis的Stream或者MQ来实现消息队列来代替阻塞队列

相关文章:

通过异步使用消息队列优化秒杀

通过异步使用消息队列优化秒杀 同步秒杀流程异步优化秒杀异步秒杀流程基于lua脚本保证Redis操作原子性代码实现阻塞队列的缺点 同步秒杀流程 public Result seckillVoucher(Long voucherId) throws InterruptedException {SeckillVoucher seckillVoucher iSeckillVoucherServi…...

AI产业告别“独奏”时代,“天翼云息壤杯”高校AI大赛奏响产学研“交响乐”

文 | 智能相对论 作者 | 陈泊丞 人工智能产业正在从“独奏”时代进入“大合奏”时代。 在早期的AI发展阶段&#xff0c;AI应用主要集中在少数几个领域&#xff0c;如语音识别、图像处理等。这些领域的研究和开发工作往往由少数几家公司或研究机构即可独立完成&#xff0c;犹…...

Hot100 - 字母异位词分组

Hot100 - 字母异位词分组 最佳思路&#xff1a;排序 时间复杂度&#xff1a; O(nmlogm)&#xff0c;其中 n 为 strs 数组的长度&#xff0c;m 为每个字符串的长度。 代码&#xff1a; class Solution {public List<List<String>> groupAnagrams(String[] strs) …...

力扣hot100-->排序

排序 1. 56. 合并区间 中等 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输…...

【VRChat 全身动捕】VIVE 手柄改 tracker 定位器教程,低成本光学动捕解决方案(持续更新中2024.11.26)

更新 0.0.1&#xff08;2024/11/26&#xff09;&#xff1a; 1.解决了内建蓝牙无法识别、“steamVR 蓝牙不可用” 的解决方案 2.解决了 tracker 虽然建立了连接但是在 steamVR 界面上看不到的问题 3.解决了 VIVE 基站1.0 无法被蓝牙识别 && 无法被 steamVR 搜索到 &…...

【Nginx】核心概念与安装配置解释

文章目录 1. 概述2. 核心概念2.1.Http服务器2.2.反向代理2.3. 负载均衡 3. 安装与配置3.1.安装3.2.配置文件解释3.2.1.全局配置块3.2.2.HTTP 配置块3.2.3.Server 块3.2.4.Location 块3.2.5.upstream3.2.6. mine.type文件 3.3.多虚拟主机配置 4. 总结 1. 概述 Nginx是我们常用的…...

Qt界面篇:QMessageBox高级用法

1、演示效果 2、用法注意 2.1 设置图标 用于显示实际图标的pixmap取决于当前的GUI样式。也可以通过设置icon pixmap属性为图标设置自定义pixmap。 QMessageBox::Icon icon(...

【二叉树】【2.1遍历二叉树】【刷题笔记】【灵神题单】

关注二叉树的三个问题&#xff1a; 什么情况适合自顶向下&#xff1f;什么时候适合用自底向上&#xff1f;一般来说&#xff0c;DFS的递归边界是空节点&#xff0c;什么情况下要额外把叶子节点作为递归边界&#xff1f;在什么情况下&#xff0c;DFS需要有返回值&#xff1f;什…...

Mongo数据库 --- Mongo Pipeline

Mongo数据库 --- Mongo Pipeline 什么是Mongo PipelineMongo Pipeline常用的几个StageExplanation with example:MongoDB $matchMongoDB $projectMongoDB $groupMongoDB $unwindMongoDB $countMongoDB $addFields Some Query Examples在C#中使用Aggreagtion Pipeline**方法一: …...

Adobe Illustrator 2024 安装教程与下载分享

介绍一下 下载直接看文章末尾 Adobe Illustrator 是一款由Adobe Systems开发的矢量图形编辑软件。它广泛应用于创建和编辑矢量图形、插图、徽标、图标、排版和广告等领域。以下是Adobe Illustrator的一些主要特点和功能&#xff1a; 矢量绘图&#xff1a;Illustrator使用矢量…...

javax.xml.ws.soap.SOAPFaultException: ZONE_OFFSET

javax.xml.ws.soap.SOAPFaultException 表示 SOAP 调用过程中发生了错误&#xff0c;并且服务端返回了一个 SOAP Fault。 错误信息中提到的 ZONE_OFFSET 可能指的是时区偏移量。在日期和时间处理中&#xff0c;时区偏移量是指格林威治标准时间 (GMT) 的偏移量。如果服务期望特…...

常用的数据结构

队列(FIFO) 栈(LIFO) 链表 hash表 hash冲突处理 开放式寻址 线性探测 表示依次检查索引为 hash(key) + 1、hash(key) + 2 ... 的位置。i 是冲突后的探查步数。公式:hash(i) = (hash(key) + i) % TableSize二次探查 规则:冲突后探查的步长是平方递增的,例如,检查位置为 hash…...

javaweb-day01-html和css初识

html:超文本标记语言 CSS&#xff1a;层叠样式表 1.html实现新浪新闻页面 1.1 标题排版 效果图&#xff1a; 1.2 标题颜色样式 1.3 标签内颜色样式 1.4设置超链接 1.5 正文排版 1.6 页面布局–盒子 &#xff08;1&#xff09;盒子模型 &#xff08;2&#xff09;页面布局…...

C++11特性(详解)

目录 1.C11简介 2.列表初始化 3.声明 1.auto 2.decltype 3.nullptr 4.范围for循环 5.智能指针 6.STL的一些变化 7.右值引用和移动语义 1.左值引用和右值引用 2.左值引用和右值引用的比较 3.右值引用的使用场景和意义 4.右值引用引用左值及其一些更深入的使用场景分…...

基于Springboot的心灵治愈交流平台系统的设计与实现

基于Springboot的心灵治愈交流平台系统 介绍 基于Springboot的心灵治愈交流平台系统&#xff0c;后端框架使用Springboot和mybatis&#xff0c;前端框架使用Vuehrml&#xff0c;数据库使用mysql&#xff0c;使用B/S架构实现前台用户系统和后台管理员系统&#xff0c;和不同级别…...

初识java(2)

大家好&#xff0c;今天我们来讲讲java中的数据类型。 java跟我们的c语言的数据类型有一些差别&#xff0c;那么接下来我们就来看看。 一.字面常量&#xff0c;其中&#xff1a;199&#xff0c;3.14&#xff0c;‘a’&#xff0c;true都是常量将其称为字面常量。&#xff08;…...

AIGC--AIGC与人机协作:新的创作模式

AIGC与人机协作&#xff1a;新的创作模式 引言 人工智能生成内容&#xff08;AIGC&#xff09;正在以惊人的速度渗透到创作的各个领域。从生成文本、音乐、到图像和视频&#xff0c;AIGC使得创作过程变得更加快捷和高效。然而&#xff0c;AIGC并非完全取代了人类的创作角色&am…...

Wonder3D本地部署到算家云搭建详细教程

Wonder3D简介 Wonder3D仅需2至3分钟即可从单视图图像中重建出高度详细的纹理网格。Wonder3D首先通过跨域扩散模型生成一致的多视图法线图与相应的彩色图像&#xff0c;然后利用一种新颖的法线融合方法实现快速且高质量的重建。 本文详细介绍了在算家云搭建Wonder3D的流程以及…...

【设计模式】【行为型模式(Behavioral Patterns)】之状态模式(State Pattern)

1. 设计模式原理说明 状态模式&#xff08;State Pattern&#xff09; 是一种行为设计模式&#xff0c;它允许对象在其内部状态发生变化时改变其行为。这个模式的核心思想是使用不同的类来表示不同的状态&#xff0c;每个状态类都封装了与该状态相关的特定行为。当对象的状态发…...

QML学习 —— 34、视频媒体播放器(附源码)

效果 说明 您可以单独使用MediaPlayer播放音频内容(如音频),也可以将其与VideoOutput结合使用以渲染视频。VideoOutput项支持未转换、拉伸和均匀缩放的视频演示。有关拉伸均匀缩放演示文稿的描述,请参见fillMode属性描述。 播放可能出错问题 出现的问题:      DirectS…...

基于STM32与ADC的锂电池电量监测系统设计

1. 锂电池电量监测为什么需要STM32和ADC&#xff1f; 做嵌入式开发的朋友应该都遇到过这样的需求&#xff1a;设备用锂电池供电&#xff0c;需要实时显示剩余电量。比如手持设备、智能家居控制器或者无人机&#xff0c;电量显示都是刚需功能。但锂电池的特性决定了直接测量电量…...

Harness Engineering:Agent 时代,工程师的新战场

关注 AI 的同学大概率对这两个词已经不陌生了&#xff1a;提示词工程&#xff08;Prompt Engineering&#xff09;和上下文工程&#xff08;Context Engineering&#xff09;。前者教你怎么跟模型说话&#xff0c;后者教你往模型的上下文窗口里塞什么内容。但从 2026 年初开始&…...

像素幻梦·创意工坊应用场景:复古风APP启动页加载动画AI生成方案

像素幻梦创意工坊应用场景&#xff1a;复古风APP启动页&加载动画AI生成方案 1. 引言&#xff1a;像素艺术的复兴与AI赋能 在移动应用设计领域&#xff0c;复古像素风格正经历一场文艺复兴。从独立游戏到主流应用&#xff0c;越来越多的产品选择用像素艺术打造独特的品牌识…...

手把手教你用NOAA气象数据做可视化分析(含常见字段解析与避坑指南)

手把手教你用NOAA气象数据做可视化分析&#xff08;含常见字段解析与避坑指南&#xff09; 气象数据可视化是理解气候模式、分析极端天气事件的重要工具。美国国家海洋和大气管理局&#xff08;NOAA&#xff09;提供的全球历史气候网络日数据&#xff08;GHCN-Daily&#xff0…...

springboot网络小说在线阅读网站的设计与实现

目录需求分析技术选型数据库设计核心功能实现性能优化安全防护测试部署项目技术支持源码获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作需求分析 明确网站的核心功能和用户需求。网络小说在线阅读网站通常需要包含以下功能模块&#xff1a;用户…...

跨语言沟通的革命性突破:FunASR语音翻译系统全解析

跨语言沟通的革命性突破&#xff1a;FunASR语音翻译系统全解析 你是否还在为国际会议中的语言障碍而烦恼&#xff1f;是否因跨国团队协作中的沟通不畅而效率低下&#xff1f;FunASR语音翻译系统将彻底改变这一现状&#xff0c;让跨语言交流如母语般自然流畅。读完本文&#xf…...

MangoHud资源占用实时监控:图表工具终极指南

MangoHud资源占用实时监控&#xff1a;图表工具终极指南 【免费下载链接】MangoHud A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more. Discord: https://discordapp.com/invite/Gj5YmBb 项目地址: https://gitcode.com/gh_mirrors/ma/…...

从‘距离’理解生成对抗:Wasserstein距离如何拯救你的GAN项目?通俗图解+代码验证

从Wasserstein距离到实战&#xff1a;如何用数学直觉拯救你的GAN训练&#xff1f; 想象你正在训练一个生成对抗网络&#xff08;GAN&#xff09;&#xff0c;却发现生成器要么完全崩溃&#xff0c;要么反复输出几乎相同的图像——这就是典型的模式坍塌&#xff08;Mode Collaps…...

语音转换完全上手:Retrieval-based Voice-Conversion-WebUI从入门到精通

语音转换完全上手&#xff1a;Retrieval-based Voice-Conversion-WebUI从入门到精通 【免费下载链接】Retrieval-based-Voice-Conversion-WebUI 语音数据小于等于10分钟也可以用来训练一个优秀的变声模型&#xff01; 项目地址: https://gitcode.com/GitHub_Trending/re/Retr…...

本地部署 Ollama + DeepSeek 完整指南:免费跑大模型,数据不出本地

本地部署 Ollama DeepSeek 完整指南&#xff1a;免费跑大模型&#xff0c;数据不出本地不花一分钱&#xff0c;不用科学上网&#xff0c;在自己电脑上跑 DeepSeek 大模型&#xff0c;这篇教程手把手带你搞定一、为什么要本地部署&#xff1f; 很多人用 AI 工具都是调用云端 AP…...