一般面试中考察的题目通常是由三类组成的,基础面试题、进阶面试题、开放性面试题,而本文的题目则属于一个开放性的面试题,但对于 Redis 这种以数据为核心的缓存中间件来说,实现在海量数据中查询一个值是否存在还是相对比较容易的。
因为是海量数据,所以我们就无法将每个键值都存起来,然后再从结果中检索数据了,比如数据库中的 select count(1) from tablename where id='XXX'
,或者是使用 Redis 普通的查询方法 get XXX 等方式,我们只能依靠专门处理此问题的“特殊功能”和相关方法来实现数据的查找。
我们本文的面试题是如何在海量数据中查询一个值是否存在?
典型回答
统计一个值是否在海量数据中可以使用布隆过滤器,布隆过滤器(Bloom Filter)是 1970 年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。也就是说布隆过滤器的优点就是计算和查询速度很快,但是缺点也很明显就是存在一定的误差。
在 Redis 中布隆过滤器的用法如下:
- bf.add 添加元素;
- bf.exists 判断某个元素是否存在;
- bf.madd 添加多个元素;
- bf.mexists 判断多个元素是否存在;
- bf.reserve 设置布隆过滤器的准确率。
使用示例如下:
127.0.0.1:6379> bf.add user xiaoming
(integer) 1
127.0.0.1:6379> bf.add user xiaohong
(integer) 1
127.0.0.1:6379> bf.add user laowang
(integer) 1
127.0.0.1:6379> bf.exists user laowang
(integer) 1
127.0.0.1:6379> bf.exists user lao
(integer) 0
127.0.0.1:6379> bf.madd user huahua feifei
1) (integer) 1
2) (integer) 1
127.0.0.1:6379> bf.mexists user feifei laomiao
1) (integer) 1
2) (integer) 0
可以看出以上结果没有任何误差,我们再来看一下准确率 bf.reserve
的使用:
127.0.0.1:6379> bf.reserve user 0.01 200
(error) ERR item exists #已经存的 key 设置会报错
127.0.0.1:6379> bf.reserve userlist 0.9 10
OK
可以看出此命令必须在元素刚开始执行,否则会报错,它有三个参数:key、errorrate 和 initialsize。 其中:
- error_rate:允许布隆过滤器的错误率,这个值越低过滤器占用空间也就越大,以为此值决定了位数组的大小,位数组是用来存储结果的,它的空间占用的越大 (存储的信息越多),错误率就越低,它的默认值是 0.01;
- initial_size:布隆过滤器存储的元素大小,实际存储的值大于此值,准确率就会降低,它的默认值是 100。
布隆过滤器常见使用场景有:
- 垃圾邮件过滤;
- 爬虫里的 URL 去重;
- 判断一个值在亿级数据中是否存在。
布隆过滤器在数据库领域的使用也比较广泛,例如:HBase、Cassandra、LevelDB、RocksDB 内部都有使用布隆过滤器。
考点分析
这道题考察的主要目的是检验你对布隆过滤器是否了解,如果没有相关知识筹备,通常的回答是先存储数据再进行检索,这显然就中了面试官的“圈套”,把一道“送分题”活活搞成了“送命题”,如果你有幸看到本篇文章,那就太好了。
和此知识点相关的面试题还有以下这些:
网友评论