海量数据处理问题之位图和布隆过滤器

从哈希函数出发,谈一谈如何处理海量数据

海量数据处理

给出如下的一个场景:
有40亿个(数据规模足够大,内存大小限制放不下)不重复的无符号整数(>=0)设计,一种算法 or 数据结构满足以下需求。

  • 40亿个数据基本固定
  • 经常进行判断一个数字n在不在集合中

**现在我们先设想如果不是海量数据,我们可以怎么做?**常规解决思路就是查找搜索

  • 遍历查找
  • 排序后二分查找
  • 放入Set中 (快 但是需要额外的空间)
  • 自己实现一种直接地址法的哈希表 (要求就是数据比较连续)

为什么自己实现直接地址法的哈希表要求数据要连续呢?
如果数据规模不大,但是数据不连续,使用直接地址法的哈希表会有下面的一些问题:

在这里插入图片描述

虽然可以解决冲突问题,但是缺点很明显。哈希数组的长度是和数字的最大值有关,而不是集合数字的规模,如果只有两个数字,一个1,一个二十万,缺点显而易见。

如果是海量数据,上述的各种做法就不太好。数据太大也就说,内存放不下,只能放到硬盘里面。内存的数据可以随机访问,但是硬盘的就不可以(磁道寻址的问题)。

位图结构 bitmap

(缩小存储空间)

第一种思路:还是上面的直接地址法的哈希,如果是40亿个数字,还是上面的做法,需要40亿个 int 占用的空间 。解决的思路就是:缩小每个数据占用的空间,进而使得整个集合占用的空间减少。

要表示存在或者不存在,本质上是一个二元信息,理论上,只需要一个bit就足够保存数据了。 40亿 *1bit - > 使得整体的空间占用小,使得可以在内存中解决

由于海量数据的特点导致集合的空间占用太大,减少每个元素的占用的空间,由于只判断在与不再,二元信息所以是1个比特即可。
由于bit数组不存在 ,所以需要自己构造.

在这里插入图片描述

那么具体数据的操作,该如何利用比特位实现:
建立一个byte[] 类型的数组,数组的 元素类型是byte。 byte 类型是一个字节,也就是可以用8个比特位
0 表示不存在 1表示存在
x/8 计算出在哪一个字节 x%8 计算出这个字节的哪一个比特位

在这里插入图片描述
在这里插入图片描述

了解了大概的原理,如何实现呢?

位图使用

首先根据计算 x/8 计算出来 了要对数组的哪一个index进行操作。
那么如何对这个byte[Index] 的 某一个bite 位进行操作呢?
假设 int bit = x %8

添加x
首先定位到byte[index] ,然后更改对应的bite位为1
(注意一个坑点,不可以直接对原来的数据进行移位操作,移位会改变原有数据)

复习一下位操作
在这里插入图片描述

可以借助对1 的移位进行操作 原来的byte[index ] | (1 << bite)
这样就可以仅仅改变bite 这1个比特位
原来是1 不变
原来是0 变为1

在这里插入图片描述

删除x
byte[index ] & ( ~ (1 << bite))
不论原来这个位置是0 还是1 都改为 0 即可
所以按位与0 就可以了。
在这里插入图片描述

判断x是否存在

byte[index ] & (1 << bite)
结果是0 说明不存在,反之,存在
在这里插入图片描述

位图面试题

给定100亿个整数,设计算法找到只出现一次的整数?

存在或者不存在,可以通过位图解决。(二元信息,一个bit存储)
但是找到只出现一次的现在需要

  • 不出现
  • 只出现一次
  • 出现一次以上
    那么就是一个三元的信息,一个bit放不下,那就用两个bit来做。两个bit可以最多保存四元信。所以需要设计一下编码的格式,同时需要使用两个位图。

但是由于找的是原来的那个数字,又由于位图本身是不能存在数字元素的信息,所以需要根据下标在反推会元素,这也就要求不可以有hash冲突

  1. 先确定是几元信息 - 三元信息

  2. 根据几元信息确定需要几位存储 - 2bit存储

  3. 为各种情况进行编码 - 考虑到计算的方便性
    0 0 表示一次都没有出现
    0 1 表示出现一次
    1 1 表示出现一次以上
    1 0 弃用 (因为设计的时候 采用得到 两个位的异或关系,要确保只出现一次的位图异或的结果是1 )

  4. 处理 遍历所有数字,记录数字的出现次数
    然后遍历位图的每一位 找出出现一次的

  5. 还原成对应的数字 (还原数字的时候 只用将上面计算在哪一个byte[index] 的哪一个bite 反过来即可!)

在这里插入图片描述

给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

  1. 确定几元信息
    两个文件 只有可能有四种情况(四元信息)
  • 没有出现在A 也没有出现在B
  • 只出现在A
  • 只出现在B
  • 出现在A和B中
  1. 需要2个比特存储

  2. 编码设计
    在这里插入图片描述

  3. 处理 找到 两个位图中为止按位与是1的即为交集。

  4. 还原成对应的数字

另一种解决思路 用一个位图搞定

可以先把第一个文件哈希放到一个位图里面去,存在设置为1 即可。
然后遍历第二个文件,也哈希,如果遇到了1,就说明找了交集。

位图总结

问题的特点

  • 海量
  • 无符号整数(>=0)
  • 不能重复 (这点很重要!)
  • 只能处理存在与否的问题 不能计算个数

使用场景

  • 只适合在一个集合中判断元素存在与否的问题(exists)
  • 排序 + 去重
  • 求两个集合的交集、并集等
  • 操作系统中磁盘块标记

布隆过滤器 Bloom Filter

特点
检查元素是否在集合中存在并且集合中的数据规模特别大

使用场景
上述位图的限制 只能是数字,不能重复,只能判断是否存在的问题,也有很多的限制。

举例一:新闻
元素:新闻
集合:已经给你推送过的新闻视频等
由于不允许重复,所以,每次需要检查新闻是否被推送过
检查元素是否在集合中存在
显然由于不是数字,无法使用位图的方式

举例二:作弊判定
作弊名单比较大
判断这个玩家是否之前作弊。也就是检查(元素)玩家是否有在(集合)作弊名单中(很多人)存在

布隆过滤器的内部结构

在这里插入图片描述

在这里插入图片描述
是n个哈希函数和位图的结合

  1. 一个元素放入结合中
  • 把元素分别经过n个hash函数的计算,并转化为位图上的合法下标
  • 为所有下标处“位图”位置改为1
  1. 删除一个元素

布隆过滤器无法实现删除的。
因为得到的合法下标的1不仅仅放着当前需要删除元素的信息,还和别的元素有关系

在这里插入图片描述
一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈
希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删
除操作。
缺陷:

  1. 无法确认元素是否真正在布隆过滤器中

  2. 存在计数回绕

  3. 判断一个元素是否在集合中

  • 把元素分别经过n个哈希函数进行计算,并转换为位图上的合法下标
    所有位置有0 ,则表示元素不在集合中
    所有位置都是1,则大概率元素在集合中

布隆过滤器总结

阳性:判定元素在集合中
阴性:判定元素不再集合中
布隆过滤器出现阴性结果,一定是准确的。但是出现阳性结果,则有概率出现假阳性的情况。即一定可以判断元素不在集合中,大概率可判断元素在集合中(概率可以通过增加hash函数的个数,有效的减少)虽有有假阳性,但是假阳性的概率不高,还是有实用性的。(宁可错杀,不可放过)
布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。

多个hash函数的原因:(哈希冲突的必然性)
元素由于不限,必然会产生hash冲突的情况(不同的元素,映射到相同的下标)
在这种情况下,如果hash函数太少,假阳性概率特别高。通过增加hash函数的个数,会使得假阳性概率下降。

布隆过滤器的优点

  1. 增加和删除元素的时间复杂度:O(k),k是哈希函数的个数,(一般比较小)与数据量无关。
  2. 哈希函数之间没有关系,方便硬件进行并行运算
  3. 布隆过滤器本身不需要存储元素,对保密要求比较严格的场合有较大的优势
  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有更大的空间优势
  5. 使用同一组散列函数的布隆过滤器可以进行交、并、补、差运算
  6. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

布隆过滤器的缺点

  1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中(补救方法:再建立一个白
    名单,存储可能会误判的数据)
  2. 不能获取元素本身
  3. 一般情况下不能从布隆过滤器中删除元素
  4. 如果采用计数方式删除,可能会存在计数回绕问题

实际场景举例:
比如有10亿个玩家,然后进行游戏行为判定找到了1万个人作弊(这里面可能会存在着没有作弊却被封号了),然后假阳性的玩家找过来了,那么可以使用更精确的的算法进行玩家作弊判定。(1万个相对来说已经不是海量数据了)

  • 如果该玩家确实作弊 那就是阳性
  • 如果没有作弊 解封补偿玩家

布隆过滤器面试题

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
处理利用上面的位图确定几元信息,还可以使用布隆过滤器

使用hash函数将第一个文件的所有整数映射到1000个文件中,每个文件有1000万个整数,大约40M内存,
内存可以放下
把1000个文件记为 a1,a2,a3…a1000
用同样的hash函数映射第二个文件到1000个文件中,这1000个文件记为b1,b2,b3…b1000,
由于使用的是相同的hash函数,所以两个文件中一样的数字会被分配到文件下标一致的文件中,分别对a1和b1求交集,a2和b2求交集,ai和bi求交集,最后将结果汇总,即为两个文件的交集

哈希切割

给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?

  • 海量数据
  • 找出现次数,不是存在与否问题

如果把数据进行文件的拆分,拆分为多个不同的文件,然后把每一个文件里面的ip出现次数最多的拿出来,再次进行一个比较。
但是这样的做法是不可行的,无法保证相同的ip出现在相同的文件中进行计数
在这里插入图片描述
那么又如何做到相同的ip分到相同的组。-- 哈希
那么海量数据处理的第二种思路,分组处理,其中分组的方式采用哈希分组(哈希函数可以灵活设计)
只需要保证ip相同的在一组,没有必要说一组里面只出现一个ip,是可以出现哈希冲突的。
经过哈希函数处理之后,文件可能会被划分长下面的样子
在这里插入图片描述

与上题条件相同,如何找到top K的IP?
5TB的访问日志,如何找到访问次数最多的10个ip?
方案一:

  1. 统计每一个ip的出现次数,得到如下的集合:
    [ip - 次数 , ip- 次数,…]
    依次处理,内存够大放内存处理,内存不够大,就去读取文件处理。并没有海量数据的限制。
    处理过程中数据量的大小和ip有多少个不同的有关系。
  2. 直接创建一个小堆(堆中可以保存10个元素)。堆中存放的元素是 ip - 次数,评判标准是次数。
  3. 遍历第一步得到的集合,然后依次和小堆的堆顶元素比较。如果比对顶元素小,那么就跳过。否则就替换堆顶,然后向下调整堆。
  4. 最后堆保留的就是出现次数最多的K个元素。

在这里插入图片描述

(为什么不可以建立大根堆)
每一个划分的子文件都用topK建立一个大根堆,然后将所用的子文件放在一起

方案二: 使用hash分组的方式,分为多个文件
在这里插入图片描述
划分文件n个,每个文件都得出topK,将n个topK进行再次的topK得出结果即可。

方案一和方案二对比:
第一种只能用线性的方式去解决,速度不快。
但是第二中方案由于划分文件了,可以并行去各自处理,最后将结果在一起合并找出。

总结

总结海量数据处理的两类思路办法

1、 压缩信息的办法:位图、布隆过滤器
2、分文件处理,一般要采用哈希分组的方式,保证相同的数据分到同一组(这种思想可以作为通用的办法)

海量数据处理的面试题,一般通过 画图+口头描述+部分伪代码的方式,把过程讲解清楚即可。重点部分突出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值