Springboot实战——黑马点评之秒杀优化

news/2024/9/28 17:03:00/文章来源:https://www.cnblogs.com/Wyuf1314/p/18431894

Springboot实战——黑马点评之 秒杀优化

1 秒杀优化

先来复习以下,秒杀优惠券业务的现有实现逻辑:


以上流程图中的操作串行执行,效率极低。
其中 判断秒杀库存 以及 校验一人一单 属于对数据库的读取,耗时较少;扣减库存 以及 创建订单 属于对数据库的写操作,耗时相对较久。

提升效率的方法我们可以考虑两个方面:
1)引入并发(开启多线程):主线程负责读取操作,如果读取检验资格通过,则开启另外的线程负责写操作
2)引入Redis缓存:可以将订单信息以及秒杀券信息存入Redis,在Redis中检验资格后,将符合资格的优惠券id+用户id+订单id存入阻塞队列,单独开启第二线程来读取阻塞队列执行写操作,即刻给用户返回下单订单号。

1.1 引入Redis进行资格检验

资格检验分为 检查库存是否充足 以及 用户是否下单过该优惠券 两个操作,如果引入Redis来实现,要考虑:

  • 秒杀券库存导入Redis,并且要数据及时更新同步,即 在检验资格通过后需要将Redis中的券库存-1
  • 下单记录:使用的数据结构需要满足1 集合;2 元素唯一性
  • 使用Redis中的set类型来缓存下单该优惠券的用户id集合,并且要保证数据及时更新同步,即 在检验资格通过后需要向set中添加用户id


以上所考虑的几点还需要保证操作的原子性,所以使用Redis的Lua脚本来实现。
Lua脚本需要的ARGV参数列表中有两个待定参数,分别是优惠券id 以及 用户id,其他的业务逻辑均调用Redis命令即可实现

-- 1. 参数列表
-- 1.1. 优惠券id 用于查询优惠券库存时的关键字
local voucherId = ARGV[1]
-- 1.2. 用户id 用于将查询下单用户对比
local useId = ARGV[2]-- 2. 数据key
-- 2.1. 库存key + 业务前缀 拼接 优惠券id
local stockKey = 'seckill:stock:' .. voucherId
-- 2.2 订单key
local orderKey = 'seckill:order:' .. voucherId-- 3. 业务执行
-- 3.1 首先判断库存是否充足
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 下单(插入用户id) sadd orderKey userId
redis.call('sadd', orderKey, userId)return 0

如果库存不足则返回1(Long),如果该用户重复下单则返回2(Long),如果资格检验通过则返回0

如果资格检验通过,则需要保证该有效订单被阻塞队列拿到,后续阻塞式执行成功,所以将“凭证”(封装好用户id、券id、订单id的订单实例)传入阻塞队列,等待异步线程阻塞式读取处理下单业务。

// 这里直接封装成订单实例
VoucherOrder voucherOrder = new VoucherOrder();
voucherOrder.setVoucherId(voucherId);
voucherOrder.setUserId(userId);
voucherOrder.setId(redisIdWorker.nextId("order"));
// 放入阻塞队列 blockingqueue
orderTasks.add(voucherOrder);

1.2 开启异步线程写数据库

需要准备以下几个数据结构:

  • 阻塞队列:当一个线程尝试从该队列中获取元素时,当查询到队列为空时会阻塞等待,直到队列中插入元素后被唤醒,不会导致线程空转消耗CPU资源。
  • 异步线程实现下单即 开启异步独立线程来阻塞式执行下单业务,所以需要准备1 线程池 2 线程任务
  • 线程池常量(单线程线程处理器),用于提交异步任务
// 线程池/线程处理器 此处创建的是单线程处理器
private static final ExecutorService SECKILL_ORDER_EXECUTOR= Executors.newSingleThreadExecutor();

并且要保证在类初始化,在用户最初调用该接口时就同步开启线程处理器

@PostConstruct
private void init(){SECKILL_ORDER_EXECUTOR.submit(new voucherOrderHandler());
}
  • 线程任务
// 定义交给线程池执行的业务内容
private class voucherOrderHandler implements Runnable{@Overridepublic void run(){// 该线程执行任务为阻塞式的 当发现队列中存在元素时才进行while(true){//...这里执行下单的具体业务//1. 获取队列中的订单信息try {VoucherOrder voucherOrder = orderTasks.take();// 2. 下单业务handleVoucherOrder(voucherOrder);} catch (Exception e) {log.error("处理订单异常",e);}}}
}

1.3 以上异步实现的弊端

1)内存限制问题:阻塞队列是有JDK内部的,底层使用的是JVM内存,如果有大量的订单信息被存入阻塞队列,将会带来较大内存负担,内存溢出
2)数据安全问题:JVM内存没有持久化机制。如果服务宕机,内存中的订单信息消失,用户支付状态与后台保存订单状态不一致;或者是 从阻塞队列中取出订单信息后尚未来得及处理下单逻辑,服务宕机了,将会造成订单丢失的问题。

2 Redis消息队列实现异步秒杀

使用Redis消息队列的两个优势:
1)Redis的消息队列是独立于JVM之外的数据结构,不受JVM内存的限制
2)Redis的消息队列可对消息作持久化,保证数据安全性,且封装有消息确认机制,确保了消息至少被消费一次

2.1 Redis实现消息队列的三种方式

  • 基于List结构:

    使用BPOP来阻塞式从Redis的list数据结构中获取队首元素,本质上原理和JDK的阻塞队列一样的。





    这样实现的弊端
    1)无法避免服务宕机导致的消息丢失
    2)只支持单消费者

  • 基于PubSub结构
    支持多消费者了,支持多生产、多消费


    这样实现的弊端
    1)不支持数据持久化,发送消息时如果消息无人订阅,消息不会永久存储在Redis中
    2)消息堆积有上限,消费者接收数据有缓存区,如果消息缓存超额,则会造成数据丢失了
    3)无法避免消息丢失

  • 基于Stream数据类型




    如果基于Stream数据类型来实现异步下单业务,则会出现消息漏读问题

  • 基于Stream的消费者组
    消费者组:将多个消费者划分到一个组中,该组监听同一个队列
    这样设计有以下几个特点:
    1)消息分流:同组内的消费者用来“竞争”同一个队列中的消息,与单消费者相比,加快了处理消息的速度,且消息可回溯
    2)消息标示:消费者组会维护一个标示,记录最后一个被处理的消息,如果服务宕机重启,能从标示之后读取消息,确保每一个消息都被消费成功,避免像单消费者出现漏读消息的问题
    3)消息确认:消费者组当获取到一个消息时,会将消息插入Pending-list中,标志该消息尚未处理,当处理结束后,会通过XACK来确认消息已处理,然后从pending-list中移除

    当消费者获取到消息时,消息会自动放入pending-list中,等待消费者处理完毕后发出XACK确认后才将其移除


    消费者1和2相继从s1队列中读取未读取过的第一条消息,与此同时这些消息均被放入了Pending-list中,等待消息确认Ack

2.2 Stream队列实现异步下单

所以可以将原有的异步下单功能替换成用Stream队列实现:
循环从Stream队列中读取订单信息 -> 消费者组以最后一个被获取的消息标识($),读取队列中还没被消费的消息,并设置2秒内阻塞式(|block)读取 -> 如果阻塞等待时间内并未拿到最新消息则continue -> 如果阻塞等待时间内获取到新消息,则按下单业务将其处理
捕获异常 -> 意味着此时pending-list中存在已被消费但未被处理完毕的消息 -> 循环从pending-list中获取第0号消息(非阻塞式,0)来尝试继续处理 -> 如果获取到尚未处理过的消息,则按正常下单业务继续处理 -> 如果没有异常中止的消息则结束异常捕获业务
如果捕获异常过程中又遇到异常 -> 继续循环读取pending-list

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/805270.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PARTVI-Oracle数据库管理与开发-数据库管理员的概念

18. 数据库管理员的概念 18.1. 数据库管理员的职责 数据库管理员(DBA)的主要责任是使企业数据对其用户可用。DBA必须与开发人员紧密合作,确保他们的应用程序有效地使用数据库,并与系统管理员合作,确保物理资源充足且使用高效。Oracle DBA负责了解Oracle数据库架构以及数据…

实验作业1

实验一 任务一 源代码#include<stdio.h> int main() {printf(" o \n");printf("<H>\n");printf("I I\n");printf(" o \n");printf("<H>\n");printf("I I\n");return 0; }效果 源代码#incl…

01. 感知层环境安装

1. 软件以及驱动的安装安装ZigBee无线网络节点开发平台 IAR Embedded Workbench(简称EW) 安装串口驱动(CH340芯片)。点击安装64位的。后续就可以使用串口对开发板进行调试。 仿真器驱动程序(用来烧录代码)的安装。 安装串口工具(XCOM)。2. IAR创建工程打开安装的IAR软件,点击…

黑马PM-内容项目-需求分析

需求分析的定义需求分析的时机需求分析的步骤

带笔TP gt9xx调试

一.添加驱动把供应商提供的驱动替换掉sdk里面默认的驱动drivers/input/touchscreen/gt9xx 二.dts配置:&i2c3 {status = "okay";pinctrl-names = "default";pinctrl-0 = <&i2c3m0_xfer>;gt9xx: gt9xx@5d {compatible = "goodix,gt9xx&q…

P3313 [SDOI2014] 旅行

题目思路 为每个宗教维护一个线段数,查询时,树剖时在对应宗教上查询区间即可。 使用动态开点线段树,每次最多新建 \(\log n\) 个节点,不会 MLE。 代码 #include <bits/stdc++.h>#define range 1, 100000using namespace std;const int N = 100010;struct edge {int t…

谷歌网站收录查询,你知道怎么查询谷歌网站的收录情况吗

查询谷歌网站的收录情况,可以通过以下几种方法来实现: 一、使用Google Search Console(谷歌搜索控制台) Google Search Console是谷歌提供的官方工具,用于监控和管理网站在谷歌搜索结果中的表现。以下是具体步骤: 访问并登录:访问Google Search Console官网,并使用谷歌…

谷歌收录批量查询,你知道怎么批量查询谷歌收录

批量查询谷歌收录是一个涉及多个步骤和工具的过程,旨在帮助网站管理员或SEO专业人士快速了解多个网站或页面在谷歌搜索引擎中的收录情况。以下是一些常用的批量查询谷歌收录的方法和步骤: 一、使用Google Search Console(谷歌搜索控制台) 虽然Google Search Console本身不直…

C10-06-Burp简单使用

一 浏览器代理设置免责声明 本文仅是个人对该工具的学习测试过程记录,不具有恶意引导意向。 本文工具仅面向合法授权的企业安全建设行为,如您需要测试本工具的可用性,请自行搭建靶机环境。 在使用本工具进行检测时,您应确保该行为符合当地的法律法规,并且已经取得了足够的…

修改网页内容

修改网页内容 控制台输入代码 document.body.isContentEditable=true document.body.isContentEditable=true

青训营 X 豆包MarsCode

如果有人对前端/后端/AI 技术以及字节感兴趣并想系统学习来自大厂课程的话,可以试试青训营!青训营是字节跳动稀土掘金社区发起的技术系列培训 & 人才选拔项目,旨在培养优秀且具有职业竞争力的开发工程师。课程全程免费,不收取任何费用!考核方式:通过发布的刷题任务即…

VMware ESXi 8.0U3 Dell (戴尔) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025

VMware ESXi 8.0U3 Dell (戴尔) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025VMware ESXi 8.0U3 Dell (戴尔) 定制版更新 OEM BIOS 2.7 支持 Windows Server 2025 VMware ESXi 8.0U3 macOS Unlocker & OEM BIOS Dell (戴尔) 定制版 ESXi 8.0U3 标准版,Dell (戴尔)、…