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

秒杀系统的业务流程以及优化方案(实现异步秒杀)

先看基本的业务流程

 那么我们可以看到整个流程都是一个线程来完成的,这样的话耗时还是很长的,那么可不可以采用多线程去实现呢?

首先我们要思考怎么对业务进行拆分,可以想象一个我们去饭店点餐,会有前台接待,询问订单,之后将小票传给后厨去做饭,这样就会快很多,也可以接待更多的客人

也就是说 一个线程负责去读数据库做准备工作,另一个线程去实现写操作,如下图中所示:

    确定了我们可以将判断库存和检验一人一单业务抽取出来之后,我们在想一下 还能不能优化,这个时候我们会发现,这两个操作还是在数据库进行的,那么mysql的并发本身也是不高的,现在我们就要通过另一个性能更好的数据库进行实现,就是redis

     

 这样只需要业务进行到校验完成就可以给用户返回下单完成的信息,之后在通过另一个线程异步进行扣减库存操作

redis中实现上面两个操作的业务流程如下:

 由于操作流程较长,应该使用lua脚本来保证原子性

将上面的逻辑采用lua脚本进行编写,之后程序运行首先判断返回值如果是0就说明用户有下单资格,如果是1或者2就说明用户没有资格下单

如果有下单资格就可以将用户id,优惠券id,和订单id存入一个阻塞队列里面,之后异步进行写入数据库操作

整体流程:

提供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)
-- 3.6.发送消息到队列中, XADD stream.orders * k1 v1 k2 v2 ...
redis.call('xadd', 'stream.orders', '*', 'userId', userId, 'voucherId', voucherId, 'id', orderId)
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);}@Overridepublic 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(), String.valueOf(orderId));int r = result.intValue();// 2.判断结果是否为0if (r != 0) {// 2.1.不为0 ,代表没有购买资格return Result.fail(r == 1 ? "库存不足" : "不能重复下单");}// 3.返回订单idreturn Result.ok(orderId);}

 redis的流程到此就完结了,接下来就是使用阻塞队列存储要进行写操作的信息

阻塞队列的实现方式通常是使用一个先进先出的队列来存储元素,同时使用锁来实现线程安全。当队列为空时,put()方法会被阻塞,直到有元素被添加到队列中;当队列满时,put()方法同样会被阻塞,直到队列中有元素被移除。

阻塞队列通常用于生产者-消费者模型中,生产者将元素添加到队列中,消费者从队列中取出元素进行处理。通过使用阻塞队列,可以避免生产者和消费者之间的直接交互,从而简化了代码的设计和维护。

首先我们可以可以使用java自带的阻塞队列实现,提供一个样例:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class ProducerConsumerExample {public static void main(String[] args) {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10); // 创建一个容量为10的阻塞队列Thread producer = new Thread(new Producer(queue));Thread consumer = new Thread(new Consumer(queue));producer.start();consumer.start();}
}class Producer implements Runnable {private BlockingQueue<Integer> queue;public Producer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {for (int i = 0; i < 100; i++) {try {System.out.println("Producing " + i);queue.put(i); // 将元素添加到队列中} catch (InterruptedException e) {e.printStackTrace();}}}
}class Consumer implements Runnable {private BlockingQueue<Integer> queue;public Consumer(BlockingQueue<Integer> queue) {this.queue = queue;}@Overridepublic void run() {while (true) {try {Integer item = queue.take(); // 从队列中取出元素进行处理System.out.println("Consuming " + item);} catch (InterruptedException e) {e.printStackTrace();}}}
}

当然如果想要性能更好的话,我们可以采用消息队列来做

相关文章:

秒杀系统的业务流程以及优化方案(实现异步秒杀)

先看基本的业务流程 那么我们可以看到整个流程都是一个线程来完成的&#xff0c;这样的话耗时还是很长的&#xff0c;那么可不可以采用多线程去实现呢&#xff1f; 首先我们要思考怎么对业务进行拆分&#xff0c;可以想象一个我们去饭店点餐&#xff0c;会有前台接待&#xff…...

Java实现根据商品ID获取1688商品详情跨境属性数据,1688商品重量数据接口,1688API接口封装方法

要通过1688的API获取商品详情跨境属性数据&#xff0c;您可以使用1688开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过1688开放平台API获取商品详情属性数据接口&#xff1a; 首先&#xff0c;确保您已注册成为1688开放平台的开发者…...

前端面试的性能优化部分(14)每天10个小知识点

目录 系列文章目录前端面试的性能优化部分&#xff08;1&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;2&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;3&#xff09;每天10个小知识点前端面试的性能优化部分&#xff08;4&#xff09;每天…...

Uniapp笔记(六)uniapp基础

一、腾讯地图 1、uniapp地图渲染 <template><view><map class"map" :longitude"longitude" :latitude"latitude"></map></view> </template> <script>export default {data() {return {longitude:1…...

C++ sort函数用法

sort函数是C标准库中的一个排序算法&#xff0c;头文件是algorithm&#xff0c;用于对容器中的元素进行排序。它可以对任何可排序的容器&#xff08;如数组、向量、列表等&#xff09;进行排序。 有以下四个基本用法&#xff1a; 1. 自定义排序规则&#xff1a;可以通过提供自…...

电子仓库预测水浸事件,他怎么做到的?

仓库环境中水浸事件可能导致严重的损失&#xff0c;不仅对货物造成损害&#xff0c;还可能影响设备的正常运行甚至威胁安全。 因此&#xff0c;为了应对这一挑战&#xff0c;引入一套完善的仓库水浸监控系统成为了不可或缺的措施。 客户案例 广东某电子公司是一家领先的电子设…...

CMake调用第三方库的两种方法

为了让连接器搜索到库路径&#xff0c;一般有两种方法 link_directories命令 使用步骤## 在add_executable或add_library前引入第三方库 # 1.引入第三方库,${THIRD_PARTY_PREFIX}为用户定义的第三方库目录 link_directories(${THIRD_PARTY_PREFIX}/lib) # 2.增加第三方库头文…...

Django基础7——用户认证系统、Session管理、CSRF安全防护机制

文章目录 一、用户认证系统二、案例&#xff1a;登陆认证2.1 平台登入2.2 平台登出2.3 login_required装饰器 三、Django Session管理3.1 Django使用Session3.1.1 Cookie用法3.1.2 Session用法 3.2 案例&#xff1a;用户登录认证 四、Django CSRF安全防护机制 一、用户认证系统…...

基于流计算 Oceanus(Flink) CDC 做好数据集成场景

由于第一次做实时&#xff0c;所以踩坑比较多&#xff0c;见谅(测试环境用的flink),小公司没有用到hadoop组件 一、踩坑记录 1:本地代码的flink版本是flink1.15.4&#xff0c;生产环境是flink1.16.1&#xff0c;在使用侧输出流时报错&#xff0c;需要使用以下写法,需要使用Si…...

MySQL8.Xx安装控制台未生成随机密码解决方案

MySQL8.xx一主两从复制安装与配置 MySQL8.XX随未生成随机密码解决方案 MySQL8.0.30一主两从复制与配置(一) 一: Mysql 安装时控制台未生成密码 安装过程中解压或者安装时报错等,这种情况一般是因网络等其他原因导致下载的安装包不完整&#xff0c; 重新下载安装即可; 二:…...

安装VS2005时提示:请插入磁盘:visual studio 2005 DVD

安装VS2005时提示&#xff1a;请插入磁盘:visual studio 2005 DVD 修改卷标为 "DVD1"...

OpenVINO2023使用简介

1 下载安装 先在anaconda中创建一个虚拟环境&#xff0c;该环境的python版本为3.7&#xff0c;之所以使用python3.7&#xff0c;是因为我在3.9上安装过程中出现不少bug&#xff0c;后面新建了一个3.7的环境才解决&#xff0c;我不知道是否由于和我已有环境中某些包不兼容&…...

基于React实现无限滚动的日历详细教程,附源码【手写日历教程第二篇】

前言 最常见的日历大部分都是滚动去加载更多的月份&#xff0c;而不是让用户手动点击按钮切换日历月份。滚动加载的交互方式对于用户而言是更加丝滑和舒适的&#xff0c;没有明显的操作割裂感。 那么现在需要做一个这样的无限滚动的日历&#xff0c;前端开发者应该如何去思考…...

68、使用aws官方的demo和配置aws服务,进行视频流上传播放

基本思想:参考官方视频,进行了配置aws,测试了视频推流,rtsp和mp4格式的视频貌似有问题,待调研和解决 第一步:1) 进入aws的网站,然后进入ioT Core 2)先配置 Thing types & Thing,选择香港的节点,然后AWS ioT--->Manage---> Thing type 然后输入名字,创建Th…...

数据库

表 记录&#xff1a;行 字段&#xff08;属性&#xff09;: 列 以行列的形式就组成了表&#xff08;数据存储在表中&#xff09; 关系数据库的表由记录组成&#xff0c;记录由字段组成&#xff0c;字段由字符或数字组成。它可以供各种用户共享&#xff0c; 具有最小冗余度和较高…...

深入了解fcntl函数:Linux系统编程中的文件控制

文章目录 概述介绍函数原型与参数 拓展&#xff1a;fcntl改文件属性总结 概述 摘要: fcntl函数是Linux系统编程中一个重要的函数&#xff0c;用于对文件描述符进行各种控制操作。本文将详细介绍fcntl函数的原型、各个参数的用法&#xff0c;以及阻塞和非阻塞模式切换的方法&am…...

汇川技术内推码

[庆祝]不一样的内推码[庆祝]&#xff1a;IVSM2R 投递了可以评论下名字&#xff0c;我会帮忙留意进度。 汇尔成川&#xff0c;共赴星海&#xff0c;欢迎加入&#xff0c;职等你来。 嵌入式软硬件&#xff0c;机器人算法&#xff0c;电机控制&#xff0c;通信软件&#xff0c;PLC…...

nacos服务器启动报错集合

报错1 Error creating bean with name ‘user‘: Unsatisfied dependency expressed through field ‘jwtTokenManage 开启鉴权之后&#xff0c;你可以自定义用于生成JWT令牌的密钥&#xff0c;application.properties中的配置信息为&#xff1a; ### Since 1.4.1, worked when…...

C语言_分支和循环语句(2)

文章目录 前言一、for 循环1.1语法1.2 for 语句的循环控制变量1.3 一些 for 循环的变种 二、do ... while()循环2.1 do 语句的语法2.2 do ... while 循环中的 break 和 continue2.3 练习1 **- 计算n的阶乘**2. - **在一个有序数组中查找具体的某个数字 n** 二分查找算法&#x…...

JMeter 接口自动化测试:从入门到精通的完全指南

JMeter 是一个开源的负载测试工具&#xff0c;它可以模拟多种协议和应用程序的负载&#xff0c;包括 HTTP、FTP、SMTP、JMS、SOAP 和 JDBC 等。在进行接口自动化测试时&#xff0c;使用 JMeter 可以帮助我们快速地构建测试用例&#xff0c;模拟多种场景&#xff0c;发现接口的性…...

Python:操作 Excel 折叠

💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

Python ROS2【机器人中间件框架】 简介

销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

Qt 事件处理中 return 的深入解析

Qt 事件处理中 return 的深入解析 在 Qt 事件处理中&#xff0c;return 语句的使用是另一个关键概念&#xff0c;它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别&#xff1a;不同层级的事件处理 方…...

渗透实战PortSwigger靶场:lab13存储型DOM XSS详解

进来是需要留言的&#xff0c;先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码&#xff0c;输入的<>当成字符串处理回显到页面中&#xff0c;看来只是把用户输…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型

在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重&#xff0c;适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解&#xff0c;并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...