问题
在 缓存穿透 一文中我们讲到了可以使用布隆过滤器可以解决缓存穿透问题。例如查询一个用户ID,它是如何快速查询,它又不是数组,又是怎么提高随机访问特性的查询效率的呢?
我们平时刷短视频(抖音,快手等)是如何做到刷到的视频是不重复的呢?如果把用户刷到的信息都存放在数据库中,抖音八亿用户,每次都要进行 exists 判断该视频是否被看过,滑动一下就需要去库中查看,磁盘是远远跟不上性能的。如果用缓存,缓存容量有限,且需要对浏览的记录设置永不过期,这么多用户,用内存存记录,成本的?哪个企业会这么做?
什么是布隆过滤器
布隆过滤器(Bloom Filter) 是 1970 年由布隆提出的。实际上是一个很长的二进制向量和若干个hash 函数,它类似于一个简单的 set 结构,但是存在一定的误判率。可以用更小的空间,解决去重问题的一种数据结构。
布隆过滤器存在一定误判率,当它说某个值不存在是,那就是一定不存在,说某一个值存在是,这个值可能不存在。只要设置参数合理,精确度足够精确,误判率极低。
使用场景
- 去重:用最小的空间解决去重问题。
- 解决缓存穿透:用户传不存在参数查询数据,缓存中一定没有,此时大量请求访问数据库,数据库被过跨,可以在访问数据库之前加布隆过滤器解决这些问题。
- 垃圾邮件的过滤。
- 文件处理软件(word),错误单词检测
- 网络爬虫,重复的 url 检测。
- Hbase 行过滤。
原理分析
布隆过滤器实际上是一个很长的二进制向量(仅包含 0 或 1 位值的列表,最初所有的值均设置为 0)和一系列随机映射函数。位数组,以bit为单位,相当于 int 类型的 1/32。
添加数据时,多个 hash 函数对 key 进行计算,得到不同的位置,并把这几个位置置位1,当查看key是否存在时,多个hash函数对key进行运算,如果对应的多个位置,其中有一个位置为0,则一定不存在,如果全部为1,不一定存在,因为存多个key时,可能会映射到相同的位置(例如上图中的 d)。可见如果布隆过滤器初始化值越大,误判率就会越低。
m/n 与误差率成反比,k与误差率成反比。
注意
- 实际元素数量远小于初始化数量;
- 当实际元素数量超过初始化数量时,重建一个更大的布隆过滤器, 再将所有的历史元素批量添加。
- 哈希函数越多,布隆过滤器的错误率就会变大。控制布隆过滤器中哈希函数的个数。有这样一个计算最优哈希函数个数的数学公式: 哈希函数个数 k = (m/n) * ln(2)。其中 m 为 bit 数组长度,n 为要存入的对象的个数。实际上,如果哈希函数个数为 1,且数组长度足够,布隆过滤器就可以退化成一个位图。所以,我们可以认为“位图是只有一个特殊的哈希函数,且没有被压缩长度的布隆过滤器”
基本用法
bf.add
添加一个元素
bf.exists
查询一个元素是否存在
bf.madd
批量添加多个元素
bf.mexists
查询多个元素是否存在
直接在客户端操作是不可以用的,Redis 官方 提供的布隆过滤器到了 Redis 4.0 提供了插件功能之后才正式登场。布隆过滤器作为一个插件加载到 Redis Server 中。
下载,解压,编译