缓存雪崩
什么是缓存雪崩?
缓存雪崩描述的就是这样⼀个简单的场景:缓存在同⼀时间⼤⾯积的失效,后⾯的请求都直接落到了数据库上,造成数据库短时间内承受⼤量请求。 这就好⽐雪崩⼀样,摧枯拉朽之势,数据库的压⼒可想⽽知,可能直接就被这么多请求弄宕机了。
举个例⼦:系统的缓存模块出了问题⽐如宕机导致不可⽤。造成系统的所有访问,都要⾛数据库。
还有⼀种缓存雪崩的场景是:有⼀些被⼤量访问数据(热点缓存)在某⼀时刻⼤⾯积失效,导致对应的请求直接落到了数据库上。 这样的情况,有下⾯⼏种解决办法:
举个例⼦ :秒杀开始 12 个⼩时之前,我们统⼀存放了⼀批商品库存等信息到 Redis 中,设置的缓存过期时间也是 12 个⼩时,那么秒杀开始的时候,这些秒杀的商品的访问直接就失效了。导致的情况就是,相应的请求直接就落到了数据库上,就像雪崩⼀样可怕。
下面给出图片加以说明(借鉴黑马程序员)
image.png
有哪些解决办法?
针对Redis服务不可用的情况:
- 分布式缓存:采用Redis集群,将缓存数据分散到多个节点上,从而降低单个节点故障的影响。
- 限流和熔断:对请求进行限流或熔断,当发现大量请求涌入时,限制或拒绝一部分请求,保护数据库免受压力过大的影响。
针对热点缓存失效的情况:
- 设置随机过期时间:在设置缓存数据的过期时间时,可以加入随机因子,使得缓存数据的过期时间不完全相同,避免同时失效。
- 引入缓存锁:在缓存数据过期时,通过加锁的方式,只允许一个线程去重新加载缓存,其他线程等待。这样可以避免多个线程同时访问数据库,从而减轻数据库的压力。
- 缓存预热:在系统启动时,或者在低峰期,将热点数据预先加载到缓存中。这样在高峰期访问热点数据时,可以直接命中缓存,避免缓存雪崩。
下面提供一个实现缓存预热功能的代码示例:
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
public class CacheUtil {
private static RedissonClient redissonClient;
// 初始化RedissonClient
static {
Config config = new Config();
config.useSingleServer().setAddress("redis://localhost:6379");
redissonClient = Redisson.create(config);
}
public static void set(String key, Object value) {
redissonClient.getBucket(key).set(value);
}
public static Object get(String key) {
return redissonClient.getBucket(key).get();
}
public static void preloadCache() {
// 在系统启动时预热缓存,将热点数据加载到Redis中
// 例如从数据库中加载热门文章数据,然后将数据放入Redis中
List<Article> hotArticles = fetchHotArticlesFromDatabase();
for (Article article : hotArticles) {
set(article.getId(), article);
}
}
private static List<Article> fetchHotArticlesFromDatabase() {
// 从数据库中查询热门文章数据的代码
// 省略具体实现
}
}
在这个示例代码中,我们通过RedissonClient来操作Redis,并使用Redis的set命令将热点数据加载到Redis中。在系统启动时,调用preloadCache()
方法进行缓存预热,将热点数据加载到Redis中。这样在高峰期访问热点数据时,可以直接从Redis中获取数据,避免缓存雪崩问题。请注意根据实际情况配置Redis的连接信息。