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

【JAVA架构师成长之路】【电商系统实战】第10集:电商秒杀系统实战(流量削峰 + 库存预热 + 请求排队)


30分钟课程:电商秒杀系统实战(流量削峰 + 库存预热 + 请求排队)


课程目标
  1. 掌握秒杀系统核心架构设计:流量削峰、库存预热、请求排队。
  2. 实现基于 Redis 的令牌桶限流与库存原子扣减。
  3. 通过 Redis List 或 Kafka 实现高并发请求的异步处理。

课程内容与时间分配


0~5分钟:课程概述

业务场景与挑战

  • 瞬时高并发:万人抢购导致服务崩溃、数据库击穿。
  • 资源竞争:库存超卖、订单重复提交。
  • 核心目标:保障系统高可用、数据一致性、用户体验流畅。

技术方案

  • 流量削峰:令牌桶限流控制入口请求量。
  • 库存预热:活动开始前将库存加载至 Redis,避免直接访问数据库。
  • 请求排队:Redis List 或 Kafka 缓冲请求,异步处理订单。

5~10分钟:技术难点与核心问题
  1. 令牌桶算法实现
    • 分布式环境下如何保证限流计数原子性?
  2. 库存预热与扣减
    • Redis 预减库存时如何避免超卖?
  3. 请求队列可靠性
    • 如何防止消息丢失或重复消费?
  4. 数据一致性
    • 订单创建、库存扣减、支付状态如何保证最终一致?

10~25分钟:解决方案与代码实战

1. 流量削峰:Redis令牌桶限流(10~15分钟)

令牌桶原理

  • 每秒发放固定数量令牌(如 1000 个),请求获取令牌后才能进入系统。

Lua脚本实现原子操作

-- KEYS[1]: 令牌桶键(如 limit:seckill)  
-- ARGV[1]: 桶容量  
-- ARGV[2]: 令牌生成速率(每秒)  
-- ARGV[3]: 当前时间戳(秒)  local key = KEYS[1]  
local capacity = tonumber(ARGV[1])  
local rate = tonumber(ARGV[2])  
local now = tonumber(ARGV[3])  -- 1. 初始化桶数据  
local last_time = tonumber(redis.call('hget', key, 'last_time')) or now  
local tokens = tonumber(redis.call('hget', key, 'tokens')) or capacity  -- 2. 计算新增令牌数  
local new_tokens = math.floor((now - last_time) * rate)  
tokens = math.min(capacity, tokens + new_tokens)  
last_time = now  -- 3. 尝试获取令牌  
if tokens >= 1 then  tokens = tokens - 1  redis.call('hset', key, 'last_time', last_time)  redis.call('hset', key, 'tokens', tokens)  return 1  -- 获取成功  
else  return 0  -- 令牌不足  
end  

Java调用代码

@Service  
public class RateLimiterService {  @Autowired  private RedisTemplate<String, String> redisTemplate;  private static final String LUA_SCRIPT = "上述 Lua 脚本内容";  public boolean tryAcquire(String key, int capacity, int rate) {  long now = System.currentTimeMillis() / 1000;  DefaultRedisScript<Long> script = new DefaultRedisScript<>(LUA_SCRIPT, Long.class);  Long result = redisTemplate.execute(script, Collections.singletonList(key),  String.valueOf(capacity), String.valueOf(rate), String.valueOf(now));  return result != null && result == 1;  }  
}  

2. 库存预热:Redis原子扣减(15~20分钟)

库存预热与扣减流程

  1. 预热库存:活动开始前将库存从数据库加载到 Redis。
  2. 原子扣减:使用 Lua 脚本保证查询与扣减的原子性。

Lua脚本(扣减库存)

-- KEYS[1]: 库存键(如 stock:item_1001)  
-- ARGV[1]: 扣减数量  
local stock = tonumber(redis.call('GET', KEYS[1]))  
if stock >= tonumber(ARGV[1]) then  redis.call('DECRBY', KEYS[1], ARGV[1])  return redis.call('GET', KEYS[1])  -- 返回剩余库存  
else  return -1  -- 库存不足  
end  

Java代码:预热库存

public void preheatStock(String itemId, int stock) {  redisTemplate.opsForValue().set("stock:" + itemId, String.valueOf(stock));  
}  public boolean deductStock(String itemId, int quantity) {  String script = "上述 Lua 脚本内容";  DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);  Long result = redisTemplate.execute(redisScript, Collections.singletonList("stock:" + itemId),  String.valueOf(quantity));  return result != null && result >= 0;  
}  

3. 请求排队:Redis List或Kafka(20~25分钟)

方案1:Redis List队列

// 生产者:接收请求并存入队列  
public void enqueueRequest(String itemId, String userId) {  redisTemplate.opsForList().leftPush("queue:seckill:" + itemId, userId);  
}  // 消费者:异步处理队列  
@Scheduled(fixedDelay = 100)  
public void processQueue() {  String userId = redisTemplate.opsForList().rightPop("queue:seckill:item_1001", 1, TimeUnit.SECONDS);  if (userId != null) {  orderService.createOrder(userId, "item_1001");  }  
}  

方案2:Kafka异步处理

// 生产者发送消息  
@Autowired  
private KafkaTemplate<String, String> kafkaTemplate;  public void sendSeckillRequest(String userId, String itemId) {  kafkaTemplate.send("seckill_requests", userId + ":" + itemId);  
}  // 消费者处理消息  
@KafkaListener(topics = "seckill_requests")  
public void handleSeckillRequest(String message) {  String[] parts = message.split(":");  orderService.createOrder(parts[0], parts[1]);  
}  

25~30分钟:练习与拓展

练习题目
  1. 动态调整限流速率
    • 要求:根据系统负载动态修改令牌桶的 rate 参数(如 CPU >80% 时降级为 500 QPS)。
  2. 库存回滚设计
    • 场景:用户超时未支付,将库存返还 Redis 并发送通知。
  3. 队列优先级
    • 任务:实现 VIP 用户的请求优先处理(Redis Sorted Set 或 Kafka 优先级队列)。
推荐拓展方向
  1. 分布式限流
    • 结合 Nginx 层限流(漏桶算法)与 Redis 令牌桶,多层防护。
  2. 库存分片
    • 将单品库存拆分为多个 Redis Key(如 stock:item_1001_shard1),提升并发性能。
  3. 热点数据隔离
    • 对秒杀商品单独部署 Redis 集群,避免影响其他业务。

课程总结

  • 流量削峰:令牌桶算法控制入口流量,Lua 脚本保障原子性。
  • 库存预热:Redis 缓存 + 原子扣减,避免超卖。
  • 请求排队:Redis List 或 Kafka 异步处理,降低数据库压力。
  • 关键代码
    1. 令牌桶限流 Lua 脚本。
    2. 库存扣减原子操作。
    3. 请求队列的生产者-消费者模型。

课后资源

  • Redis 官方文档:Redis Transactions
  • Kafka 入门指南:Apache Kafka Quickstart
  • 完整代码示例:GitHub - 秒杀系统Demo

相关文章:

【JAVA架构师成长之路】【电商系统实战】第10集:电商秒杀系统实战(流量削峰 + 库存预热 + 请求排队)

30分钟课程&#xff1a;电商秒杀系统实战&#xff08;流量削峰 库存预热 请求排队&#xff09; 课程目标 掌握秒杀系统核心架构设计&#xff1a;流量削峰、库存预热、请求排队。实现基于 Redis 的令牌桶限流与库存原子扣减。通过 Redis List 或 Kafka 实现高并发请求的异步处…...

无人机推流/RTMP视频推拉流:EasyDSS无法卸载软件的原因及解决方法

视频推拉流/直播点播EasyDSS平台支持音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景中可实现视频直播、点播、转码、管理、录像、检索、时移回看等。此外&#xff0c;平台还支持用户自行上传视频文件&#xff0c;也可将上传的点播…...

Logisim实验--计组

每个实验会先讲一下原理再给出答案。 实验一&#xff1a;7段数码管驱动电路设计 实验目的 (1)帮助学生理解真值表方式设计电路的原理&#xff1b; (2)能利用Logisim的真值表生成电路功能自动生成所需电路。 这里我们要看清每个引脚控制的是哪个灯亮&#xff0c;注意看它的线…...

【Linux】软硬链接 | 动静态链接(三)

目录 前言&#xff1a; 一、软硬链接 1.软链接 2.硬链接 3.硬链接数 4.软硬链接的区别 5.使用unlink删除链接的文件 6.目录文件链接数( . 和 .. ) 二、静态库的制作和使用 1.制作静态库 2.使用静态库 2.1方法一 2.2方法二 2.3方法三 三、动态库的制作和使用 1.…...

数据结构(回顾)

数据结构&#xff08;回顾&#xff09; 回顾 不同点顺序表链表存储空间上物理上一定连续逻辑上连续&#xff0c;物理上不一定连续随机访问支持&#xff0c;时间复杂度O(1)不支持&#xff0c;时间复杂度O(N)任意位置插入或者删除元素可能需要挪动元素&#xff0c;效率低&#…...

达梦数据库在Linux,信创云 安装,备份,还原

&#xff08;一&#xff09;系统环境检查 1操作系统&#xff1a;确认使用的是国产麒麟操作系统&#xff0c;检查系统版本是否兼容达梦数据库 V8。可以通过以下命令查看系统版本&#xff1a; cat /etc/os-release 2硬件资源&#xff1a;确保服务器具备足够的硬件资源&#xff0…...

从0开始的操作系统手搓教程23:构建输入子系统——实现键盘驱动1——热身驱动

目录 所以&#xff0c;键盘是如何工作的 说一说我们的8042 输出缓冲区寄存器 状态寄存器 控制寄存器 动手&#xff01; 注册中断 简单整个键盘驱动 Reference ScanCode Table 我们下一步就是准备进一步完善我们系统的交互性。基于这个&#xff0c;我们想到的第一个可以…...

01-简单几步!在Windows上用llama.cpp运行DeepSeek-R1模型

1.llama.cpp介绍 Llama.cpp 是一个开源的、轻量级的项目&#xff0c;旨在实现 Meta 推出的开源大语言模型 Llama 的推理&#xff08;inference&#xff09;。Llama 是 Meta 在 2023 年开源的一个 70B 参数的高质量大语言模型&#xff0c;而 llama.cpp 是一个用 C 实现的轻量化…...

Trae AI 开发工具使用手册

这篇手册将介绍 Trae 的基本功能、安装步骤以及使用方法&#xff0c;帮助开发者快速上手这款工具。 Trae AI 开发工具使用手册 Trae 是字节跳动于 2025 年推出的一款 AI 原生集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在通过智能代码生成、上下文理解和自动化任务…...

HarmonyOS Next 属性动画和转场动画

HarmonyOS Next 属性动画和转场动画 在鸿蒙应用开发中&#xff0c;动画是提升用户体验的关键要素。通过巧妙运用动画&#xff0c;我们能让应用界面更加生动、交互更加流畅&#xff0c;从而吸引用户的注意力并增强其使用粘性。鸿蒙系统为开发者提供了丰富且强大的动画开发能力&…...

JavaWeb-mysql8版本安装

下载方式 地址&#xff1a;https://www.mysql.com/cn/downloads/ 选择&#xff1a;MySQL Community (GPL) downloads 选择&#xff1a;MySQL Community Server 选择&#xff1a; 选择&#xff1a; 安装mysql &#xff08;8.0.30&#xff09; 1、以管理员身份 打开 命令行…...

【实战ES】实战 Elasticsearch:快速上手与深度实践-3.2.3 案例:新闻搜索引擎的相关性优化

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 Elasticsearch新闻搜索引擎相关性优化实战3.2.3 案例&#xff1a;新闻搜索引擎的相关性优化项目背景1. 相关性问题诊断与分析1.1 初始查询DSL示例1.2 问题诊断矩阵1.3 性能基…...

HCIA复习拓扑实验

一.拓扑图 二.需求 1.学校内部的HTTP客户端可以正常通过域名www.baidu.com访问到百度网络中HTTP服务器 2.学校网络内部网段基于192.168.1.0/24划分&#xff0c;PC1可以正常访问3.3.3.0/24网段&#xff0c;但是PC2不允许 3.学校内部路由使用静态路由&#xff0c;R1和R2之间两…...

企业如何选择研发项目进度管理软件?盘点15款实用工具

这篇文章介绍了以下工具: 1. PingCode&#xff1b; 2. Worktile&#xff1b; 3. 腾讯 TAPD&#xff1b; 4. 华为 DevCloud&#xff1b; 5. 亿方云&#xff1b; 6. 阿里云效&#xff1b; 7. CODING 码云&#xff1b; 8. 明道云&#xff1b; 9. 进度猫&#xff1b; 10. 轻流等。 …...

(二 十 二)趣学设计模式 之 备忘录模式!

目录 一、 啥是备忘录模式&#xff1f;二、 为什么要用备忘录模式&#xff1f;三、 备忘录模式的实现方式四、 备忘录模式的优缺点五、 备忘录模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;…...

conda 配置新环境时package will be install 和 package will be download 的区别

install 和 download 的区别 package will be downloaded下的包&#xff1a;这一类显示的是需要从 conda 仓库或其他指定的源下载的软件包。这些软件包通常是 .tar.bz2、.tar.xz 或 .conda 格式的压缩包。这些包会被下载到本地缓存目录&#xff08;通常是 ~/.conda 或 C:\Users…...

第本章:go 切片

注意&#xff1a; 切片必须要初始化 才能使用 &#xff0c;切片是引用类型 a :[]int{} // 这上叫始化 此时并没有申请内存 // 如果要追加值的话&#xff1a; append ints : append(a, 1, 2, 3)a : make([]int,5) // 声明切片类型var a []string //声明一…...

P6412题解

原题 题目描述 现在有一个 1 ∼ n 1\sim n 1∼n 的排列 a a a,将序列中的元素依次放进一个 BST 里,求 BST 中插入函数的执行次数。 注意:第一个数已经作为 BST 的根。 如果您无法理解上面说的话,这里有一份伪代码: insert( number x, node n )c+1;if x is less tha…...

关于AI数据分析可行性的初步评估

一、结论&#xff1a;可在部分环节嵌入&#xff0c;无法直接处理大量数据 1.非本地部署的AI应用处理非机密文件没问题&#xff0c;内部文件要注意数据安全风险。 2.AI&#xff08;指高规格大模型&#xff09;十分适合探索性研究分析&#xff0c;对复杂报告无法全流程执行&…...

编程考古-Borland历史:《.EXE Interview》对Anders Hejlsberg关于Delphi的采访内容(中)

为了纪念Delphi在2002年2月14日发布的25周年(2020.2.12),这里有一段由.EXE杂志编辑Will Watts于1995年对Delphi首席架构师Anders Hejlsberg进行的采访记录。在这次采访中,Anders讨论了Delphi的设计与发展,以及即将到来的针对Windows 95的32位版本。 Q. 编译器引擎本身是用…...

SQL 窗口函数之lead() over(partition by ) 和 lag() over(partition by )

lag() over() 与 lead() over() 函数是跟偏移量相关的两个分析函数&#xff0c;通过这两个函数可以在一次查询中取出同一字段的前 N 行的数据 (lag) 和后 N 行的数据 (lead) 作为独立的列, 从而更方便地进行进行数据过滤。这种操作可以代替表的自联接&#xff0c;并且 LAG 和 L…...

【基础知识】回头看Maven基础

背景 项目过程中&#xff0c;对于Maven的pom.xml文件&#xff0c;很多时候&#xff0c;我通过各种参考、仿写&#xff0c;最终做出想要的效果。 但实际心里有些迷糊&#xff0c;不清楚具体哪个基础的配置所实现的效果。 今天&#xff0c;特意回过头来&#xff0c;了解Maven的基…...

Manus+Ollama实现本地大模型部署和应用测试

这几天Manus即DeepSeek后又突然火爆&#xff0c;我也进行了跟踪测试&#xff0c;特记录一下分享给大家&#xff0c;目前来看&#xff0c;Manus的确是一个可以进行任务分解的自动化解决方案&#xff0c;将其他AI需要多次繁杂的迭代对话做了较大的改进&#xff0c;相当于用户抛出…...

labelimg标注的xml标签转换为yolo格式标签

本文不生产技术&#xff0c;只做技术的搬运工&#xff01;&#xff01;&#xff01; 前言 在yolo训练时&#xff0c;我们需要对图像进行标注&#xff0c;而使用labelimg标注时如果直接选择输出yolo格式的数据集&#xff0c;则原始数据的很多信息无法被保存&#xff0c;因此一版…...

【Python 数据结构 9.树】

我装作漠视一切&#xff0c;其实我在乎的太多&#xff0c;但我知道抓得越紧越容易失去 —— 25.3.6 一、树的基本概念 1.树的定义 树是n个结点的有限集合&#xff0c;n0时为空树。当n大于0的时候&#xff0c;满足如下两个条件&#xff1a; ① 有且仅有一个特定的结点&#xff…...

LLM 学习(二 完结 Multi-Head Attention、Encoder、Decoder)

文章目录 LLM 学习&#xff08;二 完结 Multi-Head Attention、Encoder、Decoder&#xff09;Self-Attention &#xff08;自注意力机制&#xff09;结构多头注意力 EncoderAdd & Norm 层Feed Forward 层 EncoderDecoder的第一个Multi-Head AttentionMasked 操作Teacher Fo…...

计算机网络软考

1.物理层 1.两个主机之间发送数据的过程 自上而下的封装数据&#xff0c;自下而上的解封装数据&#xff0c;实现数据的传输 2.数据、信号、码元 码元就是数字通信里用来表示信息的基本信号单元。比如在二进制中&#xff0c;用高电平代表 “1”、低电平代表 “0”&#xff0c…...

从高资源到低资源语言的全覆盖:Manus AI的数据革命与迁移学习策略

在全球化语境下,多语言手写识别的最大挑战并非技术本身的复杂性,而是语言资源的极度不均衡——英语、中文等高资源语言拥有海量标注数据,而藏语、斯瓦希里语等低资源语言往往仅有零星样本。Manus AI通过数据生态构建与知识迁移技术,打破了这一资源垄断,实现了从高资源到低…...

《白帽子讲 Web 安全》之身份认证

目录 引言 一、概述 二、密码安全性 三、认证方式 &#xff08;一&#xff09;HTTP 认证 &#xff08;二&#xff09;表单登录 &#xff08;三&#xff09;客户端证书 &#xff08;四&#xff09;一次性密码&#xff08;OTP&#xff09; &#xff08;五&#xff09;多因…...

VBA 数据库同一表的当前行与其他行的主键重复判断实现方案

目的&#xff0c;判断是否主键重复&#xff0c;不重复则登录新数据&#xff0c;重复则不登录。 定义类型&#xff1a; DataRecord   tableName 表名   rowNumber 行号   columnName 列名   data 数据 想要实现的代码逻辑如下&#xff1a; 模拟数据库的登录过程。假设…...