缓存穿透、缓存雪崩、缓存击穿

什么是缓存

一般是程序中的临时数据或从数据库中拿到数据等,存到内存中后,称之为缓存。

有什么作用

假设不使用缓存的情况下,全部请求都去请求数据库。像电商这种上架以后基本几天不会动的服装图,全部请求打到数据库,数据库处理起来很吃力。和直接读取内存相比来说,维护成本要大很多。所以把查出来的数据放到缓存中,以供大数据量级别的请求。
下面介绍几个缓存常见的问题以及解决方案:

缓存穿透(cache penetration)

说明

什么是缓存穿透?数据库与缓存中都不存在此值,请求穿过缓存,访问数据库,导致数据库压力过大。

解释

假如有一个 id 为 10001,现在调用getById/10001,现在我们去调用缓存,cacheUtils.getKey(10001),获取不到值以后我们开始请求数据库,然后数据库也查询不到 id 为 10001 的,因为其不存在。但是,这个时候这一条数据查不到就不会被加入缓存,然后就会一直循环往复的调用数据库本体,最终导致数据库压力过大。

解决方案

  • 根据条件临时缓存空值,过期时间设少一点,不然会对数据库的准确性产生影响。

  • 对用户的请求进行合法的校验。

  • 使用布隆过滤器。

缓存雪崩(cache avalanche)

说明

什么是缓存雪崩?缓存雪崩是一种场景,大量缓存的数据同时到期或缓存服务关闭,并且突然之间所有对这些数据的搜索都将冲击数据库并给数据库层造成高负载并影响性能。

解决方案

使用集群,例如使用 Redis 集群来确保缓存服务器在任何时间都在运行。使用断路器或者限制请求频率,例如一些网站频繁刷新就会直接频繁。

缓存击穿(cache breakdown)

说明

什么是缓存击穿?缓存击穿是一种场景,当缓存的数据过期的那一刻,同时对过期的数据进行大量的请求,这会导致这些请求直接命中数据库,然后,DB 卒。

解决方案

  • 使用互斥锁。

  • 通过异步线程更新缓存的数据,使其用不过期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public List<String> getDataList() {
// 从缓存读取数据
List<String> result = getDataFromCache();
if (result.isEmpty()) {
synchronized (this) {
// 从缓存读取数据
result = getDataFromCache();
if (result.isEmpty()) {
// 从数据库查询数据
result = getDataFromDB();
// 将查询到的数据写入缓存
setDataToCache(result);
}
}
}
return result;
}

https://cuifuan.github.io/2021/08/18/back-end/middleware/cache/
作者
cuifuan
发布于
2021年8月18日
许可协议