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
相关推荐
deadknight92 小时前
Oracle密码过期处理方式
数据库·oracle
Ljubim.te2 小时前
数据库第01讲章节测验(选项顺序可能不同)
数据库
吱吱喔喔2 小时前
数据分表和分库原理
数据库·分表·分库
快乐非自愿2 小时前
KES数据库实践指南:探索KES数据库的事务隔离级别
数据库·oracle
一只fish2 小时前
Oracle的RECYCLEBIN回收站:轻松恢复误删对象
数据库·oracle
weixin_440401692 小时前
分布式锁——基于Redis分布式锁
java·数据库·spring boot·redis·分布式
TOR-NADO2 小时前
数据库概念题总结
数据库·oracle
云计算练习生2 小时前
理解MySQL核心技术:存储过程与函数的强大功能
数据库·mysql·存储过程·函数·mysql函数
zengson_g2 小时前
当需要对大量数据进行排序操作时,怎样优化内存使用和性能?
java·数据库·算法·排序算法
胡尚3 小时前
Nacos源码分析:心跳机制、健康检查、服务发现、AP集群
java·数据库·服务发现