当前位置: 首页 > 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;发现接口的性…...

【Java】集合List的toArray()方法及其重载

在Java中&#xff0c;集合&#xff08;List 接口的实现类&#xff09;提供了一个名为 toArray 的方法&#xff0c;用于将集合中的元素转换成数组。该方法有两个主要的重载形式&#xff0c;分别用于不同的情况。 toArray()重载方法1 <T> T[] toArray(T[] a)这个方法将集…...

Python学习笔记:Requests库安装、通过url下载文件

1.下载安装requests库 在pipy或者github下载&#xff0c;通常是个zip&#xff0c;解压缩后在路径输入cmd&#xff0c;并运行以下代码 Python setup.py install 安装完成后&#xff0c;输入python再输入import requests得到可以判断时候完成安装 2.通过url下载文件 使用的是u…...

git pull --rebase 用法

git pull --rebase git pull --rebase 是 Git 命令中的一个选项&#xff0c;它的作用是在从远程仓库拉取更新时使用 rebase 而不是默认的合并方式。使用这个命令会使您的提交历史更加整洁&#xff0c;因为它将您的本地提交在远程更新之前重新应用到新的提交之上。 这个命令的…...

react antd框架中的徽标获取数据对应状态的数量

实现思路&#xff1a;获取数量的思路是通过filter过滤符合数据来实现。 列表数组.filter(item > item.status 值).length; 例子&#xff1a;以下这个例子是判断data数组中的status中在职的数量。 data.filter((item) > item.status 在职).length 效果展示&#xff…...

【多线程】Thread类的用法

文章目录 1. Thread类的创建1.1 自己创建类继承Thread类1.2 实现Runnable接口1.3 使用匿名内部类创建Thread子类对象1.4 使用匿名内部类创建Runnable子类对象1.5 使用lambda创建 2. Thread常见的构造方法2.1 Thread()2.2 Thread(Runnable target)2.3 Thread(String name)2.4 Th…...

第八章 贪心算法 part03 1005.K次取反后最大化的数组和 134. 加油站 135. 分发糖果 (day34补)

本文章代码以c为例&#xff01; 一、力扣第1005题&#xff1a;K 次取反后最大化的数组和 题目: 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择…...

Android Activity启动过程一:从Intent到Activity创建

关于作者&#xff1a;CSDN内容合伙人、技术专家&#xff0c; 从零开始做日活千万级APP。 专注于分享各领域原创系列文章 &#xff0c;擅长java后端、移动开发、人工智能等&#xff0c;希望大家多多支持。 目录 一、概览二、应用内启动源码流程 (startActivity)2.1 startActivit…...

第9章:聚类

聚类任务 性能度量 距离度量 非度量距离 原型聚类 有很好的统计学上的意义&#xff0c;但是只能找到椭球形的聚类。 密度聚类 层次聚类...

程序员为什么要写bug,不能一次性写好吗?

仅仅听到“Bug”这个词就会让你作为一个开发人员感到畏缩。我们相信&#xff0c;优秀的程序员是那些编写无错误代码的人。随着一些开发人员强调要成为一名零错误程序员&#xff0c;我们进行了更深刻的思考&#xff0c;并发现事实的准确性。 所有制作的软件都应该没有错误。对此…...

Nginx反向代理其他服务

Nginx反向代理 嘿&#xff0c;你的网络遇到了限制&#xff0c;不能直接通过服务的端口进行访问&#xff1f;别担心&#xff0c;我们可以借助Nginx这个超级英雄来解决这个问题&#xff01;让我给你讲讲关于Nginx反向代理的故事吧。 首先&#xff0c;让我们明确一下反向代理的概…...

MQ 简介-RabbitMQ

一. MQ 简介 消息队列作为高并发系统的核心组件之一&#xff0c;能够帮助业务系统结构提升开发效率和系统 稳定性&#xff0c;消息队列主要具有以下特点&#xff1a; 削峰填谷:主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题系统解耦:解决不同重要程度、不…...

强化学习(2)

强化学习(1) 1.多智能体深度强化学习重要性采样 多智能体深度强化学习&#xff08;Multi-Agent Deep Reinforcement Learning&#xff0c;MADRL&#xff09;是指在多智能体环境下使用深度强化学习算法进行协同学习。重要性采样&#xff08;Importance Sampling&#xff09;是…...

Visual Studio 2022的MFC框架——theApp全局对象

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。 MFC中的WinMain函数是如何与MFC程序中的各个类组织在一起的呢&#xff1f;MFC程序中的类是如何与WinMain函数关联起来的呢&#xff1f…...

SpringBoot Cache

一、基本概念 Spring Cache 是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring Cache 提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff0c;例如&#xff1a; • EHCache • Caffeine …...

vue 简单实验 自定义组件 component

1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"components-demo"><button-counter></button-counter> </div> <script> // 创建一个Vue 应用 const ap…...

C++ 改善程序的具体做法 学习笔记

1、尽量用const enum inline替换#define 因为#define是做预处理操作&#xff0c;编译器从未看见该常量&#xff0c;编译器刚开始编译&#xff0c;它就被预处理器移走了&#xff0c;而#define的本质就是做替换&#xff0c;它可能从来未进入记号表 解决方法是用常量替换宏 语言…...

Unity 之 GameObject.Find()在场景中查找指定名称的游戏对象

文章目录 GameObject.Find 是 Unity 中的一个函数&#xff0c;用于在场景中查找指定名称的游戏对象。这个函数的主要作用是根据游戏对象的名称来查找并返回一个引用&#xff0c;使您能够在代码中操作该对象。以下是有关 GameObject.Find 的详细介绍&#xff1a; 函数签名&…...

flink on yarn with kerberos 边缘提交

flink on yarn 带kerberos 远程提交 实现 flink kerberos 配置 先使用ugi进行一次认证正常提交 import com.google.common.io.Files; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.flink.client.cli.CliFrontend; import o…...

NodeJS的简介以及下载和安装

本章节会带大家下载并安装NodeJs 以及简单的入门&#xff0c;配有超详细的图片&#xff0c;一步步带大家进行下载与安装 NodeJs简介关于前端与后端Node是什么&#xff1f;为什么要学习NodeNodeJS的优点&#xff1a; NodeJS的下载与安装NodeJS的下载&#xff1a; NodeJS的快速入…...

量化面试-概率题

文章目录 一、题目1.糖果罐&#xff08;绿皮书79页&#xff09;2 折木棍&#xff08;绿皮书89页&#xff09;3 第一张ACE&#xff08;绿皮书95页&#xff09;4 n个均匀分布之和&#xff08;绿皮书95页&#xff09; 二、答案1. 糖果罐2 折木棍3 第一张ACE4 n个均匀分布之和 一、…...

【spark】java类在spark中的传递,scala object在spark中的传递

记录一个比较典型的问题&#xff0c;先讲一下背景&#xff0c;有这么一个用java写的类 public class JavaClass0 implements Serializable {private static String name;public static JavaClass0 getName(String str) {if (name null) {namestr;}return name;}... }然后在sp…...

php 文字生成图片保存到本地

你可以使用PHP的GD库来生成图片并保存到本地。首先&#xff0c;你需要确保你的PHP环境已经安装了GD库。然后&#xff0c;你可以使用GD库的函数来创建一个画布&#xff0c;并在上面绘制文字。最后&#xff0c;使用imagepng或imagejpeg函数将画布保存为PNG或JPEG格式的图片文件。…...

面试手撕—二叉搜索树及其后序遍历

一、引言 在面试地平线的时候&#xff0c;聊到了二叉搜索树&#xff0c;让手撕二叉搜索树&#xff0c;以下是要求 1、用类模板实现二叉搜索树 2、写一个函数&#xff0c;实现给一个vector数组&#xff0c;转换成二叉搜索树 3、写出二叉搜索树的后序遍历 二、代码实现 #inc…...

Java数据结构面试题以及答案

本专栏记录Java后端开发相关的面试题&#xff0c;欢迎大家阅读专栏的其他文章。 目录 1.B树和B树的区别&#xff1f;B树和B树的优点分别是&#xff1f; 2.排序算法的种类和复杂度 3.HashMap和Hashtable的原理、区别、应用场景 4.ConcurrentHashMap的原理、应用场景 5.Arra…...

Java——它要求用户输入一个整数(实际上是一个字符串),然后计算该整数的平方值,并将结果输出。

这是一个Java程序&#xff0c;它要求用户输入一个整数&#xff08;实际上是一个字符串&#xff09;&#xff0c;然后计算该整数的平方值&#xff0c;并将结果输出。程序的基本流程如下&#xff1a; 首先&#xff0c;声明并初始化变量data和result&#xff0c;它们的初始值都为…...

【科研论文配图绘制】task6直方图绘制

【科研论文配图绘制】task6直方图绘制 task6 主要掌握直方图的绘制技巧&#xff0c;了解直方图含义&#xff0c;清楚统计指标的添加方式 1.直方图 直方图是一种用于表示数据分布和离散情况的统计图形&#xff0c;它的外观和柱形图相近&#xff0c;但它所 表达的含义和柱形图…...

Leetcode刷题:395. 至少有 K 个重复字符的最长子串、823. 带因子的二叉树

Leetcode刷题:395. 至少有 K 个重复字符的最长子串、823. 带因子的二叉树 1. 395. 至少有 K 个重复字符的最长子串算法思路参考代码和运行结果 2. 823. 带因子的二叉树算法思路参考代码和运行结果 1. 395. 至少有 K 个重复字符的最长子串 题目难度&#xff1a;中等 标签&#…...

java八股文面试[多线程]——Synchronized的底层实现原理

笔试&#xff1a;画出Synchronized 线程状态流转实现原理图 synchronized关键字解决的是多个线程之间访问资源的同步性&#xff0c;synchronized 翻译为中文的意思是同步&#xff0c;也称之为”同步锁“。 synchronized的作用是保证在同一时刻&#xff0c; 被修饰的代码块或方…...

C#,《小白学程序》第三课:类、类数组与排序

类class把数值与功能巧妙的进行了结合&#xff0c;是编程技术的主要进步。 下面的程序你可以确立 分数 与 姓名 之间关系&#xff0c;并排序。 1 文本格式 /// <summary> /// 同学信息类 /// </summary> public class Classmate { /// <summary> /…...

史上最全AP、mAP详解与代码实现

文章目录 前言一、mAP原理1、mAP概念2、准确率3、精确率4、召回率5、AP: Average Precision 二、mAP0.5与mAP0.5:0.951、mAP0.52、mAP0.5:0.95 三、mAP代码实现1、真实标签json文件格式2、模型预测标签json文件格式3、mAP代码实现4、mAP结果显示 四、模型集成mAP代码1、模型mai…...