Redis 在电商秒杀场景中的应用
Redis 在电商秒杀场景中的应用
- 一、简介
- 1.1 简介
- 1.2 场景应用
- 二、Redis 优势与挑战
- 2.1 优势
- 2.2 秒杀场景的挑战
- 三、应用场景分析
- 3.1 库存预热
- 代码示例
- 3.2 分布式锁
- 3.3 消息队列
- 四、系统设计方案
- 4.1 架构设计
- 4.2 技术选型
- 4.3 数据结构设计
- 五、Redis 性能优化
- 5.1 集群部署
- 5.1.1 Redis Sentinel
- 5.1.2 Redis Cluster
- 5.2 缓存策略
- 5.2.1 本地缓存
- 5.2.2 分布式缓存
- 5.3 流量控制
一、简介
1.1 简介
在电商平台的特定时期,如双十一、618等节日或促销活动,会有大量用户涌入该平台进行购物。其中,秒杀场景是用户数量最多的,因为消费者可在限定的时间内获得极具性价比的产品和服务。
举例来说,假设某电商平台推出了一款新品,原价为100元。但在秒杀期间,只需要支付10元便可购买该产品,而且该产品总共只有100件。那么这样的秒杀场景既能提高消费者的购买积极性,也能解决商家库存问题。
1.2 场景应用
Redis是一个基于内存的缓存数据库(In-memory data structure store),拥有快速响应和高并发等优点。在电商秒杀场景中,Redis作为数据库或缓存,发挥着极其重要的作用:
-
读写能力强:Redis支持多种数据类型操作,并且数据的存储和读取都非常迅速。在秒杀场景中,用户的访问需要在几毫秒内完成,否则用户就会离开。Redis的快速响应能力保证了用户能够顺利地完成秒杀操作。
-
分布式锁:在秒杀活动中,如果两个用户同时竞买同一件商品,将会产生资源竞争问题。为了解决这个问题,需要采用分布式锁机制来控制并发,保证数据的完整性和准确性。Redis提供了多种分布式锁的实现方案,使得开发人员在秒杀场景中等高并发下能够更精准、高效地处理业务。
二、Redis 优势与挑战
2.1 优势
-
快速读写:Redis的内存读写速度非常快,特别适合对单条数据进行频繁读写的情况,更是秒杀场景中所需的。
-
高并发:Redis拥有良好的并发支持,对于增删改查这样频繁地路由请求而言,Redis可以应对秒杀中的高并发访问。
-
数据类型多样:Redis支持多种数据类型,如字符串、哈希、集合、有序集合和列表等。在处理复杂的秒杀逻辑时,这些数据类型能显著提升其效率。
2.2 秒杀场景的挑战
-
容量不足:由于Redis使用内存来存储缓存,容量较小,可能无法容纳全部数据。
-
数据一致性问题:秒杀场景中,如果两个用户同时竞买同一件商品,需要使用分布式锁来解决竞争问题。而使用分布式锁可能会引起数据不一致的问题,需要在设计方案时考虑处理。
-
系统可用性问题:在电商秒杀场景中,系统的可用性非常重要。如果Redis服务器宕机或出现其他意外情况,会导致许多的秒杀请求失败,给用户和商家带来交易上的损失。因此,需要采取多种手段,如数据备份、灾备和监控等,保护系统稳定运行。
三、应用场景分析
3.1 库存预热
在一些特定的电商促销活动中,为了避免在秒杀开始时由于读取数据库等操作造成的系统延迟等问题,可以使用 Redis 对库存进行预热。
代码示例
public class StockPreheating {private static final String HOST = "127.0.0.1";private static final int PORT = 6379;public static void main(String[] args) {// 连接 RedisJedis jedis = new Jedis(HOST, PORT);// 设置预热初始值int stockNum = 100000;String key = "stock";jedis.set(key, String.valueOf(stockNum));// 循环生成商品预热缓存for(int i=1; i<=100000; i++){String skuKey = "sku:" + i;jedis.set(skuKey, "stock");}// 关闭 Redis 连接jedis.close();}
}
说明:
以上示例代码中,我们通过 Jedis Java 客户端连接到 Redis,并使用循环生成商品预热缓存,最后关闭 Redis 连接。
3.2 分布式锁
使用 Redis 的分布式锁可以解决分布式环境下的共享资源互斥问题。常用于秒杀、抢购等高并发场景中。
public class DistributedLock {private static final String HOST = "127.0.0.1";private static final int PORT = 6379;private static Jedis jedis = new Jedis(HOST, PORT);/*** 加锁操作* @param key 要加锁的键名* @param value 键值,在释放锁时需要校验* @param expireTime 锁过期时间,防止死锁* @return 是否加锁成功*/public static boolean lock(String key, String value, int expireTime) {// nx:key不存在时才进行设置,保证锁是互斥的// ex:键值设置后,经过 expireTime 秒自动过期释放锁,避免产生死锁return "OK".equals(jedis.set(key, value, "NX", "EX", expireTime));}/*** 解锁操作* @param key 要加锁的键名* @param value 键值,在加锁时设置的值* @return 是否解锁成功*/public static boolean unlock(String key, String value) {// lua 脚本:先判断键值是否与 value 相等,如果相等则删除 key 并返回 true,否则返回 falseString script ="if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +"then\n" +" return redis.call(\"del\",KEYS[1])\n" +"else\n" +" return 0\n" +"end";Object result = jedis.eval(script, Collections.singletonList(key), Collections.singletonList(value));return Long.parseLong(result.toString()) == 1;}
}
说明:
以上示例代码中,我们实现了 Redis 的加锁和解锁操作,并配合 Lua 脚本实现了多个命令的原子性,保证了加锁和解锁的一致性。
3.3 消息队列
使用 Redis 可以方便地实现消息队列,对于需要异步处理和业务解耦的场景非常有用。
public class MessageQueue {private static final String HOST = "127.0.0.1";private static final int PORT = 6379;private static Jedis jedis = new Jedis(HOST, PORT);/*** 发布订阅,实现消息队列* @param channel 订阅频道名* @param message 消息内容*/public static void sendMessage(String channel, String message) {jedis.publish(channel, message);}/*** 消息订阅者* @param channel 订阅频道名* @param listener 消息接收监听器*/public static void subscribe(String channel, JedisPubSub listener) {jedis.subscribe(listener, channel);}
}
说明:
以上示例代码中,我们实现了 Redis 的发布订阅消息队列功能,并通过 JedisPubSub 接口进行消息接收监听,实现了消费者消息异步接收处理。
四、系统设计方案
4.1 架构设计
本电商秒杀系统采用分布式架构,包含以下组件:
- 前端负载均衡器:将用户请求均匀地分发到不同的Web服务器上,使用Nginx负责实现。
- 应用服务器:接收用户请求并处理,使用Java + Spring Boot框架实现。
- Redis服务器:作为主要的数据存储和缓存介质,使用单机或集群模式均可。
- 数据库服务器:保存订单信息等长期存储的数据,使用MySQL数据库。
4.2 技术选型
- Web服务器:Nginx
- 后端框架:Spring Boot
- 数据库:MySQL
- 缓存:Redis
4.3 数据结构设计
为了保证秒杀活动的效率和可靠性,需要采用以下数据结构来支持系统的运行:
- 商品库存队列:使用Redis的List结构来保存活动商品的库存数量,每次有人秒杀成功后,将库存队列中的商品数量减1。
- 用户请求队列:使用Redis的List结构保存用户请求信息,每次请求进来后都先放入请求队列中,再由后台系统取出处理。如果请求队列过长,则说明系统可能已经无法处理更多的请求,需要返回秒杀失败的提示。
下面是Java代码实现:
// 库存队列操作
public class StockQueue {private static final String STOCK_QUEUE_KEY = "stock_queue";// 添加库存数量public static void addStock(int num) {RedisTemplate<String, Integer> redisTemplate = getRedisTemplate();redisTemplate.opsForList().rightPush(STOCK_QUEUE_KEY, num);}// 减少库存数量public static boolean reduceStock() {RedisTemplate<String, Integer> redisTemplate = getRedisTemplate();Long stockNum = redisTemplate.opsForList().leftPop(STOCK_QUEUE_KEY);if (stockNum == null || stockNum <= 0) {return false;}return true;}// 获取当前库存数量public static int getCurrentStock() {RedisTemplate<String, Integer> redisTemplate = getRedisTemplate();List<Integer> stockList = redisTemplate.opsForList().range(STOCK_QUEUE_KEY, 0, -1);int totalStock = 0;for (Integer num : stockList) {totalStock += num;}return totalStock;}private static RedisTemplate<String, Integer> getRedisTemplate() {// 配置Redis连接等操作return redisTemplate;}
}// 用户请求队列操作
public class RequestQueue {private static final String REQUEST_QUEUE_KEY = "request_queue";// 将用户请求添加到队列中public static void addRequest(String request) {RedisTemplate<String, String> redisTemplate = getRedisTemplate();redisTemplate.opsForList().rightPush(REQUEST_QUEUE_KEY, request);}// 从队列中获取一个请求进行处理public static String getRequest() {RedisTemplate<String, String> redisTemplate = getRedisTemplate();String request = redisTemplate.opsForList().leftPop(REQUEST_QUEUE_KEY);return request;}private static RedisTemplate<String, String> getRedisTemplate() {// 配置Redis连接等操作return redisTemplate;}
}
五、Redis 性能优化
5.1 集群部署
5.1.1 Redis Sentinel
Redis Sentinel 是 Redis 官方提供的高可用解决方案。它通过选举一个主节点以及多个备份节点的方式,实现了 Redis 服务的自动切换和故障恢复。在生产环境中建议使用 Redis Sentinel 来保证 Redis 服务的高可用性。
5.1.2 Redis Cluster
Redis Cluster 也是 Redis 官方提供的高可用解决方案。与 Redis Sentinel 不同的是,Redis Cluster 主要是通过分片来实现数据的自动平衡和故障恢复。它提供了强大的横向扩展能力,能够支持更大规模的数据存储和高并发访问。
5.2 缓存策略
5.2.1 本地缓存
本地缓存是指应用程序直接将数据存储在本地内存中,以加快数据的读取速度。应该注意的是,本地缓存一般只适用于数据量较小、相对固定且不怎么变化的情况下。当数据量过大或者经常变化时,本地缓存很容易引起内存溢出等问题。
代码示例:
// 使用 HashMap 作为本地缓存
private static Map<String, Object> localCache = new HashMap<>();public static Object getData(String key) {Object value = localCache.get(key);if (value != null) {return value;}// 从数据库中获取数据value = getDataFromDB(key);if (value != null) {localCache.put(key, value);}return value;
}
5.2.2 分布式缓存
分布式缓存是指将数据缓存在多个服务器节点上,以提高数据的读取速度和可用性。相比于本地缓存,分布式缓存具有更好的扩展性和容错性,能够适应大数据量和高并发访问的场景。
常见的分布式缓存方案包括 Memcached 和 Redis。其中 Redis 的性能和功能都要优于 Memcached,所以在选择分布式缓存方案时建议选择 Redis。
使用 Redis 进行分布式缓存代码示例:
// 创建 JedisPool 对象,用于连接 Redis
JedisPool jedisPool = new JedisPool("localhost", 6379);public static Object getData(String key) {try (Jedis jedis = jedisPool.getResource()) {// 尝试从 Redis 中获取数据byte[] value = jedis.get(key.getBytes());if (value != null) {// 如果存在则直接返回return deserialize(value);} else {// 否则从数据库中获取Object data = getDataFromDB(key);if (data != null) {// 并写入 Redis 中byte[] bytes = serialize(data);jedis.set(key.getBytes(), bytes);}return data;}}
}
5.3 流量控制
流量控制是指对系统中的请求和响应进行限制,以避免因高并发访问而造成系统崩溃或服务不可用。常用的流量控制方案包括限流和熔断。
限流是指通过设置各种限制条件,对请求进行限制的一种方式。常见的限流方式包括漏桶算法、令牌桶算法等。
熔断是指当系统出现故障或异常时,立即切断上游请求或降级处理的一种方式。在使用熔断技术时需要关注熔断恢复、熔断点、熔断时间等问题。
以下是 Java 使用 Google Guava 包进行的限流和熔断的代码示例:
// 创建 RateLimiter 对象,每秒最多处理 100 个请求
private static RateLimiter rateLimiter = RateLimiter.create(100);public static void processRequest(Request request) {if (!rateLimiter.tryAcquire()) {// 请求被拒绝,返回错误信息throw new RuntimeException("系统繁忙,请稍后再试!");}// 处理请求// ...
}// 使用 CircuitBreaker 进行熔断,最多允许出现 3 次错误
private static CircuitBreaker circuitBreaker = CircuitBreaker.create("circuitBreaker", CircuitBreakerConfig.custom().failureRateThreshold(50).ringBufferSizeInClosedState(3).waitDurationInOpenState(Duration.ofMillis(1000)).build());public static Object getData(String key) {try {// 尝试执行业务逻辑circuitBreaker.decorateSupplier(() -> getDataFromDB(key)).get();} catch (Exception e) {// 出现故障或异常,进行降级处理return fallback();}// 处理数据// ...
}
相关文章:
Redis 在电商秒杀场景中的应用
Redis 在电商秒杀场景中的应用 一、简介1.1 简介1.2 场景应用 二、Redis 优势与挑战2.1 优势2.2 秒杀场景的挑战 三、应用场景分析3.1 库存预热代码示例 3.2 分布式锁3.3 消息队列 四、系统设计方案4.1 架构设计4.2 技术选型4.3 数据结构设计 五、Redis 性能优化5.1 集群部署5.…...
大麦订单生成器 大麦一键生成订单
后台一键生成链接,独立后台管理 教程:修改数据库config/Conn.php 不会可以看源码里有教程 下载源码程序:https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3...
Java实现Google cloud storage 文件上传,Google oss
storage 控制台位置 创建一个bucket 点进bucket里面,权限配置里,公开访问,在互联网上公开,需要配置角色权限 新增一个访问权限 ,账号这里可以模糊搜索, 角色配置 给allUser配置俩角色就可以出现 在互联…...
适配器模式(AdapterPattern)
适配器模式 适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。 优缺点 优点: 单一职责原则。你可以将接口或数据转换代码从程序主要业务逻辑中分…...
Apache Kafka Learning
目录 一、Kafka 1、Message Queue是什么? 2、Kafka 基础架构 3、Kafka安装 4、Offset自动控制 5、Acks & Retries 6、幂等性 7、事务控制 8、数据同步机制 9、Kafka-Eagle 二、Maven项目测试 1、Topic API 2、生产者&消费者 一、Kafka Kafka是…...
手把手教你用idea实现Java连接MySQL数据库
目录 1.下载MySQL 2.下载mysql 的jdbc驱动 3.将驱动jar包导入idea 4.通过Java测试数据库是否连接成功 1.下载MySQL 首先如果没有mysql的需要先下载MySQL,可以看这个教程 MYSQL安装手把手(亲测好用)_程序小象的博客-CSDN博客 2.下载mysql…...
Ubuntu 22.04安装和使用ROS1可行吗
可行。 测试结果 ROS1可以一直使用下去的,这一点不用担心。Ubuntu会一直维护的。 简要介绍 Debian发行版^_^ AI:在Ubuntu 22.04上安装ROS1是可行的,但需要注意ROS1对Ubuntu的支持只到20.04。因此,如果要在22.04上安装ROS1&am…...
83 | Python可视化篇 —— Bokeh数据可视化
Bokeh 是一种交互式数据可视化库,它可以在 Python 中使用。它的设计目标是提供一个简单、灵活和强大的方式来创建现代数据可视化,同时保持良好的性能。Bokeh 支持多种图表类型,包括线图、散点图、柱状图、饼图、区域图、热力图等。此外,它还支持将这些图表组合在一起以创建…...
图像 检测 - RetinaNet: Focal Loss for Dense Object Detection (arXiv 2018)
图像 检测 - RetinaNet: Focal Loss for Dense Object Detection - 密集目标检测中的焦点损失(arXiv 2018) 摘要1. 引言2. 相关工作References 声明:此翻译仅为个人学习记录 文章信息 标题:RetinaNet: Focal Loss for Dense Obje…...
MySQL 与MongoDB区别
一、什么是MongoDB呢 ? MongoDB 是由C语言编写的,是一个基于分布式文件存储的开源数据库系统。在高负载的情况下,添加更多的节点,可以保证服务器性能。 MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB 将数据存储为一…...
Kaggle First Place Winner Solution Study——多变量回归问题
本期分享一个Kaggle上playground系列多变量回归问题的第一名解决方案。试着分析、复现、学习一下金牌选手的数据分析思路。 赛题链接: Prediction of Wild Blueberry Yield | Kagglehttps://www.kaggle.com/competitions/playground-series-s3e14第一名解决方案链…...
分布式应用:Zookeeper 集群与kafka 集群部署
目录 一、理论 1.Zookeeper 2.部署 Zookeeper 集群 3.消息队列 4.Kafka 5.部署 kafka 集群 6.FilebeatKafkaELK 二、实验 1.Zookeeper 集群部署 2.kafka集群部署 3.FilebeatKafkaELK 三、问题 1.解压文件异常 2.kafka集群建立失败 3.启动 filebeat报错 4.VIM报错…...
Last-Mile Embodied Visual Navigation 论文阅读
论文阅读 题目:Last-Mile Embodied Visual Navigation 作者:JustinWasserman, Karmesh Yadav 来源:CoRL 时间:2023 代码地址:https://jbwasse2.github.io/portfolio/SLING Abstract 现实的长期任务(例如…...
thunder gbm
文章目录 背景参考官网信息训练调参模型保存推理 背景 想在 GPU 上使用使用闪电般快速的提升方法?了解这个库就好了。在很多任务上,它都比 LightGBM 和 XGBoost 快。 ThunderGBM 的主要特征如下: 通常是其它库的 10 倍。 支持 Python&#x…...
数据结构--单链表
前言 上一章,我们讲了数据结构--动态顺序表,我们会发现有以下问题: 1.当我们要头部或者插入或删除时,都需要进行位置挪动,腾出某一个位置,时间复杂度为0(N); 2.增容需要申请新空间,…...
过程:从虚拟机上添加 git 并成功提交到 GitLab 的全过程
Ⅰ、准备工作: 1、Git 查看: 其一、命令:git --version // 此时就能在虚拟机环境下看到 git 的版本为: git version 2.41.0 其二、如何在虚拟机上安装 git : A、命令 : sudo apt-get install git B、然后再输入虚…...
机器学习笔记之优化算法(九)收敛速度的简单认识
机器学习笔记之优化算法——收敛速度的简单认识 引言收敛速度的判别标准 Q \mathcal Q Q-收敛速度 R \mathcal R R-收敛速度关于算法复杂度与收敛速度 引言 本节对收敛速度简单介绍。 收敛速度的判别标准 我们之前几节介绍了线搜索方法 ( Line Search Method ) (\text{Line …...
FPGA学习——Altera IP核调用之PLL篇
文章目录 一、IP核1.1 IP核简介1.2 FPGA中IP核的分类1.3 IP核的缺陷 二、PLL简介2.1 什么是PLL2.2 PLL结构图2.3 C4开发板上PLL的位置 三、IP核调用步骤四、编写测试代码五、总结 一、IP核 1.1 IP核简介 IP核(知识产权核),是在集成电路的可…...
经纬度坐标工具
LngLatUtil :用于计算里程数 import cn.hutool.core.util.ArrayUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import lombok.Getter; import lombok.Setter;import java.io.FileInputStream; import java.io.Serializable; import java.t…...
如何使用伪元素::before和::after?
伪元素(::before和::after)是CSS中非常有用的特性,它们允许你在元素的内容之前或之后插入额外的内容,并且不需要在HTML结构中添加额外的标记。这样可以方便地在页面上添加装饰性元素、图标、或者样式效果。以下是使用伪元素的基本方法: 1、创…...
Rydberg原子量子门实现原理与优化技术
1. Rydberg原子平台中的量子门实现基础1.1 Rydberg原子特性与量子计算优势Rydberg原子是指外层电子被激发到高主量子数能级的原子态,这类原子具有三个关键特性使其成为量子计算的理想平台:强偶极-偶极相互作用:当两个原子同时处于Rydberg态时…...
Python 3.7 + XGBoost 多分类实战:从数据清洗到SHAP模型解释的保姆级教程
Python 3.7 XGBoost 多分类实战:从数据清洗到SHAP模型解释的保姆级教程在机器学习领域,XGBoost因其出色的性能和可解释性成为众多数据科学家的首选工具。本文将带您完整走过多分类任务的全流程,从原始数据到可解释的预测模型,每个…...
如何快速掌握开源UE资产编辑器:UAssetGUI完整配置与实战指南
如何快速掌握开源UE资产编辑器:UAssetGUI完整配置与实战指南 【免费下载链接】UAssetGUI A tool designed for low-level examination and modification of Unreal Engine game assets by hand. 项目地址: https://gitcode.com/gh_mirrors/ua/UAssetGUI UAss…...
2605.VGGT-Omega 论文解读: 3D重建的Scaling Law, Register Attention效率革命 | Oxford+Meta CVPR26 Oral
VGGT-Omega: Scaling Feed-Forward 3D Reconstruction Jianyuan Wang, Minghao Chen, Shangzhan Zhang, Nikita Karaev, Johannes Schonberger, et al. Visual Geometry Group, Oxford Meta AI | CVPR 2026 Oral | arXiv 2605.15195 Paper | Project Page 一句话总结 VGGT-Om…...
《我看见的世界:李飞飞自传》第1-6章阅读笔记:从移民少女到AI教母的“看见“之旅
前言 当我们谈论人工智能时,我们谈论的是算法、数据、算力,是那些冰冷的代码和复杂的模型。但在《我看见的世界:李飞飞自传》中,李飞飞用她独特的视角告诉我们:AI的本质,是人类对"看见"世界的渴望…...
Owl-Alpha 新手快速上手指南
在处理大规模数据或构建高性能应用时,我们常常会遇到一个棘手的问题:如何在不阻塞主线程的情况下,高效地执行耗时任务?无论是处理图像、解析大型文件,还是进行复杂的数学运算,传统的单线程模式往往会让界面…...
skills CANN开源社区贡献技能包开发指南
前言 开源社区的健康运转,不仅依赖核心代码的贡献,还需要降低贡献门槛、提供清晰的指南和自动化工具。skills仓库是CANN开源社区的"贡献技能包",提供了一系列辅助脚本、代码模板、CI检查和文档生成工具,帮助新手快速上…...
基于Arduino与nRF24L01+的无线传感器平台设计与部署指南
1. 项目概述与设计思路如果你和我一样,喜欢在阳台或者小院子里种点蔬菜瓜果,那你肯定也遇到过这样的烦恼:出门几天,心里总惦记着家里的番茄苗是不是缺水了,小温室里的温度会不会太高。传统的温湿度计只能让你在现场读数…...
别再只用递归了!用C语言栈实现非递归快速排序,内存效率提升实战
从递归到迭代:C语言栈实现非递归快速排序的工程实践 在嵌入式开发和大规模数据处理场景中,递归实现的快速排序常常面临栈溢出风险。当排序10万个元素的数组时,递归深度可能达到log₂100000≈17层,在仅有2KB栈空间的STM32F103上极易…...
API渗透测试:契约驱动的协议/语义/架构三层攻防
1. 为什么“API渗透测试”不是Web渗透的简单延伸?很多人刚接触API安全时,第一反应是:“不就是把Burp Suite抓到的HTTP请求换个参数发一发?跟测网页表单差不多。”我2018年第一次接手某金融类SaaS平台的API安全评估时,也…...
