位图

位图

通过位图来实现URL去重功能,避免重复爬去相同的网页。

我们要处理的对象是URL,需要支持的操作就两个,添加和查询URL。除了功能方面,我们还要求这两个操作执行效率要高,当处理上亿URL时,内存会非常严重,所以在存储效率上也要高效。

满足条件的数据结构有:

  1. 散列表
  2. 红黑树
  3. 跳表

这些动态数据结构,都支持快速添加和查找数据,但是对内存消耗方面有些大。

比如散列表:当爬去10亿URL,为了去重,将10亿URL存储在散列表中。假设一个URL平均长度为64字节,大约需要60GB内存空间。散列表必须要维持较小装载因子,才能保证不会出现过多散列冲突,导致内存性能下降。而且用链表法解决冲突,还会存储链表。所以将10亿URL构成散列表需要内存可能超过100GB。

优化方向

散列表:中添加、查找数据的时间复杂度已经是O(1)。大O表示法,会忽略掉常数、系数和低阶。

  • 链表的节点在内存中是不连续存储的,不能一下加载到CPU缓存中,没办法很好利用CPU告诉缓存,所以性能方面会打折扣。
  • 链表的数据都是URL,平均长度为64字节,并且待判重的URL还要进行字符串匹配。

针对内存消耗方面的优化,我们可以使用 布隆过滤器(Bloom Filter)

位图

布隆过滤器本身就是基于位图的,是对位图的一种改进。

示例:我们有1千万个整数,整数范围在1到1亿之间,我们怎么快速查找某个整数是否在1千万个整数中呢?

解决:我们除了用散列表的解决,还可以使用一种“特殊”的散列表,就是 位图

方式:申请一个1亿、数据类型为布尔类型的数组,将这1千万个整数作为数组下标,将对应的数组值设置成True,比如整数5对应下标为5的数组值设置为true。

布尔类型大小是1字节,实际上我们只需要用一个二进制位(bit)来表示就可以。位图通过下标来定位数据,所以访问效率非常高,并且数字用一个二进制位来表示,所需要的内存空间非常节省。

当使用散列表来存储1千万数据,数据是32位的整形,也就要4个字节存储,总共需要40MB存储空间。使用位图的话只需要1亿个二进制位,也就是12MB作用的存储空间。

但是数字范围在1到100亿之间的1千万个数,那么位图大小就是100亿个二进制位,也就是1200MB消耗内存空间,不降反增。

接下来就需要布隆过滤器。

布隆过滤器

示例:1千万数据,数据范围是1到10亿,布隆过滤器做法是,仍然使用1亿个二进制大小的位图,然后通过哈希函数,对数字进行处理,让它落在1到1亿范围内。设计哈希函数 f ( x ) = x f(x) = x % n f(x)=x x表示数字,n表示位图大小(1亿),也就是数字跟位图的大小进行取模求余。

问题:1亿零1和1得到的结果都是1,这样就无法区分存储了。

解决:我们设计复杂、随机点的哈希函数,还可以用多个哈希函数一块来定位一个数据,来降低冲突概率。

布隆过滤器就是通过K个不同的哈希值,将K个数字作为位图的下标,来表示一个数字的存在。

当我们查询某个数字存在时,同样用K个哈希函数,对这个数字求哈希值,得到K个哈希值,看对应为图中的位置是否都是True,有任意一个不是True,说明该数字不存在。

弊端:会产生误判,只会对存在的情况会有误判,如果某个数字经过布隆过滤器判断不存在就是真的不存在,不会发生误判。不过我们调整哈希函数的个数、位图大小和存储数字的个数之间的比例,这种误判概率就会很低。

URL去重

假设去重网页有10亿,我们可以用100亿二进制位,换成字节是1.2GB,用散列表需要100GB空间。

效率:

  • 用多个哈希函数对同一个URL进行处理,只需从内存中读取一次URL,进行多次哈希计算,理论上这个操作是CPU密集型。
  • 散列表处理,需要读取散列表冲突链的多个网页URL分布进行字符串匹配,涉及多次的内存数据读取,所以是内存密集型的。
  • CPU密集型比内存密集型访问更快。

优化

我们需要支持自动扩容的功能,当数据个数与位图大小的比例超过某个阈值,就重新申请一个新的位图。新来的数字就会被放置在新的位图,但是在判断数据是否存在布隆过滤器中,就需要查看多个位图,执行效率就会降低一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值