Redis的缓存击穿,缓存雪崩,缓存穿透问题

Redis的缓存击穿,缓存雪崩,缓存穿透问题

引言

在分布式系统中,缓存是一种常用的提升性能和减轻数据库压力的手段。然而,缓存系统也存在一些常见的问题,如缓存击穿、缓存雪崩和缓存穿透。这些问题如果不加以解决,可能会导致系统性能下降,甚至崩溃。本文将详细介绍这三个问题是如何发生的,并说明在Spring开发框架中的解决方案。

缓存击穿

问题描述

缓存击穿(Cache Breakdown)指的是缓存中某个热点数据在失效的瞬间,有大量请求同时到达该缓存节点,从而导致请求直接打到数据库上,造成数据库瞬时压力过大。

发生原因

当某一热点数据在缓存中过期,而此时有大量请求并发访问该数据时,由于缓存失效,所有请求都会直接访问数据库,造成数据库的瞬时高并发压力。

解决方案

在Spring中,可以使用以下方法来解决缓存击穿问题:

  1. 使用互斥锁:在缓存失效时,使用互斥锁(如Redis分布式锁)控制只有一个线程能够访问数据库,其它线程等待,从而避免数据库被击穿。
java 复制代码
String cacheKey = "hotData";
String data = redisTemplate.opsForValue().get(cacheKey);
if (data == null) {
    synchronized (this) {
        data = redisTemplate.opsForValue().get(cacheKey);
        if (data == null) {
            data = databaseService.getDataFromDb();
            redisTemplate.opsForValue().set(cacheKey, data, 1, TimeUnit.HOURS);
        }
    }
}
return data;
  1. 提前预热缓存:在热点数据过期前,通过后台任务或者定时任务提前刷新缓存,保证热点数据始终在缓存中存在。
java 复制代码
@Scheduled(fixedRate = 3600000)
public void preloadCache() {
    String hotData = databaseService.getHotDataFromDb();
    redisTemplate.opsForValue().set("hotData", hotData, 1, TimeUnit.HOURS);
}

缓存雪崩

问题描述

缓存雪崩(Cache Avalanche)指的是在某一时刻,缓存中大量数据同时失效,大量请求直接打到数据库,造成数据库压力剧增甚至宕机。

发生原因

缓存雪崩通常发生在缓存集群中某些节点同时失效,或者缓存设置了相同的过期时间导致大量数据同时过期。

解决方案

在Spring中,可以采用以下策略解决缓存雪崩问题:

  1. 设置不同的缓存失效时间:为不同的缓存数据设置不同的过期时间,避免大量缓存数据同时失效。
java 复制代码
redisTemplate.opsForValue().set("data1", data1, randomExpireTime(), TimeUnit.SECONDS);
redisTemplate.opsForValue().set("data2", data2, randomExpireTime(), TimeUnit.SECONDS);

private long randomExpireTime() {
    return 3600 + new Random().nextInt(600);
}
  1. 使用二级缓存:通过引入本地缓存(如Guava Cache)和远程缓存(如Redis),形成多级缓存架构,减轻缓存失效时对数据库的直接压力。
java 复制代码
Cache<String, String> localCache = CacheBuilder.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES)
        .maximumSize(1000)
        .build();

public String getData(String key) {
    String data = localCache.getIfPresent(key);
    if (data == null) {
        data = redisTemplate.opsForValue().get(key);
        if (data == null) {
            data = databaseService.getDataFromDb();
            redisTemplate.opsForValue().set(key, data, 1, TimeUnit.HOURS);
        }
        localCache.put(key, data);
    }
    return data;
}

缓存穿透

问题描述

缓存穿透(Cache Penetration)指的是查询一个不存在的数据时,因为缓存中没有该数据的记录,请求直接到达数据库,并且数据库也没有该数据,导致每次请求都穿透缓存直接查询数据库。

发生原因

缓存穿透通常是由于恶意请求或者错误请求频繁查询缓存中不存在的数据,缓存未能对此类请求进行有效处理。

解决方案

在Spring中,可以通过以下方法解决缓存穿透问题:

  1. 缓存空值:对于查询结果为空的数据也进行缓存,设置一个较短的过期时间,防止相同请求频繁穿透缓存。
java 复制代码
String data = redisTemplate.opsForValue().get(cacheKey);
if (data == null) {
    data = databaseService.getDataFromDb();
    if (data == null) {
        redisTemplate.opsForValue().set(cacheKey, "", 5, TimeUnit.MINUTES);
    } else {
        redisTemplate.opsForValue().set(cacheKey, data, 1, TimeUnit.HOURS);
    }
}
return data; 
  1. 使用布隆过滤器:在缓存前增加布隆过滤器,用于快速判断请求的数据是否存在,避免对不存在的数据频繁查询数据库。
java 复制代码
@Bean
public BloomFilter<String> bloomFilter() {
    return BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), 10000);
}

@Autowired
private BloomFilter<String> bloomFilter;

public String getData(String key) {
    if (!bloomFilter.mightContain(key)) {
        return null;
    }
    String data = redisTemplate.opsForValue().get(key);
    if (data == null) {
        data = databaseService.getDataFromDb();
        if (data != null) {
            redisTemplate.opsForValue().set(key, data, 1, TimeUnit.HOURS);
            bloomFilter.put(key);
        }
    }
    return data;
}

参考链接

  1. Redis 官方文档
  2. Spring Cache 官方文档
  3. Guava Cache 官方文档
  4. 布隆过滤器 Wikipedia
相关推荐
玄同7651 小时前
SQLite + LLM:大模型应用落地的轻量级数据存储方案
jvm·数据库·人工智能·python·语言模型·sqlite·知识图谱
吾日三省吾码1 小时前
别只会“加索引”了!这 3 个 PostgreSQL 反常识优化,能把性能和成本一起打下来
数据库·postgresql
chian-ocean1 小时前
百万级图文检索实战:`ops-transformer` + 向量数据库构建语义搜索引擎
数据库·搜索引擎·transformer
小Tomkk1 小时前
数据库 变更和版本控制管理工具 --Bytebase 安装部署(linux 安装篇)
linux·运维·数据库·ci/cd·bytebase
qq_12498707532 小时前
基于JavaWeb的大学生房屋租赁系统(源码+论文+部署+安装)
java·数据库·人工智能·spring boot·计算机视觉·毕业设计·计算机毕业设计
倒流时光三十年2 小时前
SpringBoot 数据库同步 Elasticsearch 性能优化
数据库·spring boot·elasticsearch
forestsea2 小时前
深入理解Redisson RLocalCachedMap:本地缓存过期策略全解析
redis·缓存·redisson
码农小卡拉2 小时前
深入解析Spring Boot文件加载顺序与加载方式
java·数据库·spring boot
佛祖让我来巡山3 小时前
Redis 为什么这么快?——「极速快递站」的故事
redis·redis为什么快?
怣503 小时前
MySQL多表连接:全外连接、交叉连接与结果集合并详解
数据库·sql