何谓海量数据处理?
所谓海量数据处理,无非就是基于海量数据上的存储、处理、操作。何谓海量,就是数据量太大,所以导致要么是无法在较短时间内迅速解决,要么是数据太大,导致无法一次性装入内存。
那解决办法呢?针对时间,我们可以采用巧妙的算法搭配合适的数据结构,如布隆过滤器/Hash/bit-map/堆/数据库或倒排索引/trie树,针对空间,无非就一个办法:大而化小,分而治之(hash映射),你不是说规模太大嘛,那简单啊,就把规模大化为规模小的,各个击破不就完了嘛。
海量数据处理主要方法:
- 分而治之-hash映射 + hash统计 + 堆排序/外排序
- 布隆过滤器
- 布隆过滤器+分层
哈希函数的性质:
- 典型的哈希函数都有无限的输入值域。
- 当给哈希函数传入相同的输入值时,返回值一样,即哈希值一样。
- 当给哈希函数传入不同的值时,返回值可能一样,也可能不一样。
- 很多不同的输入之所得到的返回值会均匀的分布在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=−n∗Inp(In2)2
m = 19.19n,向上取整为20n,即需要2000亿个bit,就是25GB。哈希函数的个数k:
k=m∗In2n
即k = 14个。- 然后用25GB的bitmap在单独实现14个哈希函数,布隆过滤器真实的失误率为:
k=(1−e−nkm)k
根据这个公式算出来失误率为0.006%,比0.01%更低的失误率。
来源:CSDN
作者:夏倩倩
链接:https://blog.csdn.net/chun0801/article/details/51850414