Skip to main content

如何解决缓存雪崩?

作者:程序员马丁

在线博客:https://open8gu.com

note

大话面试,技术同学面试必备的八股文小册,以精彩回答应对深度问题,助力你在面试中拿个offer。

回答话术

缓存雪崩是应用系统指在某个时间点上,缓存中的大部分数据同时失效,导致大量的请求直接访问底层数据库或后端服务,从而造成数据库负载剧增,甚至导致数据库崩溃的情况。

image.png

通常情况下,缓存中的数据会设置不同的过期时间,以避免同时失效的情况。然而,如果某个不可控的事件导致了大量缓存同时失效,就会出现缓存雪崩。

需要从以下几个方面去解决缓存雪崩问题:

1. 使用锁机制避免数据库频繁访问

可以使用锁机制来避免多个相同的请求同时访问数据库,只让一个或少量请求去加载数据,其他请求等待。

参考如何防止缓存击穿文章锁章节:✅ 如何解决缓存击穿?

2. 缓存预热

在系统启动时或者活动开始前,将热门数据预先加载到缓存中,避免在高并发访问时出现数据不存在缓存中,还需要去数据库加载问题。

参考数据如何缓存预热文档:✅ 缓存如何预热?

3. 热点数据永不过期

对于一些热点数据,可以设置永不过期,以保证这部分数据始终在缓存中可用。同时,需要保障缓存设置的内存淘汰策略是不淘汰或者从带过期时间 Key 中去淘汰。

关于缓存淘汰策略,可以参考:✅ Redis 常用内存淘汰策略?

4. 合理设置缓存失效时间

避免所有缓存在同一时间点失效,可以采用随机分布的方式设置缓存失效时间,或者使用带有随机偏移的失效时间。

通过以上几种方案组合使用,可以一定程度上减少缓存雪崩的可能性。

问题详解

1. 热点数据永不过期

咱们以两种系统举例,分别是电商系统以及 12306 铁路购票系统:

  • 电商系统:比如需要参加秒杀的商品数据,我们为了避免因设计过期时间自动过期或者不合适的 Redis 过期策略自动清楚,需要将参与秒杀的商品数据直接设置为永不过期。
  • 12306:同上,如果在售票期间的列车数据缓存就不要设置过期时间了。

如果商品过了秒杀时间或者 12306 列车时间过了售票时间,这些数据岂不是会占用缓存空间么?

这种一般都会有定时任务在活动结束或过了售票周期后统一删除。

2. 合理设置缓存失效时间

可以通过设置随机过期时间或者固定过期时间引入随机偏移量两种方案,一定程度上缓解缓存雪崩问题。

注意,只是一定程度上,并不能真正解决,如果缓存基数比较大,这种随机过期可能也会撞一起。最终解决方案一定是结合其它解决方案一起使用。

2.1. 随机过期时间

import java.util.Random;

public class RandomExpiration {

public static void main(String[] args) {
Random random = new Random();
int minExpiration = 60; // 最小过期时间,单位秒
int maxExpiration = 600; // 最大过期时间,单位秒
// 生成随机的过期时间
int randomExpiration = random.nextInt(maxExpiration - minExpiration + 1) + minExpiration;
System.out.println("随机过期时间:" + randomExpiration + " 秒");
}
}

2.2. 固定过期时间并引入随机偏移量

import java.util.Random;

public class RandomOffsetExpiration {

public static void main(String[] args) {
Random random = new Random();
int fixedExpiration = 300; // 固定的过期时间,单位秒
int maxOffset = 60; // 最大的随机偏移量,单位秒
// 生成随机的偏移量
int randomOffset = random.nextInt(maxOffset + 1);
// 结合固定过期时间和随机偏移量,得到最终的过期时间
int finalExpiration = fixedExpiration + randomOffset;
System.out.println("固定过期时间:" + fixedExpiration + " 秒");
System.out.println("随机偏移量:" + randomOffset + " 秒");
System.out.println("最终过期时间:" + finalExpiration + " 秒");
}
}