Redis系列之布隆过滤器

125 阅读6分钟

Redis系列之布隆过滤器

之前在介绍Redis基本数据结构的那篇文章中,我提到过布隆过滤器,在大数据面前,布隆过滤器有着很棒的性能,也有着很广泛的应用场景,比如垃圾邮件的过滤加粗样式缓存雪崩、击穿、拦截器等。

什么是布隆过滤器

布隆过滤器(Boolm Filter)是1970年由布隆提出的一种算法。实际上是一个很长的二进制向量和一系列随机映射函数。其实就是一种数据结构,类似于Hash、Set,主要用于检索一个元素是否在一个集合中

在编程中,我们想要判断一个元素是不是在一个集合中,一般会想到将所有元素保存起来,然后比较确定。链表、树等待数据结构都是这种思路。但是,随着集合中元素的增加,需要的存储空间越来越大,检索的速度也就越来越慢了。

布隆过滤器相对于Hash、Set加粗样式等数据结构不同的时,它无需存储key,对于每个key,只需要k个比特位,每个存储一个标志,用来判断key是否在集合中。

所以,它可以实现高效的插入和查询并且在时间和空间方面都具有巨大的优势,都是常数

布隆过滤器的原理是:当一个元素被加入集合时,通过N个Hash函数将这个元素进行Hash,算出一个整数索引值,然后对数组长度进行取模运算,从而得到一个位置,每个Hash函数都会得到一个不同的位置,然后再把位数组中的几个位置点都置为1。

在这里插入图片描述

检索时,也会把哈希的几个位置算出来,然后看看位数组中对应的几个位置是否都为1,只要有一个位为0,那么就说明布隆过滤器里不存在这个元素。

但是,这几个位置都为1,并不能完全说明这个元素就一定存在其中,有可能这些位置为1是因为其他元素的存在,这就是布隆过滤器会出现误判的原因。

简而言之就是,布隆过滤器可以判断某个元素一定不存在,但是无法判断一定存在。

所以,我们可以得出布隆过滤器的优缺点如下:

优点

1)占用空间极小,插入和查询速度极快; 2)布隆过滤器可以表示全集,其它任何数据结构都不能;

缺点

1)误算率随着元素的增加而增加; 2)一般情况下无法删除元素;

Redis中的布隆过滤器

在Redis4.0之前,只能通过bitmaps来实现,在Redis4.0之后,官方提供了module能力,这时候,官方提供的RedisBloom才算正式出道。

bitmaps

我们知道,计算机是以二进制为底层的存储单位,一个字节等于8位。

比如“big”字符串是由三个字符组成,这三个字符对应的ASCII码分别为98,105,103,对应的二进制存储如下:

在这里插入图片描述

基本命令如下:

# 设置字节值
setbit key offset value
# 获取字节值
gitbit key offset
# 获取指定范围内为1的元素个数
bitcount key [start end]

RedisBloom

在Redis4.0之后。我们可以将RedisBloom作为一个模块加载到Redis Server中,从而获取强大的布隆过滤器能力。

在RedisBloom中,布隆过滤器有两个基本命令,分别是:

1)bf.add:添加元素到布隆过滤器中,类似于集合的sadd命令,不过bf.add命令只能一次添加一个元素,如果想一次添加多个元素,可以使用bf.madd命令。

2)bf.exists:判断某个元素是否在过滤器中,类似于集合的sismember命令,不过bf.exists命令只能一次查询一个元素,如果想一次查询多个元素,可以使用bf.mexists命令。

上面的例子中使用的布隆过滤器只是默认参数的布隆过滤器,它在我们第一次使用bf.add命令时自动创建的。

Redis还提供了自定义参数的布隆过滤器,想要尽量减少布隆过滤器的误判,就要设置合理的参数。

在使用bf.add命令添加元素之前,使用bf.reserve命令创建一个自定义的布隆过滤器。bf.reserve命令有三个参数,分别是:

key:键

error_rate:期望错误率,期望错误率越低,需要的空间就越大。

capacity:初始容量,当实际元素的数量超过这个初始化容量时,误判率上升。

示例如下:

127.0.0.1:6379> BF.RESERVE customFilter 0.0001 600000
OK

如果对应的key已经存在时,在执行bf.reserve命令就会报错。如果不使用bf.reserve命令创建,而是使用Redis自动创建的布隆过滤器,默认的error_rate是 0.0001,capacity是 60。

布隆过滤器的error_rate越小,需要的存储空间就越大,对于不需要过于精确的场景,error_rate设置稍大一点也可以。

布隆过滤器的capacity设置的过大,会浪费存储空间,设置的过小,就会影响准确率,所以在使用之前一定要尽可能地精确估计好元素数量,还需要加上一定的冗余空间以避免实际元素可能会意外高出设置值很多。

总之,error_rate和 capacity都需要设置一个合适的数值。

官方地址如下:github.com/RedisBloom/…

布隆过滤器典型的应用场景

场景一:数据库防止穿库

Google Bigtable,HBase 和 Cassandra 以及 Postgresql 使用BloomFilter来减少不存在的行或列的磁盘查找。避免代价高昂的磁盘查找会大大提高数据库查询操作的性能。

场景二:判断用户是否访问过

判断用户是否阅读过某视频或文章,比如抖音或头条,当然会导致一定的误判,但不会让用户看到重复的内容。

场景三:解决缓存穿透

一般判断用户是否在缓存中,如果在则直接返回结果,不在则查询db,如果来一波冷数据,会导致缓存大量击穿,造成雪崩效应,这时候可以用布隆过滤器当缓存的索引,只有在布隆过滤器中,才去查询缓存,如果没查询到,则穿透到db。如果不在布隆器中,则直接返回。

场景四:Web拦截器,黑名单校验

如果相同请求则拦截,防止重复被攻击。用户第一次请求,将请求参数放入布隆过滤器中,当第二次请求时,先判断请求参数是否被布隆过滤器命中。可以提高缓存命中率。

当然,这里只是几个典型的应用场景,其实还有很多好玩的地方,等待着你去探索。如果玩出新花样了,记得call我哦!