面试点-布隆过滤器

直入主题,布隆过滤器是什么?

布隆过滤器是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中,它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定 的误差率 和删除困难 。

面试题–腾讯2019有一道面试题就说:

一个网站有 20 亿 url 存在一个黑名单中,这个黑名单要怎么存?
若此时随便输入一个 url,你如何快速判断该 url 是否在这个黑名单中?
并且需在给定内存空间(比如:500M)内快速判断出

我们假设就用hashset基于haspMap的特性,保证键的唯一,理论上时间复杂度是:O(1)。这时候的空间复杂度呢?URL字符串通过hash函数得到一个integer的值,integer占4个字节,那这里的20条数据就需要
20(亿) *4 /1024/1024/1024 = 7.45G 的内存

简单说一下:1G= 1024MB = 1024*1024KB = 1024*1024*1024 B 
           1B = 8bit 

那根据计算远远超出空间存储大小,不符合空间复杂度的要求。

使用布隆过滤器

首先,一个hash 算法得出的最大Integer为 : Integer.MAX_VALUE = 2147483647,意思就是任何一个URL的哈希都会在0~2147483647之间。

那么我们假如先定义一个2147483647 长度的byte数组,用来存储集合所有可能的值,为了存储这个byte数组,系统只需要 2147483647 / 8 /1024 /1024 = 256M。

那么,回到实际问题上,这些URL都经过hash算法之后,假如 一个hash值为2,那么落到这个byte数组在第二位上的数就是1,这个byte数组将是 000…000000010,基于此我们就可以将这20亿数全部hash并落到byte数组中。

使用布隆过滤器所导致的问题

回到上面,假如说我现在byte数组第二位是1,那么这个URL可能存在?因为有可能其它URL因为hash碰撞hash出来的也是2,这就是误判。

但是如果这个byte数组上的第二位是0。那么这个URL就一定不存在。

这里就可以进行多次hash的策略了
为了减少因hash碰撞导致的误判概率,可以对这个URL用不同的hash算法进行N次hash,得出N个hash值,落到这个byte数组上,如果这N个位置都没有为1,那么就说明这个URL一定不存在这个集合中。

public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions, double fpp);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions, double fpp);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, int expectedInsertions);
public static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions);

Guava提供BloomFilter的静态create方法。

static <T> BloomFilter<T> create(Funnel<? super T> funnel, long expectedInsertions, double fpp, Strategy strategy);
// 参数含义:
// funnel 指定布隆过滤器中存的是什么类型的数据,有:IntegerFunnel,LongFunnel,StringCharsetFunnel。
// expectedInsertions 预期需要存储的数据量
// fpp 误判率,默认是0.03。

最终调用。

BloomFilter里的 byte数组空间大小是有expectedINsertions , fpp 参数决定

static long optimalNumOfBits(long n, double p) {
    if (p == 0) {
        p = Double.MIN_VALUE;
    }
    return (long) (-n * Math.log(p) / (Math.log(2) * Math.log(2)));
}

真正的byte数组 在维护类是 bitArray 。

最后可以通过put 或者 mightContain方法 ,添加元素 和是否包含。

这个算法的特点在于

布隆过滤器 解决的仅仅只是判断该元素一定不存在该集合中。
如果说一定存在其实是有误判的可能。另外因为无法分辨hash碰撞,所以不是很好做删除操作。

使用场景

1、诸如 黑名单的校验
2、URL去重 。这里如果对于安全性要求不是很高的系统。需要设置对应ip及端口映射,使用访问路径定点登录说不定是可以实现的,但是一般系统在这里使用的应该是API 网关zuul之类的。
3、key-value 缓存key校验 。redis 的缓存穿透 中,一个数据如果既不存在redis中,也不存在数据库中,这时候黑客数据大量攻击/访问数据库,造成系统奔溃。这里在redis判断没有该热点数据的时候可以加一个布隆过滤器。对于数据库中真实存在的数据我才处理这个请求。
4、ID校验。面试中很多面试官的实际场景问题也都是ID校验,比如订单系统中查询某个订单ID是否存在。如果不存在就直接返回。

布隆过滤器就聊到这里啦。

最后说一下最近跟一个朋友聊天,他问我写博客真的有用么?
其实博客本身的价值在于你对一个知识点的认识和总结。开始写的时候可能都是认识一些知识。随着写的多了理解的高度就不同了。像现在让我谈HashMap我感觉不只是在大学时候那种浅显的指导key不重复。甚至我很推荐和我一样刚刚毕业的大学生写博客。不止记录技术的学习。对生活和其它各个学科都可以写。

ok,周末愉快!

猜你喜欢

转载自blog.csdn.net/qq_42963930/article/details/106163287