随着互联网的兴起,越来越多的内容被放到互联网中,从而导致海量数据处理受到更多人的重视,尤其是在百度、腾讯等这些涉及海量数据的公司。下面我们简单谈一下关于海量数据处理的一些常用数据结构。包括哈希、bitmap、Bloom filter、堆、mapreduce、trie树。
(1)哈希
对于哈希,相信大家都不会陌生。其基本原理不再说明,哈希的一个关键点是哈希函数的选择,如何使映射结果更加均衡及冲突减少。关于哈希也有许多变种,如一致性哈希(详见http://www.cnblogs.com/dong008259/archive/2011/11/26/2264175.html)。海量数据处理中,哈希可用于快速查找及删除,通常需要总数据量可以放入内存中。
哈希实例:海量日志数据,提取出某日访问百度次数最多的那个IP。
讲解:IP最多为2^32个,为4G,一次放入内存中不行,可以采用分而治之的方法,先Hash(IP)/1024,将IP地址分别映射到1024个小文件中,每个文件4M,再将每个小文件中的IP放入内存中,进行hashmap,统计出出现频率最高的那个IP,最后可以得到1024个出现高频的IP,采用冒泡排序,可以迅速找出频率最高的那个IP.
(2)bitmap
bitmap可谓是非常经典的海量数据处理工具,其本质是用bit数组的某一位表示某一数据,从而一个bit数组可以表示海量数据。关于bitmap详见:http://dong008259.blog.163.com/blog/static/51645417201191083740520/。
bitmap实例:已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
讲解:8位数字最多为99,999,999,则1亿个bit就可以存储,大约为12.5MB内存。依此查询电话号码,若电话号码对应的比特位为0,则置1,若已经为1,则表明前面已出现该号码。遍历文件完毕后,统计所有比特位为1的位数,即为不同号码的个数。
(3)布隆过滤器(Bloom filter)
关于布隆过滤器,在前面的一篇博客中已详细讲过,详见:http://www.cnblogs.com/dong008259/archive/2012/01/04/2311332.html。布隆过滤器的实质是由一个位数组及k个哈希函数组成,将将要映射的元素通过k次哈希映射到位数组的相应位置当中。布隆过滤器可以看做是哈希函数和bitmap的扩展,它允许有一定的错误。且错误率与位数组的个数m、哈希函数个数k及要映射的元素个数m有必然的联系,当hash函数个数k=(ln2)*(m/n)时错误率最小。
布隆过滤器实例:给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。
讲解:50亿条URL,大约有5G条,每条占用64B,则共为320G,远远超过内存的4G。若想把这50亿条URL全部映射到内存中,可以用布隆过滤器处理。4G内存大约为320亿个bit,将50亿条URL若按出错率0.01计算,大约需要650亿个bit,现在为320亿个bit,也是可以接受的。所以在允许有出错率的情况下,将50亿条URL映射到4G内存中,再遍历另一个文件,若发现已经在内存中映射,则表明该URL为共有的。
(4)堆
刚接触堆这个概念,是在数据结构中的内部排序算法中,是一种树形选择排序。在海量数据存储中,堆适合解决求取数据中符合条件的某n个数,如出现频率最大的前n个,堆可以放在内存中进行。
堆实例:有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。
讲解:由于内存只有1M,不能将整个文件全部放入内存中。我们采取分而治之的方法,首先将1G文件中所有的词哈希到2000个文件中,每个文件大约为500K,哈希过程必须保证相同的词映射到同一文件中。再在每个小文件中采用trie树或hash_map统计出现的次数。最后在维护一个容量为100的小顶堆即可。
(5)mapreduce
mapreduce是一种分布式处理,将数据划分到不同的机器上进行处理,最后再对每台机器上的结果进行整合。数据划分,结果规约。
mapreduce实例:上千万或亿数据,统计其中出现次数最多的前N个数据。
讲解:首先可以根据数据值或者把数据hash后的值,将数据按照范围划分到不同的机子,最好可以让数据划分后可以一次读入内存,这样不同的机子负责处理各种的数值范围,实际上就是map。得到结果后,各个机子只需拿出各自的出现次数最多的前N个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是reduce过程。
(6)trie树
trie树是快速查找字符串的一个很有用的工具,是一种树形结构。适用于重复数量比较多的海量数据。详见:http://www.cnblogs.com/dong008259/archive/2011/11/11/2244900.html
trie树实例:1000万个记录(这些查询串的重复度比较高,长度为1-255个字节,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。
讲解:可以建立一棵trie树(3百万个字符串,大约为765M,小于1G),在关键字域存储其串的出现次数,然后用小顶堆求前10个字符串。或者利用Hash将1000万记录哈希到300万的范围内,统计每个记录的频度(用trie或hash_map)。然后用维护一个大小为10的小顶堆遍历这300万的统计结果。