0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何在SpringBoot中解决Redis的缓存穿透等问题

jf_78858299 来源:JAVA日知录 作者:飘渺Jam 2023-04-28 11:35 次阅读

今天给大家介绍一下如何在SpringBoot中解决Redis的缓存穿透、缓存击穿、缓存雪崩的问题。

缓存穿透

什么是缓存穿透

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

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

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

如何解决

我们可以使用Guava在内存中维护一个布隆过滤器。具体步骤如下:

  1. 添加Guava和Redis依赖:
<dependency>
    <groupId>com.google.guava<span class="hljs-name"groupId>
    <artifactId>guava<span class="hljs-name"artifactId>
    <version>29.0-jre<span class="hljs-name"version>
<span class="hljs-name"dependency>

<dependency>
    <groupId>org.springframework.boot<span class="hljs-name"groupId>
    <artifactId>spring-boot-starter-data-redis<span class="hljs-name"artifactId>
<span class="hljs-name"dependency>
  1. 创建一个BloomFilterUtil类,用于在缓存中维护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);
    }
}
  1. 在Controller中查询数据时,先根据请求参数进行Bloom Filter的过滤
@Autowired
private RedisTemplate

缓存击穿

什么是缓存击穿

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

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

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

如何解决

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

  1. 添加Redis依赖
<dependency>
    <groupId>org.springframework.boot<span class="hljs-name"groupId>
    <artifactId>spring-boot-starter-data-redis<span class="hljs-name"artifactId>
<span class="hljs-name"dependency>
  1. 在Controller中查询数据时,先从缓存中查询数据,如果缓存中无数据则进行锁操作
@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;
}

缓存雪崩

什么是缓存雪崩

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

常见的Redis缓存雪崩场景包括:

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

如何解决

在遇到缓存雪崩时,我们可以使用两种方法:一种是将缓存过期时间分散开,即为不同的数据设置不同的过期时间;另一种是使用Redis的多级缓存架构,通过增加一层代理层来解决。具体步骤如下:

  1. 添加相关依赖
<dependency>
    <groupId>org.springframework.boot<span class="hljs-name"groupId>
    <artifactId>spring-boot-starter-data-redis<span class="hljs-name"artifactId>
<span class="hljs-name"dependency>
<dependency>
    <groupId>net.sf.ehcache<span class="hljs-name"groupId>
    <artifactId>ehcache<span class="hljs-name"artifactId>
    <version>2.10.6<span class="hljs-name"version>
<span class="hljs-name"dependency>
  1. 在application.properties中配置Ehcache缓存
spring.cache.type=ehcache
  1. 创建一个CacheConfig类,用于配置Ehcache:
@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public EhCacheCacheManager ehCacheCacheManager(CacheManager cm){
        return new EhCacheCacheManager(cm);
    }
    @Bean
    public CacheManager ehCacheManager(){
        EhCacheManagerFactoryBean cmfb = new EhCacheManagerFactoryBean();
        cmfb.setConfigLocation(new ClassPathResource("ehcache.xml"));
        cmfb.setShared(true);
        return cmfb.getObject();
    }
}
  1. 在ehcache.xml中添加缓存配置
<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"/>

<span class="hljs-name"ehcache>
  1. 在Controller中查询数据时,先从Ehcache缓存中获取,如果缓存中无数据则再从Redis缓存中获取数据
@Autowired
private RedisTemplate

以上就是使用SpringBoot时如何解决Redis的缓存穿透、缓存击穿、缓存雪崩的常用方法。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • spring
    +关注

    关注

    0

    文章

    340

    浏览量

    14340
  • Boot
    +关注

    关注

    0

    文章

    149

    浏览量

    35828
  • Redis
    +关注

    关注

    0

    文章

    374

    浏览量

    10871
  • SpringBoot
    +关注

    关注

    0

    文章

    173

    浏览量

    177
收藏 人收藏

    评论

    相关推荐

    使用Redis缓存model层

    〈译〉使用REDIS处理RAILS MODEL缓存
    发表于 04-18 17:07

    redis缓存注解怎么使用

    spring boot —— redis 缓存注解使用教程
    发表于 09-11 14:43

    Spring bootRedis的使用

    【本人秃顶程序员】springboot专辑:Spring bootRedis的使用
    发表于 03-27 11:42

    Redis在高速缓存系统的序列化算法研究

    Redis是一个key?value存储系统,通过对Redis高速缓存系统的序列化算法优化,可提高缓存读取的效率和存储容量。引入现代统计学B
    发表于 11-23 16:07 0次下载

    Java 使用Redis缓存工具的详细解说

    本文是关于Java 使用Redis缓存工具的详细解说。详细步骤请看下文
    的头像 发表于 02-09 14:10 7882次阅读
    Java 使用<b class='flag-5'>Redis</b><b class='flag-5'>缓存</b>工具的详细解说

    Redis常见面试题及答案

    本文的面试题如下: Redis 持久化机制 缓存雪崩、缓存穿透缓存预热、缓存更新、
    的头像 发表于 12-16 11:44 2196次阅读
    <b class='flag-5'>Redis</b>常见面试题及答案

    缓存雪崩/穿透/击穿的解决方案

    缓存是我们项目应用肯定会使用,是我们数据库的守护神,能够保证数据库的稳定,能够提高整个系统的性能。一般我们采用市面上的redis、memcahce方案;redis已经非常强大了,每秒支持几万的连接时不成问题。
    发表于 01-26 09:44 1311次阅读
    <b class='flag-5'>缓存</b>雪崩/<b class='flag-5'>穿透</b>/击穿的解决方案

    Redis缓存的异常原因及其处理办法分析

    Redis 是当前最流行的 NoSQL 数据库。Redis 主要用来做缓存使用,在提高数据查询效率、保护数据库等方面起到了关键性的作用,很大程度上提高系统的性能。
    的头像 发表于 02-06 15:02 740次阅读

    SpringBoot+Redis实现点赞功能的缓存和定时持久化(附源码)

    用户对浏览内容进行【点赞/取赞】,并发送【点赞/取赞】请求到后端,这些信息先存入Redis缓存,再每隔两小时将Redis的内容直接写入数
    的头像 发表于 02-09 16:38 4566次阅读

    基于SpringBoot+Redis的转盘抽奖

    基于SpringBoot+Redis技术实现转盘抽奖活动项目,含前端、后台及数据库文件
    的头像 发表于 02-28 14:24 1527次阅读
    基于<b class='flag-5'>SpringBoot+Redis</b>的转盘抽奖

    缓存穿透了如何解决

    首先来了解几个概念: 缓存穿透:大量请求根本不存在的key 缓存雪崩:redis中大量key集体过期 缓存击穿:
    的头像 发表于 05-23 09:54 687次阅读
    <b class='flag-5'>缓存</b>被<b class='flag-5'>穿透</b>了如何解决

    如何用Springboot整合Redis

    本篇文件我们来介绍如何用Springboot整合Redis。 1、Docker 安装 Redis 1.1 下载镜像 docker pull redis: 6 . 2 . 6 1.2 创
    的头像 发表于 10-08 14:56 582次阅读
    如何用<b class='flag-5'>Springboot</b>整合<b class='flag-5'>Redis</b>

    SpringBoot AOP + Redis 延时双删功能实战

    注意:要知道经常修改的数据表不适合使用Redis,因为双删策略执行的结果是把Redis中保存的那条数据删除了,以后的查询就都会去查询数据库。所以Redis使用的是读远远大于改的数据缓存
    的头像 发表于 10-13 16:08 633次阅读
    <b class='flag-5'>SpringBoot</b> AOP + <b class='flag-5'>Redis</b> 延时双删功能实战

    Oracle与Redis Enterprise协同,作为企业缓存解决方案

    单独使用Oracle作为企业缓存数据库时,会出现哪些问题呢?使用Redis Enterprise与Oracle共同用作企业级缓存或副本数据库,会出现哪些喜人的提升呢?Orcle配合使用Redi
    的头像 发表于 11-22 10:00 471次阅读
    Oracle与<b class='flag-5'>Redis</b> Enterprise协同,作为企业<b class='flag-5'>缓存</b>解决方案

    Redis缓存预热+缓存雪崩+缓存击穿+缓存穿透要点简析

    缓存预热就是系统上线后,提前将相关的缓存数据直接加载到缓存系统。
    的头像 发表于 12-25 09:41 905次阅读
    <b class='flag-5'>Redis</b><b class='flag-5'>缓存</b>预热+<b class='flag-5'>缓存</b>雪崩+<b class='flag-5'>缓存</b>击穿+<b class='flag-5'>缓存</b><b class='flag-5'>穿透</b>要点简析