[SpringBoot] 解决Redis相关问题

news/2025/2/11 16:14:59/
  • 缓存穿透
  • 缓存击穿
  • 缓存雪崩

文章目录

      • 1.缓存穿透
      • 2.缓存击穿
      • 3.缓存雪崩

1.缓存穿透

缓存穿透指的是一个缓存系统无法缓存某个查询的数据,从而导致这个查询每一次都要访问数据库。

常见的Redis缓存穿透场景包括:

  1. 查询一个不存在的数据:攻击者可能会发送一些无效的查询来触发缓存穿透。
  2. 查询一些非常热门的数据:如果一个数据被访问的非常频繁,那么可能会导致缓存系统无法处理这些请求,从而造成缓存穿透。
  3. 查询一些异常数据:这种情况通常发生在数据服务出现故障或异常时,从而造成缓存系统无法访问相关数据,从而导致缓存穿透。

使用Guava在内存中维护一个布隆过滤器

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>29.0-jre</version>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

缓存中维护Bloom Filter

public class BloomFilterUtil {// 布隆过滤器的预计容量private static final int expectedInsertions = 1000000;// 布隆过滤器误判率private static final double fpp = 0.001;private static BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), expectedInsertions, fpp);/*** 向Bloom Filter中添加元素*/public static void add(String key){bloomFilter.put(key);}/*** 判断元素是否存在于Bloom Filter中*/public static boolean mightContain(String key){return bloomFilter.mightContain(key);}
}

在Controller中查询数据时,先根据请求参数进行Bloom Filter的过滤

@Autowired
private RedisTemplate<String, Object> redisTemplate;@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id){// 先从布隆过滤器中判断此id是否存在if(!BloomFilterUtil.mightContain(id.toString())){return null;}// 查询缓存数据String userKey = "user_"+id.toString();User user = (User) redisTemplate.opsForValue().get(userKey);if(user == null){// 查询数据库user = userRepository.findById(id).orElse(null);if(user != null){// 将查询到的数据加入缓存redisTemplate.opsForValue().set(userKey, user, 300, TimeUnit.SECONDS);}else{// 查询结果为空,将请求记录下来,并在布隆过滤器中添加BloomFilterUtil.add(id.toString());}}return user;
}

2.缓存击穿

缓存击穿指的是在一些高并发访问下,一个热点数据从缓存中不存在,每次请求都要直接查询数据库,从而导致数据库压力过大,并且系统性能下降的现象。

缓存击穿的原因通常有以下几种:

  • 缓存中不存在所需的热点数据:当系统中某个热点数据需要被频繁访问时,如果这个热点数据最开始没有被缓存,那么就会导致系统每次请求都需要直接查询数据库,造成数据库负担。
  • 缓存的热点数据过期:当一个热点数据过期并需要重新缓存时,如果此时有大量请求,那么就会导致所有请求都要直接查询数据库。

在遇到缓存击穿问题时,我们可以在查询数据库之前,先判断一下缓存中是否已有数据,如果没有数据则使用Redis的单线程特性,先查询数据库然后将数据写入缓存中。

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

先从缓存中查询数据,如果缓存中无数据则进行锁操作

@Autowired
private RedisTemplate<String, Object> redisTemplate;@GetMapping("/user/{id}")
public User getUserById(@PathVariable Long id){// 先从缓存中获取值String userKey = "user_"+id.toString();User user = (User) redisTemplate.opsForValue().get(userKey);if(user == null){// 查询数据库之前加锁String lockKey = "lock_user_"+id.toString();String lockValue = UUID.randomUUID().toString();try{Boolean lockResult = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 60, TimeUnit.SECONDS);if(lockResult != null && lockResult){// 查询数据库user = userRepository.findById(id).orElse(null);if(user != null){// 将查询到的数据加入缓存redisTemplate.opsForValue().set(userKey, user, 300, TimeUnit.SECONDS);}}}finally{// 释放锁if(lockValue.equals(redisTemplate.opsForValue().get(lockKey))){redisTemplate.delete(lockKey);}}}return user;
}

3.缓存雪崩

缓存中大量数据的失效时间集中在某一个时间段,导致在这个时间段内缓存失效并额外请求数据库查询数据的请求大量增加,从而对数据库造成极大的压力和负荷

  • 缓存服务器宕机:当缓存服务器宕机或重启时,大量的访问请求将直接命中数据库,并在同一时间段内导致大量的数据库查询请求,从而将数据库压力大幅提高。
  • 缓存数据同时失效:在某个特定时间点,缓存中大量数据的失效时间集中在一起,这些数据会在同一时间段失效,并且这些数据被高频访问,将导致大量的访问请求去查询数据库。
  • 缓存中数据过期时间设计不合理:当缓存中的数据有效时间过短,且数据集中在同一时期失效时,就容易导致大量的请求直接查询数据库,加剧数据库压力。
  • 波动式的访问过程:当数据的访问存在波动式特征时,例如输出某些活动物品或促销商品时,将会带来高频的查询请求访问,导致缓存大量失效并产生缓存雪崩。
  1. 一种是将缓存过期时间分散开,即为不同的数据设置不同的过期时间;
  2. 一种是使用Redis的多级缓存架构

添加相关依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency><groupId>net.sf.ehcache</groupId><artifactId>ehcache</artifactId><version>2.10.6</version>
</dependency>

application.properties中配置Ehcache缓存

spring.cache.type=ehcache

创建一个CacheConfig类,用于配置Ehcache

@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic EhCacheCacheManager ehCacheCacheManager(CacheManager cm){return new EhCacheCacheManager(cm);}@Beanpublic CacheManager ehCacheManager(){EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));cmfb.setShared(true);return cmfb.getObject();}
}

在ehcache.xml中添加缓存配置

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"updateCheck="true"monitoring="autodetect"dynamicConfig="true"><cache name="userCache" maxEntriesLocalHeap="10000" timeToLiveSeconds="60" timeToIdleSeconds="30"/></ehcache>

先从Ehcache缓存中获取,如果缓存中无数据则再从Redis缓存中获取数据

@Autowired
private RedisTemplate<String, Object> redisTemplate;@Autowired
private CacheManager ehCacheManager;@GetMapping("/user/{id}")
@Cacheable(value = "userCache", key = "#id")
public User getUserById(@PathVariable Long id){// 先从Ehcache缓存中获取String userKey = "user_"+id.toString();User user = (User) ehCacheManager.getCache("userCache").get(userKey).get();if(user == null){// 再从Redis缓存中获取user = (User) redisTemplate.opsForValue().get(userKey);if(user != null){ehCacheManager.getCache("userCache").put(userKey, user);}}return user;
}

http://www.ppmy.cn/news/34389.html

相关文章

python例程:五子棋(控制台版)程序

目录《五子棋&#xff08;控制台版&#xff09;》程序使用说明程序示例代码可执行程序及源码下载路径《五子棋&#xff08;控制台版&#xff09;》程序使用说明 在PyCharm中运行《五子棋&#xff08;控制台版&#xff09;》即可进入如图1所示的系统主界面。 图1 游戏主界面 具…

【Linux】进程概念二

文章目录进程概念二1. 进程状态2. 进程状态查看3. 僵尸进程3.1 僵尸进程的危害4. 孤儿进程5. 环境变量5.1 常见环境变量5.2 查看环境变量的方法5.3 测试PATH5.4 环境变量相关的命令5.5 环境变量的组织方式5.6 通过代码获取环境变量6. 程序地址空间7. 进程地址空间8. 扩展8.1 为…

三十七、实战演练之接口自动化平台的文件上传

上传文件功能 上传文件功能主要针对需要测试上传文件的接口。原理是&#xff0c;把要测试上传的文件先上传到测试平台&#xff0c;然后把路径写入 用例中&#xff0c;后台真正测试时再将其进行上传。 一、上传文件模型 在testplans/models.py 模块中编写如下模型&#xff1a;…

ViewService——一种保证客户端与服务端同步的方法

简介在分布式系统中&#xff0c;最常见的场景就是主备架构。但是如果主机不幸宕机&#xff0c;如何正确的通知客户端当前后端服务器的状况成为一个值得研究的问题。本文描述了一种简单的模型用于解决此问题。背景以一个分布式的Key-Value数据库为背景。数据库对外提供3个接口Ge…

基于Springboot+Vue2前后端分离框架的智慧校园系统源码,智慧学校源码+微信小程序+人脸电子班牌

▶ 智慧校园开发环境&#xff1a; 1、使用springboot框架Javavue2 2、数据库MySQL5.7 3、移动端小程序使用小程序原生语音开发 4、电子班牌固件安卓7.1&#xff1b;使用Java Android原生 5、elmentui &#xff0c;Quartz&#xff0c;jpa&#xff0c;jwt 智慧校园结构导图▶ 这…

【VSCode】Windows 下搭建 Fortran 环境

文章目录Part.I 预备知识Part.II 安装与配置Chap.I 编译环境Chap.II 插件Part.III 测试Chap.I 一个示例Chap.II 注意事项Part.I 预备知识 Fortran 是一种比较古老的语言了&#xff0c;当时作为一种科学计算工具&#xff0c;还是比较火的&#xff0c;因为很多有名的软件都是基于…

MagicalCoder可视化开发平台:轻松搭建业务系统,为企业创造更多价值

让软件应用开发变得轻松起来&#xff0c;一起探索MagicalCoder可视化开发工具的魔力&#xff01;你是否为编程世界的各种挑战感到头痛&#xff1f;想要以更高效、简单的方式开发出专业级的项目&#xff1f;MagicalCoder低代码工具正是你苦心寻找的产品&#xff01;它是一款专为…

菜鸟刷题Day5

⭐作者&#xff1a;别动我的饭 ⭐专栏&#xff1a;菜鸟刷题 ⭐标语&#xff1a;悟已往之不谏&#xff0c;知来者之可追 一.一维数组的动态和&#xff1a;1480. 一维数组的动态和 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个数组 nums 。数组「动态和」的计算公式…