缓存穿透是什么?
我们在查询缓存时是根据某一个Key去查询,如果查询的Key不在缓存中,那么就会查数据库,如果这种查询很多那么这种场景就被称为 缓存穿透,还有缓存击穿,缓存雪崩等,有兴趣可以了解一下。
我们先看一下这个请求流程图,是不是存在一些问题,我们缓存中只有 USER:1, USER:2, USER:3 这几个 key,而请求中的 Key 并不在我们缓存中,那么这些请求还是需要查询数据库,这样缓存形同虚设。
如何解决存在的问题?
其实我们可以通过将已有的数据保存起来,在用户查询时先去查询是不是已有的数据,若不在我们保存已有数据的结果中,那么直接返回,不需要查询缓存以及数据库了,若存在那么可能就需要继续查下去了。
保存数据的数据结构
还有一个问题就是我们将已有的数据保存在哪里呢?还是缓存中?那么数据结构选用什么呢?集合?字符串?(我们先假设我们有一千万条数据,更简便一点,我们仅仅只保存 ID)
首先看一下集合,我们就假设我们有一个Key且每个元素就一个字节,那么一千万个元素占用的空间就有34M左右,对于内存来说,这么大的空间是非常珍贵的,所以我们暂时先不能将数据存在集合中。
那么我们再看一下字符串,这个就有点不太好计算了,预估占用也是很大的一个存储空间,因为有的开发会考虑将ID进行拼接保存在字符串中,那么一千万个ID拼接在一起,必然不会很短,所以我们也不考虑。
我们可以解决这个问题,但是方法并不是最优的,例如集合可能会占用很大的一个内存空间,字符串也同样会,但是字符串还有一个问题就是处理效率低下。
布隆过滤器
安装(Docker)
docker pull redislabs/rebloom
docker run -p 6379:6379 --name redis-bloom redislabs/rebloom
docker exec -it redis-bloom bash
先看一下在 Redis 中是如何使用的,可以通过下面示例中可以看到我们在往 TESTUSER 中加入值,然后再判断,若有则返回 1 没有则返回 0,很容易实现判断是否存在的问题
root@3f20c7a7e3ac:/data# redis-cli
127.0.0.1:6379> bf.add TESTUSER 1
(integer) 1
127.0.0.1:6379> bf.add TESTUSER 2
(integer) 1
127.0.0.1:6379> bf.exists TESTUSER 1
(integer) 1
127.0.0.1:6379> bf.exists TESTUSER 3
(integer) 0
127.0.0.1:6379> bf.exists TESTUSER 2
(integer) 1
127.0.0.1:6379> bf.madd TESTUSER 3 4 5 6 7
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) (integer) 1
5) (integer) 1
127.0.0.1:6379> bf.mexists TESTUSER 3 4 5 6 7 8
1) (integer) 1
2) (integer) 1
3) (integer) 1
4) (integer) 1
5) (integer) 1
6) (integer) 0
布隆过滤器(Bloom Filter)概念
它的存在时为了检索一个元素是否存在一个集合(并不是 Redis 中的集合)中,它的空间效率和时间效率都远超过一般的算法,但是数据量庞大的话会有一定的误判率还有就是无法删除。
原理
当一个元素加入到布隆过滤器的集合中时,通过 K 个 hash 函数将这个元素映射成为一个数组中的下标,并将数组下标所在的值设置为 1,在检索值是否存在时,只需要通过 K 次 hash 函数找到对应的坐标,并判断这些元素的值是否为 1,若全部为 1 则有可能存在,若全为 0 则肯定不存在。
布隆过滤器的数学公式就不在列举了,个人觉得对理解原理是没有特别大的帮助。
设置错误率以及预估存储的数量
布隆过滤器是有一定的错误率的,若我们对所存的值进行预估,设置存储数量的时候尽量设置的大一些,这样可能会避免错误的判断。
127.0.0.1:6379> bf.reserve TESTRESERVE 0.03 10000
OK
127.0.0.1:6379> bf.add TESTRESERVE 1
(integer) 1
127.0.0.1:6379> bf.reserve TESTRESERVE 0.03 10000
(error) ERR item exists
总结
其实关于布隆过滤器的使用场景是很多的,也可以解决或者避免很多问题,例如缓存穿透等,我们在使用它的时候,我们真正的知道它的优缺点,那么我们才会放心的去使用,并且通过优化来尽量避免它的缺点,这样就会发挥它最大的价值。
延伸
布隆过滤器的缺点其一是对已经增加的值无法进行删除,那么有一个过滤器可以解决这个问题,那就是布谷鸟过滤器,功能几乎与布隆过滤器一致,但是布谷鸟过滤器是可以将已增加的元素进行删除。感兴趣的可以看一下。