缓存穿透就是请求了大量原本就不存在的数据,比如请求A数据,此时缓存没有数据,则从数据库查询,但是数据库也没有值,所以返回空的时候,缓存依然没有数据。这类的请求一大,数据库要一直处理这些数据,很容易导致崩溃。以下方法也适用于缓存集体失效的缓存雪崩。
保存空值
如果数据库没有数据,则在redis中也赋值相应的key(比如A)为空。这样每次下次再发送A过来,直接从redis中获取,就不用再查数据库了。流程如下:
缺点:
1、如果A在当前没有数据,过几秒有数据,所以上述方法中还要加一个过期时间。但是这样数据的一致性也很难保证,比如在过期时间内,A有了数据,但是redis里还是空值。
2、如果请求的数据是随机的,那缓存的数据A被命中的概率也是很低的,这种情况下,还是会到数据库,而且浪费了大量内存空间。
互斥锁
保存空值的另外一个缺点就是,比如三个请求同时访问A时,此时缓存是没有数据的,则他们会一起请求数据库,针对这个情况,我们就可以使用互斥锁来做了,也就是用redis的setnx方法,流程是这样的:
获取锁和设置失效时间两个对redis操作的步骤不是原子性的,也就是说,如果setnx成功了,expire不一定成功,所以可以用lua来做。不过新版本可以将这两个操作以原子性的形式操作。
这个方法同样也没办法处理随机的数据的。
布隆过滤器
布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。误判率和数组的长度有关,数组长度越长,误判率越低,但是所需要的内存越高。
比如我们定义一个8个bit的布隆过滤器:
hash(A)=1后存入
hash(B)=3后存入
hash(C)=3后存入,此时还是3,则3位置的值还是1。这个就是很难删除的原因,因为删除3的位置,会连带着B、C都影响。
如果hash(D)也等于3,此时隆过滤器会觉得他存在的,这个就是误判率。
结合缓存使用的时候,流程是这样的:
在4中,尽管有误判的key,但是概率已经很小了,对数据库不会有太大的影响。
另外一个缺点就是我们要知道哪些key要放入布隆过滤器。
异步构建缓存
这种方法,在缓存不存在的时候,并没有直接读取数据库,而是放入队列里,让队列去更新缓存。当队列处理完后,后面的请求就有值了。
缺点就是数据不够实时。
流程如下:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。