缓存穿透、缓存并发、缓存雪崩、缓存预热

xingqinstar 2018-09-08

缓存穿透、缓存并发和缓存雪崩是常见的由高并发引起的缓存问题,而缓存预热是缓存雪崩的一种解决方案。

这篇文章将来带大家讲解一下这四个概念。

1.缓存穿透

缓存穿透指的是并发使用大量缓存中不存在的key进行查询,由于缓存无法命中,大量的查询会穿过缓存直接查询数据库,使得数据库压力太大,导致数据库可能被拖垮。

一般是受到了恶意的攻击才会导致这种问题,所以一旦遇到了缓存穿透的问题就会非常棘手,所以我们需要在代码层面提前尽量规避掉这个问题。下面介绍几种规避方案:

1.1 缓存空值

我们可以将从数据库查询不到结果的key也存到缓存中,value为nul,这样下次再查询该key的时候就会在缓存中拿到null直接返回,而不会去数据库查询了。

如果攻击者一直使用不同的key来攻击,那么不仅会引起缓存穿透的问题,同时还会导致缓存中存了大量null值的key(空值可以不用缓存太久,设置一个有效期可以提前释放缓存),所以光缓存空值还不够,还需要和其他方案相结合。

1.2 使用有规则的key

通常缓存的key都是一个指定前缀加一个id拼接组成的,那么我们可以对这个进行合法校验。

比如说id的长度是否合法,以及我们可以按照一定规则生成缓存key中的id,这样在有查询过来的时候我们可以先校验一下key是否合法,不合法的话直接返回null。

1.3 使用布隆过滤器存储所有可能的key,再进行校验查询的key

我们可以把所有可能的key放到一个集合里,这样一个请求过来的时候,可以先去集合里看是否存在这个key,不存在就直接返回null。

那么这个集合的数据类型怎么定义呢?上一篇文章讲一致性Hash算法的时候就分析了数组和红黑树的查询效率,最终选择了时间复杂度O(logN)的红黑树。

这里我们校验key是否存在在集合中的时候的要求是:

- 在集合中的元素一定要返回”在集合中”

- 但是不在集合中的元素我们允许一定概率的返回”在集合中”

所以我们可以使用布隆过滤器来帮我们实现这个数据结构,帮我们过滤掉大量不合法的key。

2.缓存并发

缓存并发指定是在查询高并发的场景下,如果某一个缓存的key过期了,那么将有大量的请求访问数据库并且会写缓存,这样会导致数据库的压力变大。

如果从缓存中查询不到结果,那么就会去数据库查询,那么可以对这一段代码加锁,这样其他请求就会进入等待,可以使用下面的代码实现:

Object result = getFromCache(key);
if (result == null) {
 synchronized(object) {
 result = getFromCache(key);
 if (result != null) {
 return getFromCache;
 }
 getFromDB(key)
 ......
 }
}
1
2
3
4
5
6
7
8
9
10
11

当然了,加锁会导致其他线程等待,那么线程等待还是数据库压力二者之间就需要根据具体业务场景做一个抉择了。

3.缓存雪崩

缓存雪崩指的是大量key同时失效或者缓存服务器重启(清除缓存数据)之后,大量的用户请求涌入服务器导致所有压力全部落到数据库上,从而压垮数据库。

缓存雪崩的问题的引发可以分为两个部分,一个是大量key同时过期,另一个是缓存服务器重启,下面我们分两中情况来分析一下解决方案。

3.1 大量key同时过期

大量key同时过期是由于我们写入缓存的时候设置的有效时间相同导致的,所以我们可以在缓存时间上再加上一定的随机数使得不同的key失效时间不一致就行了。

3.2 缓存服务器重启

缓存服务器重启导致缓存清空的问题可以使用缓存预热来解决,我们直接看下面的缓存预热。

4.缓存预热

缓存预热指的是在缓存系统重启(没有任何缓存数据)的时候,提前将热点数据加载到缓存中。

我们可以在深夜或者用户访问量不大的时候进行缓存服务器的重启,重启之后进行缓存预热,提前预热部分热点数据。

需要注意的是缓存预热的时候可以对缓存数据的有效时间加上一定的随机数,不然可能会导致大量的key同时过期。


通常在业务并发量不是太大的场景下很难遇到上面描述的几个问题,但是理论知识我们还是要了解一下的。

今天关于缓存的分享就到这里了,希望大家多多支持。

缓存穿透、缓存并发、缓存雪崩、缓存预热

相关推荐