缓存穿透问题

缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常出于容错的考虑,如果从存储层查不到数据则不写入缓存层。一般对于未命中的数据我们是按照如下方式进行处理的:

1.缓存层不命中。

2.存储层不命中,不将空结果写回缓存。

3.返回空结果。

redis穿透解决 redis的穿透_redis


缓存穿透将导致不存在的数据每次请求都要到存储层去查询,失去了缓存保护后端存储的意义。

缓存穿透问题可能会使后端存储负载加大,由于很多后端存储不具备高并发性,甚至可能造成后端存储宕掉。

方案一:缓存空对象

redis穿透解决 redis的穿透_布隆过滤器_02


缓存空对象会有一个必须考虑的问题:

空值做了缓存,意味着缓存层中存了更多的键,需要更多的内存空间(如果是攻击,问题更严重),比较有效的方法是针对这类数据设置一个较短的过期时间,让其自动剔除。

方案二:布隆过滤器拦截

布隆过滤器介绍

概念:

布隆过滤器(英语:Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。

如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定。链表、树、散列表(又叫哈希表,Hash table)等等数据结构都是这种思路。但是随着集合中元素的增加,我们需要的存储空间越来越大。同时检索速度也越来越慢,上述三种结构的检索时间复杂度分别为 O(n),O(log n),O(n/k)

布隆过滤器的原理是,当一个元素被加入集合时,通过 K 个散列函数将这个元素映射成一个位数组中的 K 个点,把它们置为 1。检索时,我们只要看看这些点是不是都是 1 就(大约)知道集合中有没有它了:如果这些点有任何一个 0,则被检元素一定不在;如果都是 1,则被检元素很可能在。这就是布隆过滤器的基本思想。

示例:

google guava 包下有对布隆过滤器的封装,BloomFilter。

redis穿透解决 redis的穿透_数据_03


redis穿透解决 redis的穿透_redis_04


布隆过滤器拦截

设置过期时间,让其自动过期失效,这种在很多时候不是最佳的实践方案。

我们可以提前将真实正确的商品 Id,在添加完成之后便加入到过滤器当中,每次再进行查询时,先确认要查询的 Id 是否在过滤器当中,如果不在,则说明 Id 为非法 Id,则不需要进行后续的查询步骤了。

redis穿透解决 redis的穿透_redis穿透解决_05


redis穿透解决 redis的穿透_布隆过滤器_06