Redis缓存穿透、缓存击穿与缓存雪崩的详细讲解和案例示范

2024-08-31 05:28

本文主要是介绍Redis缓存穿透、缓存击穿与缓存雪崩的详细讲解和案例示范,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在高并发的电商交易系统中,Redis缓存的使用可以极大地提高系统的性能。然而,缓存机制也面临着一些挑战,尤其是缓存穿透、缓存击穿和缓存雪崩问题。这些问题如果处理不当,可能导致系统的性能大幅下降,甚至出现系统崩溃的情况。本文将详细介绍这些问题及其解决方案,并结合电商交易系统的案例进行示范,提供相应的代码示例。


第一章:Redis缓存穿透

1.1 缓存穿透的定义

缓存穿透是指用户请求的数据在缓存中不存在,并且在数据库中也不存在。由于缓存未命中,每次请求都会直接访问数据库,导致数据库压力骤增,最终可能导致系统崩溃。

1.2 缓存穿透的原因
  1. 用户恶意攻击:攻击者通过大量请求不存在的数据来绕过缓存,直接攻击数据库。
  2. 未正确设置缓存:对于数据库中不存在的值,未设置空缓存,导致每次查询都穿透到数据库。
1.3 解决方案
1.3.1 使用布隆过滤器

布隆过滤器是一种概率型数据结构,能够高效判断一个元素是否在一个集合中存在。布隆过滤器通过多个哈希函数将数据映射到位数组中,以达到快速判断的目的。虽然布隆过滤器存在一定的误判率,但它可以有效防止缓存穿透。

实现代码示例
public class BloomFilterService {private BloomFilter<String> bloomFilter;public BloomFilterService(int expectedInsertions, double fpp) {bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, fpp);}public void addToFilter(String key) {bloomFilter.put(key);}public boolean mightContain(String key) {return bloomFilter.mightContain(key);}
}
1.3.2 缓存空对象

对于数据库中不存在的数据,将空对象缓存起来,并设置较短的过期时间。这样可以避免大量查询穿透到数据库。

实现代码示例
public String getProductInfo(String productId) {String cacheKey = "product:" + productId;String productInfo = redisTemplate.opsForValue().get(cacheKey);if (productInfo != null) {return productInfo;}// 查询数据库Product product = productRepository.findById(productId);if (product == null) {// 数据库中不存在,缓存空值redisTemplate.opsForValue().set(cacheKey, "", 60, TimeUnit.SECONDS);return null;}// 存在,缓存数据redisTemplate.opsForValue().set(cacheKey, product.toString(), 10, TimeUnit.MINUTES);return product.toString();
}
1.3.3 图示

在这里插入图片描述

1.4 电商系统中的案例

在电商系统中,用户请求某个商品详情,而该商品可能已经下架或者从未存在。在这种情况下,通过使用布隆过滤器和缓存空对象,能够有效防止系统缓存穿透。


第二章:Redis缓存击穿

2.1 缓存击穿的定义

缓存击穿是指缓存中某个热点数据在到期失效的瞬间,有大量的并发请求同时访问该数据,由于缓存失效,这些请求都会穿透到数据库,导致数据库负载剧增,甚至可能导致系统崩溃。

2.2 缓存击穿的原因
  1. 高并发访问:某些热点数据由于频繁访问,可能在缓存失效的瞬间引发大量并发请求直接访问数据库。
  2. 缓存未提前续期:如果没有在缓存即将失效时提前续期,可能导致缓存击穿。
2.3 解决方案
2.3.1 使用互斥锁(分布式锁)

在高并发场景下,当缓存失效时,只有一个请求能够获取锁,其他请求需要等待,直到锁释放后才能访问数据库并更新缓存。

实现代码示例
public String getProductInfo(String productId) {String cacheKey = "product:" + productId;String productInfo = redisTemplate.opsForValue().get(cacheKey);if (productInfo != null) {return productInfo;}// 获取分布式锁String lockKey = "lock:product:" + productId;boolean isLock = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 5, TimeUnit.SECONDS);if (isLock) {try {// 查询数据库并更新缓存Product product = productRepository.findById(productId);redisTemplate.opsForValue().set(cacheKey, product.toString(), 10, TimeUnit.MINUTES);return product.toString();} finally {// 释放锁redisTemplate.delete(lockKey);}} else {// 如果没有获取到锁,稍后重试Thread.sleep(100);return getProductInfo(productId);}
}
2.3.2 提前续期

在缓存即将失效时,提前续期缓存数据,以防止缓存失效瞬间的大量并发请求击穿缓存。

实现代码示例
@Scheduled(fixedRate = 5000)
public void refreshHotKeys() {List<String> hotKeys = getHotKeys(); // 获取热点数据的缓存Keyfor (String key : hotKeys) {redisTemplate.expire(key, 10, TimeUnit.MINUTES); // 重新设置过期时间}
}
2.3.3 图示

在这里插入图片描述

2.4 电商系统中的案例

在电商系统中,某些热门商品如秒杀商品会被频繁访问。在缓存失效时,通过分布式锁和提前续期策略,可以有效防止缓存击穿问题。


第三章:Redis缓存雪崩

3.1 缓存雪崩的定义

缓存雪崩是指大量缓存数据在同一时间失效,导致大量请求同时穿透到数据库,造成数据库压力过大,可能导致系统不可用。

3.2 缓存雪崩的原因
  1. 缓存集中失效:由于缓存数据的过期时间设定不合理,大量缓存数据在同一时间失效,导致请求集中穿透到数据库。
  2. 系统重启:如果Redis服务因故障重启,可能导致大量缓存数据失效。
3.3 解决方案
3.3.1 缓存数据过期时间设置为随机值

通过为缓存数据设置随机的过期时间,避免大量缓存数据在同一时间失效。

实现代码示例
public void cacheProductInfo(String productId, String productInfo) {int expireTime = 10 + new Random().nextInt(5); // 随机生成10-15分钟的过期时间String cacheKey = "product:" + productId;redisTemplate.opsForValue().set(cacheKey, productInfo, expireTime, TimeUnit.MINUTES);
}
3.3.2 预热缓存

在系统上线或重启时,提前将热点数据加载到缓存中,防止大量请求瞬间击穿缓存。

实现代码示例
public void cachePreheat() {List<String> hotProductIds = productRepository.getHotProductIds();for (String productId : hotProductIds) {String productInfo = productRepository.findById(productId).toString();redisTemplate.opsForValue().set("product:" + productId, productInfo, 10, TimeUnit.MINUTES);}
}
3.3.3 图示

在这里插入图片描述

第四章:总结

通过本文的详细讲解,我们对Redis缓存穿透、缓存击穿和缓存雪崩三大问题有了深入的理解。我们使用布隆过滤器和缓存空对象来防止缓存穿透,通过分布式锁和提前续期防止缓存击穿,并通过设置随机过期时间和缓存预热来防止缓存雪崩。每种问题都有其特定的解决方案,通过这些解决方案,我们能够有效提高系统的稳定性和性能。

通过图示,我们直观地展示了这些解决方案的实现过程,这有助于更好地理解和应用这些技术。在实际的电商系统中,合理运用这些技术能够极大提升系统的抗压能力,确保在高并发场景下的稳定性。

希望这篇文章能为您在实际项目中处理Redis缓存问题提供有益的参考。

这篇关于Redis缓存穿透、缓存击穿与缓存雪崩的详细讲解和案例示范的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1123007

相关文章

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

SQL Server数据库死锁处理超详细攻略

《SQLServer数据库死锁处理超详细攻略》SQLServer作为主流数据库管理系统,在高并发场景下可能面临死锁问题,影响系统性能和稳定性,这篇文章主要给大家介绍了关于SQLServer数据库死... 目录一、引言二、查询 Sqlserver 中造成死锁的 SPID三、用内置函数查询执行信息1. sp_w

Python UV安装、升级、卸载详细步骤记录

《PythonUV安装、升级、卸载详细步骤记录》:本文主要介绍PythonUV安装、升级、卸载的详细步骤,uv是Astral推出的下一代Python包与项目管理器,主打单一可执行文件、极致性能... 目录安装检查升级设置自动补全卸载UV 命令总结 官方文档详见:https://docs.astral.sh/

Redis Cluster模式配置

《RedisCluster模式配置》:本文主要介绍RedisCluster模式配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录分片 一、分片的本质与核心价值二、分片实现方案对比 ‌三、分片算法详解1. ‌范围分片(顺序分片)‌2. ‌哈希分片3. ‌虚

Python包管理工具核心指令uvx举例详细解析

《Python包管理工具核心指令uvx举例详细解析》:本文主要介绍Python包管理工具核心指令uvx的相关资料,uvx是uv工具链中用于临时运行Python命令行工具的高效执行器,依托Rust实... 目录一、uvx 的定位与核心功能二、uvx 的典型应用场景三、uvx 与传统工具对比四、uvx 的技术实

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal

SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程

《SpringBoot集成LiteFlow实现轻量级工作流引擎的详细过程》LiteFlow是一款专注于逻辑驱动流程编排的轻量级框架,它以组件化方式快速构建和执行业务流程,有效解耦复杂业务逻辑,下面给大... 目录一、基础概念1.1 组件(Component)1.2 规则(Rule)1.3 上下文(Conte

Springboot整合Redis主从实践

《Springboot整合Redis主从实践》:本文主要介绍Springboot整合Redis主从的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言原配置现配置测试LettuceConnectionFactory.setShareNativeConnect

Springboot3+将ID转为JSON字符串的详细配置方案

《Springboot3+将ID转为JSON字符串的详细配置方案》:本文主要介绍纯后端实现Long/BigIntegerID转为JSON字符串的详细配置方案,s基于SpringBoot3+和Spr... 目录1. 添加依赖2. 全局 Jackson 配置3. 精准控制(可选)4. OpenAPI (Spri

六个案例搞懂mysql间隙锁

《六个案例搞懂mysql间隙锁》MySQL中的间隙是指索引中两个索引键之间的空间,间隙锁用于防止范围查询期间的幻读,本文主要介绍了六个案例搞懂mysql间隙锁,具有一定的参考价值,感兴趣的可以了解一下... 目录概念解释间隙锁详解间隙锁触发条件间隙锁加锁规则案例演示案例一:唯一索引等值锁定存在的数据案例二: