海量数据处理面试题

何谓海量数据处理?

所谓海量数据处理,无非就是基于海量数据上的存储、处理、操作。何谓海量,就是数据量太大,所以导致要么是无法在较短时间内迅速解决,要么是数据太大,导致无法一次性装入内存。

那解决办法呢?针对时间,我们可以采用巧妙的算法搭配合适的数据结构,如布隆过滤器/Hash/bit-map/堆/数据库或倒排索引/trie树,针对空间,无非就一个办法:大而化小,分而治之(hash映射),你不是说规模太大嘛,那简单啊,就把规模大化为规模小的,各个击破不就完了嘛。

海量数据处理主要方法:

  1. 分而治之-hash映射 + hash统计 + 堆排序/外排序
  2. 布隆过滤器
  3. 布隆过滤器+分层

哈希函数的性质:

  1. 典型的哈希函数都有无限的输入值域。
  2. 当给哈希函数传入相同的输入值时,返回值一样,即哈希值一样。
  3. 当给哈希函数传入不同的值时,返回值可能一样,也可能不一样。
  4. 很多不同的输入之所得到的返回值会均匀的分布在S上,S为输出域–范围固定。这条性质是评判一个哈希函数优劣的关键。

堆排序的注意点:

  • 要找出最大的TopK,要建立最小堆,堆顶元素为最小的,先拿K个数建立最小堆,接下来每拿一个数都和堆顶元素比较,如果比堆顶元素大,则代替堆顶元素,然后对该堆进行凋整,使之成为最小堆。重复操作直到遍历所有数据。
  • 要找出最小的TopK,要建立最大堆,每次与最大堆的堆顶元素比较,小于堆顶元素则代替堆顶元素,然后进行调整。重复操作直到遍历所有数据。

1. 分而治之-hash映射 + hash统计 + 堆排序/外排序

1、海量日志数据,提取出某日访问百度次数最多的那个IP。

既然是海量数据处理,那么可想而知,给我们的数据那就一定是海量的。针对这个数据的海量,我们如何着手呢?对的,无非就是分而治之/hash映射 + hash统计 + 堆/快速/归并排序,说白了,就是先映射,而后统计,最后排序:

  • 分而治之/hash映射:针对数据太大,内存受限,只能是:把大文件化成(取模映射)小文件,即16字方针:大而化小,各个击破,缩小规模,逐个解决。
  • hashmap统计:当大文件转化了小文件,那么我们便可以采用常规的hashmap(ip,value)来进行频率统计。
  • 堆/快速排序:统计完了之后,便进行排序(可采取堆排序),得到次数最多的IP。

2、有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

  • 分而治之/hash映射:顺序读文件中,对于每个词x,取hash(x)%5000,然后按照该值存到5000个小文件(记为x0,x1,…x4999)中。这样每个文件大概是200k左右。如果其中的有的文件超过了1M大小,还可以按照类似的方法继续往下分,直到分解得到的小文件的大小都不超过1M。
  • hashmap统计:对每个小文件,采用hashmap等统计每个文件中出现的词以及相应的频率。
  • 堆/归并排序:取出出现频率最大的100个词(可以用含100个结点的最小堆)后,再把100个词及相应的频率存入文件,这样又得到了5000个文件。最后就是把这5000个文件进行归并(类似于归并排序)的过程了。

3、海量数据分布在100台电脑中,想个办法高效统计出这批数据的TOP10。

如果每个数据元素只出现一次,而且只出现在某一台机器中,那么可以采取以下步骤统计出现次数TOP10的数据元素:

  • 堆排序:在每台电脑上求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆,比如求TOP10大,我们首先取前10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元素就是TOP10大)。
  • 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,再利用上面类似的方法求出TOP10就可以了。

但如果同一个元素重复出现在不同的电脑中呢?这个时候,你可以有两种方法:

  • 遍历一遍所有数据,重新hash取摸,如此使得同一个元素只出现在单独的一台电脑中,然后采用上面所说的方法,统计每台电脑中各个元素的出现次数找出TOP10,继而组合100台电脑上的TOP10,找出最终的TOP10。
  • 暴力求解:直接统计统计每台电脑中各个元素的出现次数,然后把同一个元素在不同机器中的出现次数相加,最终从所有数据中找出TOP10。

2. 多层划分

  • 适用范围:第k大,中位数,不重复或重复的数字
  • 基本原理及要点:因为元素范围很大,不能利用直接寻址表,所以通过多次划分,逐步确定范围,然后最后在一个可以接受的范围内进行。

1、32位无符号整数的范围是0-429467293,现在有一个正好包含40个亿无符号整数的文件,所以在整个范围内中必然有没有出现过的数。可以使用最多1GB的内存怎么找到所有没有出现过的数?

  • 首先如果用哈希表来保存出现过的数据,在最差的情况下需要40亿4B(记录出现次数) 2(键和值)= 32GB。需要32GB的空间,不符合要求。
  • 哈希表需要占很多空间,使用bitmap的方式来表示数出现的情况。
  • 用一个长度为429467293的bit型数组bitArr,bitArr上的每个位置只可以表示0或1两种状态。所以该数组占控件500MB。
  • 遍历这40亿个无符号数,假如遇到7000,就把bitArr[7000]置为1。
  • 遍历完之后,再次遍历bitArr,哪个位置上的值没有被设置为1,哪个数就不在40个亿数中。

2、问题1的进阶问题,内存限制为10MB,但是只用找到一个没有出现过的数即可。

  • 考虑到只有10M内存,所以将0-429467293范围分成64个小区间,每个区间是67108864个数。
  • 第一次遍历时,先申请长度为64的整型数组countArr,用来记录统计区间i 上的数有多少。遍历所有的数,根据当前的数来决定让哪一个区间的计数增加。遍历完后,至少有一个区间的数量小于67108864。此时使用的内存就是64*4B。
  • 对于找到的区间进行第二次遍历:
    • 申请长度为67108864的bitmap,这占用大概8MB的空间,记为bitArr,
    • 在遍历40亿个数,此时遍历只关注落在该区间a上的数,其他区间上的数忽略。
    • 如果该数num在该区间a上,则将bit[num - 67108864 * a] 置为1,就只做这个区间上的数的映射。
    • 在bitArr上必然有存在没被设置为1的位置。

总结进阶的解法:

  • 根据10MB的内存限制,无额定统计区间的大小,就是第二次遍历时bitArr大小。
  • 利用区间计数的方式,找到那个计数不足的区间,这个区间上可定有没有出现的数。
  • 对这个区间上的数做bitmap映射,在遍历bitmap,找到一个没出现过的数即可。

3. 布隆过滤器

  • 适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集,网页黑名单系统、垃圾邮件过滤系统、爬虫的网址重判系统等。且系统容忍一定程度的失误率,但是对空间要求比较严格。
  • 优势在于:使用很少的控件可以将准确率做到很高。判断一个元素是否在集合中。

1、不安全网页的黑名单包含100亿个黑名单网页,每个网页的URL最多占64B。现在想要实现一种网页过滤系统,可以根据网页的URL判断该网页是否在黑名单上,请设计该系统。该系统允许有万分之一的判断失误率,使用的额外空间不能超过30GB。

  • 讲解:假设有一个长度为m的bit类型数组,有k个哈希函数,这些函数的输出域都大于等于m,并且这些哈希函数都足够优秀,且彼此独立。那么对同一个输入对象,经过k个哈希函数算出来的结果也是独立的,可能相同,也可能不同,但彼此独立。对算出来的每一个结果都对m取余,然后在bitArr上把相应的位置1。
  • 检查阶段,把对象通过k个哈希函数算出k个值,然后把k个值取余,得到的值与bitArr对比,如果对应位不都为1,则一定不在。如果都为1,说明该对象在这个集合里,但有可能误判。有可能由于bitmap过小,输入对象过多,导致bitmap大多数位置为1,那么在检查是有可能该对象不是输入对象,而错误的认为为输入对象。
  • bitmap的大小m相比于输入对象的个数n过小,失误率会增大。根据输入对象个数n和失误率p求bitmap的大小m。

    m=nInpIn22

    m = 19.19n,向上取整为20n,即需要2000亿个bit,就是25GB。

  • 哈希函数的个数k:

    k=mIn2n

    即k = 14个。

  • 然后用25GB的bitmap在单独实现14个哈希函数,布隆过滤器真实的失误率为:
    k=1enkmk

    根据这个公式算出来失误率为0.006%,比0.01%更低的失误率。

你可能感兴趣的:(数据结构,算法,面试题,海量数据处理)