海量数据的常见处理算法

海量数据的处理算法

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

解决办法:

(1)针对时间,可以采用巧妙的算法搭配合适的数据结构,如Hash/bitmap/堆/倒排索引/trie树;

(2)针对空间,大而化小:分而治之/hash映射,把规模大化为规模小的,各个击破。
在这里插入图片描述


一、海量数据中的最值问题:

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

思路:分而治之/hash映射 + hash统计 + 堆/快速/归并排序,就是先映射,后统计,最后排序。

​ a. 分而治之/hash映射:针对数据太大,内存受限,只能是:把大文件化成(取模映射)小文件,即16字方针:大而化小,各个击破,缩小规模,逐个解决

​ b. hash统计:当大文件转化了小文件,那么我们便可以采用常规的hash_map(ip,value)来进行频率统计。

​ c. 堆/快速排序:统计完了之后,便进行排序(可采取堆排序),获取每个小文件的最值。再归并每个小文件的最值进而获取最大的值。

实现: 首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如模1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map对那1000个文件中的所有IP进行频率统计,然后依次找出各个文件中频率最大的那个IP)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。


二、海量数据的top K问题。

海量数据存储于多个文件,任何一条数据都可能存在于任何一个文件当中,现需要筛选出现的次数最多的k条数据。 一般思路:
1)、 依次遍历这些文件,通过hash映射,将每个文件的每条数据映射到新构造的多个小文件中(设生成了n个小文件);
2)、依次统计每个小文件中出现次数最多的k条数据,构成hash表,hash表中每个键值对的形式为 dataItem: count;
3)、利用堆排序,依次遍历这些hash表,在n∗k条数据中,找出count值最大的k个;

1、如何在1亿个数中找出最大的100个数
a. 如果这1亿个书里面有很多重复的数,先通过Hash法,把这1亿个数字去重复,这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间。
b. 将1亿个数据分成100份,每份100万个数据,
c. 统计每份文件中数据出现的kv值,找到每份数据中最大的100个。
d. 堆/快速排序归并获取最大的100个数

2、寻找热门查询,300万个查询字符串中统计最热门的10个查询。
思路: 我们知道,数据大则划为小的,如一亿个数求Top10,可先%1000将ip分到1000个小文件中去,并保证一种ip只出现在一个文件中,再对每个小文件中的ip进行hashmap计数统计并按数量排序,最后归并或者最小堆依次处理每个小文件的top10以得到最后的结果。但如果数据规模比较小,能一次性装入内存呢? 虽然有一千万个Query,但是由于重复度比较高,因此事实上只有300万的Query,每个Query255Byte,因此我们可以考虑把他们都放进内存中去(300万个字符串假设没有重复,都是最大长度,那么最多占用内存3M*1K/4=0.75G。所以可以将所有字符串都存放在内存中进行处理),而现在只是需要一个合适的数据结构,在这里,HashTable绝对是我们优先的选择。所以我们放弃分而治之/hash映射的步骤,直接上hash统计,然后排序。So,针对此类典型的TOP K问题,采取的对策往往是:hashmap + 堆。

a. hash_map统计: 先对这批海量数据预处理。具体方法是:维护一个Key为Query字串,Value为该Query出现次数的HashTable,即hash_map(Query,Value),每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(N)的时间复杂度内用Hash表完成了统计;
b. 堆排序: 第二步、借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。即借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。所以,我们最终的时间复杂度是:O(N) + N’ * O(logK),(N为1000万,N’为300万)。

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

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

实现:
a. 直接在每台电脑上堆排序求出TOP10,可以采用包含10个元素的堆完成(TOP10小,用最大堆,TOP10大,用最小堆,比如求TOP10大,我们首先取前10个元素调整成最小堆,如果发现,然后扫描后面的数据,并与堆顶元素比较,如果比堆顶元素大,那么用该元素替换堆顶,然后再调整为最小堆。最后堆中的元素就是TOP10大)。

b. 求出每台电脑上的TOP10后,然后把这100台电脑上的TOP10组合起来,共1000个数据,

c. 再利用利用堆排序的方法求出TOP10就可以了。

重复数据的思路: 如果同一个元素重复出现在不同的电脑中呢,这个时候,你可以有两种方法:遍历一遍所有数据,重新hash取摸,如此使得同一个元素只出现在单独的一台电脑中,然后采用上面所说的方法,统计每台电脑中各个元素的出现次数找出TOP10,继而组合100台电脑上的TOP10,找出最终的TOP10。


三、海量数据的某个数据是否存在或重复存在的问题。

1、给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中?
思路: 一个bit位代表一个unsigned int值。读入40亿个数,设置相应的bit位。由于2^32=42.9+亿,那么2^32bit才能存下40亿个数,也就需要2^32=4Gb=0.5GB=512M内存。读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在。

2、在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数。

思路:采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。


四、海量数据中找出第K大的数。(如中位数)

1、现在 有10亿个int型的数字(假设int 型占4B),以及一台可用内存为1GB的机器,如何找出这10亿个数字的中位数?

思路:采用基于二进制位比较和快速排序算法中的“分割思想”来寻找中位数。

具体实现: 假设10亿个数字保存在一个大文件中,依次读一部分文件到内存(不超过内存的限制:1GB),将每个数字用二进制表示,比较二进制的最高位(第32位),如果数字的最高位为0,则将这个数字写入 file_0文件中;如果最高位为 1,则将该数字写入file_1文件中。【这里的最高位类似于快速排序中的枢轴元素】。从而将10亿个数字分成了两个文件(几乎是二分的),假设 file_0文件中有 6亿 个数字,file_1文件中有 4亿 个数字。那么中位数就在 file_0 文件中,并且是 file_0 文件中所有数字排序之后的第 1亿 个数字。

为什么呢?因为10亿个数字的中位数是10亿个数排序之后的第5亿个数。现在file_0有6亿个数,file_1有4亿个数,file_0中的数都比file_1中的数要大(最高位为符号位,file_1中的数都是负数,file_0中的数都是正数,也即这里一共只有4亿个负数,排序之后的第5亿个数一定是正数,那么排序之后的第5亿个数一定位于file_0中)。除去4亿个负数,中位数就是6亿个正数从小到大排序之后 的第 1 亿个数。现在,我们只需要处理 file_0 文件了(不需要再考虑file_1文件)。对于 file_0 文件,同样采取上面的措施处理:将file_0文件依次读一部分到内存(不超内存限制:1GB),将每个数字用二进制表示,比较二进制的 次高位(第31位),如果数字的次高位为0,写入file_0_0文件中;如果次高位为1,写入file_0_1文件 中。

现假设 file_0_0文件中有3亿个数字,file_0_1中也有3亿个数字,则中位数就是:file_0_0文件中的数字从小到大排序之后的第1亿个数字。抛弃file_0_1文件,继续对 file_0_0文件 根据 次次高位(第30位) 划分,假设此次划分的两个文件为:file_0_0_0中有0.5亿个数字,file_0_0_1中有2.5亿个数字,那么中位数就是 file_0_0_1文件中的所有数字排序之后的 第 0.5亿 个数。

按照上述思路,直到划分的文件可直接加载进内存时(比如划分的文件中只有5KW个数字了),就可以直接对数字进行快速排序,找出中位数了。

总结: 上面的海量数据寻找中位数,其实就是利用了“分割”思想,每次将 问题空间 大约分解成原问题空间的一半左右。(划分成两个文件,直接丢弃其中一个文件),故总的复杂度可视为O(logN) N=10亿。


五、多个海量文件之间对比查重

A和B两个大文件,每个文件都存储着海量数据,要求给出A,B中重复的数据。 一般思路:

1)、遍历A中的所有数据,通过hash映射将他们分布存储在n个小文件中,记为{a1,a2,…,an};
2)、遍历B中的所有数据,通过hash映射将他们分布存储在n个小文件中,记为{b1,b2,…,bn};
3)、根据hash函数性质可知,A和B中的相同数据一定被映射到序号相同的小文件,所以我们依次比较{ai,bi}即可;
4)、如果问题更进一步,要求返回重复次数最多的k条数据,则可以将对比小文件找到的数据存入hash表,键为数据,值为该数据出现的次数。再用大小为k的堆,排序找出即可。

1、给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,让你找出a、b文件共同的url?

思路: 假如每个url大小为10bytes,那么可以估计每个文件的大小为50G×64=320G,远远大于内存限制的4G,所以不可能将其完全加载到内存中处理,可以采用分治的思想来解决。

Step1:遍历文件a,对每个url求取hash(url)%1000,然后根据所取得的值将url分别存储到1000个小文件(记为a0,a1,…,a999,每个小文件约300M);

Step2:遍历文件b,采取和a相同的方式将url分别存储到1000个小文件(记为b0,b1,…,b999);

巧妙之处:这样处理后,所有可能相同的url都被保存在对应的小文件(a0vsb0,a1vsb1,…,a999vsb999)中,不对应的小文件不可能有相同的url。然后我们只要求出这个1000对小文件中相同的url即可。

Step3:求每对小文件ai和bi中相同的url时,可以把ai的url存储到hash_set/hash_map中。然后遍历bi的每个url,看其是否在刚才构建的hash_set中,如果是,那么就是共同的url,存到文件里面就可以了。

猜你喜欢

转载自blog.csdn.net/u014618114/article/details/107621559