海量数据处理方法整理记录 – 黄青石 – 博客园

随着现在数据量的不断增加,很多大数量的问题随之而来,就得需要我们想办法解决,我找了一些问题并首先思考,然后找到方法,在这里记录一下,未来有需要的同学可以拿走去用。 1. 在海量日志数据里,提取某天访问

来源: 海量数据处理方法整理记录 – 黄青石 – 博客园

随着现在数据量的不断增加,很多大数量的问题随之而来,就得需要我们想办法解决,我找了一些问题并首先思考,然后找到方法,在这里记录一下,未来有需要的同学可以拿走去用。

1. 在海量日志数据里,提取某天访问量最多的IP。

一般处理海量的思路都是分治处理,就是现将数据进行拆分,然后进行处理,排序等。这个例子也不例外,IPV4的地址一共32位,最大值为2^32也就是总数大约4G左右,如果放到内存里边,以目前的内存容量也是可以处理的,但是咱们可以为自己设置一些条件,比如目前没有那么多内存。

a) 首先分治,将这个文件按照IP的HASH分成1024份(如果想要均匀的分的算法需要使用一致性Hash算法),这样每个文件大约4M左右并且存放到磁盘上去。

b) 构建一个需要以IP为Key,出现次数为Value的TreeMap。读取每个文件,将IP和出现次数放入有序的TreeMap。

c) 这样就可以得到出现次数最多的IP,前N个出现次数多的IP都可以获取到了。

这种问题一般是TOP K的问题,思路都可以按照这样的思路去解决。当然这种场景比较合适的就是Map Reduce莫属了。另外,关于TOP K的这种排序的话可以采用最小堆排序(即根节点是最小的),它的时间复杂度为n*mlogm,n即为一共多少数据,m为取出前m个数据。关于这种结构不知道的同学可以进行谷歌搜索。分治的作用就是为了减少使用系统的资源,比如系统内容。

2. 上个问题是统计重复出现的个数,那么如何统计不重复的个数。比如:有个电话本,里边记录的电话号码都是8位数字,统计电话本里边有多少电话号码?这个里边肯定也是有一些局限的,比如内存限制。再比如再2.5亿整数中找到不重复的整数的个数,当然,内存中不能够存储着2.5亿数据。这种解决的思路一般是位图算法(bitMap)解决。

以电话号码为例:

a)电话号码是8位数字,也就是出现的数字应该为11111111-99999999,总数为99999999,咱们采用位图法(因为最省内存)。

b)一个bit位代表一个数字,那么这些数字共需要99999999个bit,占用内存为 99999999/8/1024/1024约等于11.92M,即如果这个数字所在的位有数据,那么这个bit位就设置为1,否则设置为0。

这样只需要12M的内存就可以统计这些数据了。当然2.5亿整数同理,在内存中所有整数的个数为2^32,一个数对应一个bit,大概需要512M内存就可以了,如果给的内存还不够的话,则需要再次进行拆分。

3. 还有一些与上边类似的,但是不太相同的,因为有重复的数(1、2、2、3、3、4,排好序的数并且偶数个的话,中位数是[2+3]/2=2.5 奇数个的话正好是中间的),比如在5亿int数中找到中位数。这个问题的解决思路其实采用双层桶划分思路。注意一个int占4个Byte,整数的最大位数为32位,那么我们将每个数转换为二进制,然后截取前多少位,要看内存大小。解决思路:

a) 把整数转为二进制数,然后截取前5位,那么总共分出2^5=32个区间,如果分出文件来共分出32个文件,如果内存不够的话,那么再继续截取(比如16位,这里举例)。比如:file_00000, file_00001等。

b) 如果截取完了,所有文件一共32个文件,因为都是二进制,所以文件是按照有序排好的。统计每个文件的个数,然后计算中位数所在的文件里。

c) 如果文件还是比较大,假设文件在最后一个文件,即前边2.5亿,最后一个文件2.5亿,文件名字为file_11111,那么再继续按照上边的方法继续拆分(比如再5位 文件名:file_11111_00000 等),知道内存中可以装下整个文件。

d) 可以装下整个文件下的话再进行排序,排好序之后,找到中间的数就是中位数。

4. 两个文件,各存放50亿条URL,每个URL占64字节。内存限制是4G,找出两个文件中相同的URL。这个问题有一个内存限制,那么肯定需要分治法。

方法一(分治+hash+hashset):

a) 50亿个64Byte= 5G*64Byte = 320G,内存4个G,肯定是不可以的。那么咱们将每个URL进行hash,然后放到1024个文件中,也就是每个文件为320G/1024=320M左右。以hash值作为文件名,第一个文件hash出来的文件命名为(hash[URL]%1024)a1…..a1024,第二个文件hash出来的文件命名为b1…..b1024。

b)1024个文件生成了,那么相同的URL肯定在hash命名文件的后缀中,比如a1 vs b1,这样依次读取文件的内容放入到hashset中,如果存在的话记录并且追加放到文件中。

c)  最后文件中就是所有URL即为相同的URL。

方法二(Bloom Filter布隆过滤器)

a) 先说一下布隆过滤器,主要将需要内容进行hash,然后对应到相应的bit上,即Bit Map位图法,但是这个里边有一个问题就是hash会碰撞,即不同的结果可能会hash成相同的值,这样就会出错。如果可以接受错误率,当然错误率较低,那么可以采用这种方式。4G内存=2^32 * 8 约等于 40亿Byte * 8 大约等于340亿。先遍历第一个文件,然后再遍历第二个,这样会错误率。

5. 有40亿个不重复的unsigned int的整数,没排过序,现在给一个数,如何快速判断这个数是否在这40亿个数当中。这个如果直接放到内存里边的话得需要2^32*4Byte(int 4Byte) = 4G *4 = 16G. 显然内存比较大了。

a) 这个也采用位图法,所需要的内存为  2*32Byte / 8 = 500M 内存,所以仅仅需要500M内存就可以放下这些数字了,然后查找就可以了。

6. 给定一个文件,里面最多含有n个不重复的正整数(也就是说可能含有少于n个不重复正整数),且其中每个数都小于等于n,n=10^7。输出:得到按从小到大升序排列的包含所有输入的整数的列表。条件:最多有大约1MB的内存空间可用,但磁盘空间足够。且要求运行时间在5分钟以下,10秒为最佳结果。

如果采用位图法的话需要为10^7 / 8 /1024/1024 大约等于1.19M,大于题目的1M,显然位图法不太合适,那么咱们考虑一下多路归并排序。

a)  首先将这个文件分批次读取拆分,比如一次读取256K,然后进行memory sort 在内存排序,写到文件中。假如文件大小是10M的大小,则需要循环40次,写入40个文件当中。

b)  然后将文件进行merge sort合并排序,创建一个数组40个长度,依次读取最小的文件,然后找到数组中最小的写入到文件当中,然后继续读取文件并且继续排序,将最小的再次写入文件即可。

6. 有10个文件,每个文件1G,每个文件的每一行都存放的是用户的搜索的关键字,每个文件的搜索的关键字都可能重复。找出热度高的前1000个搜索关键字。(提示分治+hash+trie树+最小堆)

看到这种问题的话,首先得考虑是否机器资源足够使用,如果足够使用的话,就直接加入内存,但是如果不够的话需要考虑分治。解决思路。

a) 将每个文件按关键字进行hash,然后拆分成100个文件,然后每个文件大概100M左右。(分治+hash)。

b) 读取每个小文件,并且将读取的关键字形成Trie树字典树,这样会达到去重的效果。Trie树的插入和查询复杂度是O(k), k为最长字符串的长度。然后建立长度为1000的小根堆,将遍历每个关键字的出现的次数放到小根堆里。

c) 以上一遍就可以得出第一个1G文件的结果,然后按照相同的原理继续以上步骤。

 

总结一下:

如果是大量数据不重复的,而且需要内存占用比较少的需要找出出现的内容的话,适合使用BitMap位图法进行处理。

还有就是一般的TOP K问题,就是找出前多少位的这种,一般内存容量都不是很大,采用的方式是 分治+hash+最小(大)堆排序。当然分布式的适合处理方式为MapReduce处理。

排序可以有很多种,按照不同的方式进行不同的排序,比如快排,最小堆排序,归并排序。如果大文件需要排序,并且严格要求内存的话,分治成小文件,然后采用归并排序很合适。

如果涉及到单词的类型处理的话,需要使用Trie树进行,因为这个非常合适处理,并且复杂度为O(k)。

 

如果有不对的地方,欢迎指正。

分享到:更多 ()