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
相关推荐
安当加密36 分钟前
达梦数据库TDE透明加密解决方案:构建高安全数据存储体系
网络·数据库·安全
Jabes.yang3 小时前
Java求职面试实战:从Spring Boot到微服务架构的技术探讨
java·数据库·spring boot·微服务·面试·消息队列·互联网大厂
阿巴~阿巴~4 小时前
Redis 核心文件、命令与操作指南
数据库·redis·缓存·客户端·服务端
koping_wu4 小时前
【Redis】用Redis实现分布式锁、乐观锁
数据库·redis·分布式
abcefg_h4 小时前
关系型数据库与非关系型数据库
数据库·nosql
海奥华25 小时前
SQLEXPLAIN 详解
数据库·mysql
00后程序员张5 小时前
【Python】基于 PyQt6 和 Conda 的 PyInstaller 打包工具
运维·服务器·数据库
huihuihuanhuan.xin5 小时前
后端八股之Redis
数据库·redis·缓存
情深不寿3175 小时前
MySQL————数据库基础
数据库·mysql
程序新视界6 小时前
如何选择合适的数据库?PostgreSQL与MySQL各项对比
数据库·mysql·postgresql