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、创…...
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明
LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造,完美适配AGV和无人叉车。同时,集成以太网与语音合成技术,为各类高级系统(如MES、调度系统、库位管理、立库等)提供高效便捷的语音交互体验。 L…...
观成科技:隐蔽隧道工具Ligolo-ng加密流量分析
1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具,该工具基于TUN接口实现其功能,利用反向TCP/TLS连接建立一条隐蔽的通信信道,支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式,适应复杂网…...
深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
4. TypeScript 类型推断与类型组合
一、类型推断 (一) 什么是类型推断 TypeScript 的类型推断会根据变量、函数返回值、对象和数组的赋值和使用方式,自动确定它们的类型。 这一特性减少了显式类型注解的需要,在保持类型安全的同时简化了代码。通过分析上下文和初始值,TypeSc…...
Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
