位图与布隆过滤器的使用

一、为什么要使用位图

我们先来看一个问题,假设我们有1千万个不同的整数需要存储,每个整数的大小范围是1到1亿。然后,给定任意一个整数X,我们需要判断X是否在刚才的1千万个整数内。这个问题该如何处理呢?

常规的做法肯定就是先考虑如何存储这1千万个整数,在Java中,int类型是4个字节,可以表示的范围区间是-2147483648~2147483647,所以每个整数都用int来表示是可行的。那么1千万个整数需要占用多少内存空间呢?两者相乘就是了,应该是4000万字节,也就是40MB。

而且如果我们存放的整数是是一亿个,每个整数大小范围是1到100亿怎么办?占用的内存空间将会是非常巨大的,是普通的计算机或者手机不能接受的。

所以我们需要使用位图这种数据结构来大大节省内存的使用量。

二、什么是位图

我们知道Byte表示字节,一个字节等于8bit,这里的bit就和位图有关系。在上面的例子中,我们不是要存放1千万个整数嘛,那就申请一个具有1千万个bit的数组,用每个bit(二进制位)来表示一个整数,当前bit为0表示不存在这个整数,为1表示该整数就存在。

虽然数量是1千万个,但是每个数的范围是1到1亿,所以我们需要1亿个bit,换算下来,就是12.5MB,和刚才的40MB相比,节省的不是一点点。

三、布隆过滤器

位图也不是万能的,倘若我们需要存放的1千万个整数的范围是1到100亿怎么办,按道理说,我们申请一个包含100亿个bit的数组就可以了,也是需要1.25GB,太大了,这也是很多电脑和手机承受不起的,那怎么办呢?

布隆过滤器就是让我们有办法在原来12.5MB的内存空间下,使用1亿个bit来存放原本需要100亿个bit才能解决的问题。我们的核心思想就是使用hash函数,把1到100亿大小的数值映射到1到1亿个bit内。

但是这肯定会造成冲突的,鸽巢理论就告诉过我们这一点了。那我们就使用多个不同的hash函数,来把同一个值映射到1到1亿bit中的不同位置上,如此,我们使用多个bit的值来表示一个数值是否存在。对于两个不同的数值来说,经过同一个hash处理得到的值可能会冲突,但是经过多个hash处理得到的多个值就不太可能全部冲突的。

布隆过滤器的工作原理

当我们将某个整数放到内存中时,把它所有hash出来的bit都置为1;当我们需要判断给定的整数X是否已经存在时,就去读取所有的hash出来的bit值,只要有一个不是1,那么就说明这个值不存在;

但是如果所有hash出来的bit值都是1,也不能一定说明这个值X就是已经存在的,布隆过滤器会对结果为存在的情况发生误判。

布隆过滤器发生误判的场景示意

当我们加入的数据越来越多,位图中为0的bit越来越少的时候,布隆过滤器发生误判的概率就会越来越高了。假设我们能提前知道总共有多少数据和数据的范围,我们可以通过调整hash函数的个数、位图的大小来减小误判发生的概率;假设我们并不能提前知道这些,那我们的位图就需要支持自动扩容,当为0的bit小于某个阈值的时候,我们就应该触发扩容位图的操作。

布隆过滤器在对某些可以容忍小概率误判的业务场景非常有效。

四、位图的常见使用场景

4.1 搜索引擎爬虫网页去重

4.2 大型网站每天UV数量统计

你可能感兴趣的:(位图与布隆过滤器的使用)