在bat等大公司,基本所有业务的数据量级都很庞大,那么如何在保证数据完整性的情况下快速处理成了一个通用的难题,这里列举几个例子,大致反应一些处理思想。
1.一个文件中,每一行有一个整数,有上亿行,目的:统计出现次数超过三次的整数写入到另一个文件中。
分析:
(1)首先数据在文件中,既然要统计,那么有一个原则就是减少IO次数。
(2)其次数据量上亿,内存中肯定不可能全放下。
(3)需要统计次数,那么就需要知道每个整数的出现次数。
问题:既然内存中放不下每一个数据,那么就没办法知道当前拿到的数据出现了几次,也就不知道应不应该把它写入到目标文件中去。
思考:上亿的数据无法存放在内存中,我们先缩小数据量级,100w的数据是可以放在内存中的,那么我们是不是可以把上亿的数据分成100份100w的数据分开处理呢。
解决思路:先轮询一遍整个文件,对数据进行分片,分片规则:0<= num <100w放一个文件,100w<= num <200w放一个文件,依此类推。那么现在就会分为多个小文件了,我们就可以一个一个处理了,总的会进行2次IO,时间复杂度就是2n了。(PS:分治的方法,需要遍历两次,也可以使用位图,值需要遍历一次,但位图有限制,只能缩小32倍,2G内存最多可存放2^34个整数对应的位)
2.海量日志数据,提取出某日访问网站次数最多的IP
分析:
(1)日志数据极其庞大,包括各种格式不一致的数据。
(2)需要统计每个IP的访问次数,内存中肯定放不下当前的统计值,但是也不能放到外部存储中,这样会增加IO,并且效率会慢。
思考:一个ip32位,最多存在2^32次方个ip,可以采用分治的思想,比如IP模1000,再找出每个小文件中次数最多的ip,最后从1000个ip中找到访问次数最多的ip。(PS:分治的方法,类似于第一题)
3.搜索引擎会通过日志文件把用户每次检索使用的检索串都记录下来,每个查询串的长度为1~255个字节,假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G
分析:
(1)1G最多能放1024^2*4=4194304个查询串,是足够放不超过300w查询串的数据的。
(2)然后需要在300w数据中找出前10大,即第k大问题,可以通过堆排序进行计算。
解决思路:使用hashmap,遍历1000w记录,得出300w查询串的统计值。然后维护一个10大小的小根堆,遍历300w次,每次与根对比,最后得出前10。(PS:采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序)
4.有一个1G大小的文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M,返回频数最高的100个词
分析:
(1)最大的问题是内存仅1M,那么能同时读入内存的数据就不能超过1M,只能分片。
(2)需要的是统计每个词出现的频率。
(3)每个词不超过16个字节,那么至少也就有1024^3/16=67108864个词。
问题:分片应该如何分?如何从分片后的数据统计得出所有数据的频数最高的词。
思考:1g至少要分1024份才能保证每一份平均大小为1M,为避免出现部分文件超出1M,我们可以取值再大一点。
解决思路:首先,取每个词%5000,然后把每个词按结果分别存到5000个小文件中,这样每个文件大约200k,如果还有文件大于1M,用这个方法继续拆。然后对每个小文件进行统计,按每个小文件前100个词带统计结果存入统计文件中,最后对这个文件内容进行排序,得出前100。
5.给定a,b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4g,找出a,b文件共同的url。
分析:
(1)50亿个url需要5000000000*64 / (1024*1024*1024) ≈ 298G内存,也就是说每次只能处理单个文件1/75的数据,即数据至少分片75份。
(2)需要找出a,b共同的url,所以难点在于如何检索,如果以其中一个文件为基准去遍历至少也要(50亿*75)次遍历。
思考:使用url%1000,把a文件中所有url分到(a0,a1,…a999)中,同样b文件也分到(b0,b1,…,b999)中,这样每个文件大概300M,这样处理后,所有可能相同的url都在对应的小文件中(a0-b0,a1-b1…a999-b999),因为不对应的小文件不可能出现相同的url。然后对每个小文件进行对比就ok了。
6.给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中。
分析:
(1)给定的数是40亿,一个特殊的值,是否有什么特殊含义。40亿个整数需要消耗40*10^8*4/1024^3 ≈ 14.9g内存。
(2)需要判断一个数是否存在,那么最好能直接在内存中检索。
问题:内存中不足以放下所有的数。
思考:由于只需要判断是与否,所以可以优先考虑“位图算法”,即一个整数用一个位来表示,存在为“1”,不存在为“0”。
解决思路:一个int值为4个字节,一个字节8bit,那么一个int值为32位,就可以表示0~31这32个整数是否存在,下一个int值表示32~63这32个整数是否存在,依此类推。比如1565/32=48余29,应该在第47个整数的第28位上,0表示不存在,1表示存在。这样相当于节省内存32倍,需要的内存就为14.9g/32=476M,那么我们可以申请512M的内存。
另一种解决思路:分桶。可以参考第一题的按照数值大小分,也可以用另一个方案如下:
一个int值32位,首先,按首位为“0”和为“1”分,然后分别存放在两个文件中;然后再按次位为“0”或为“1”分,又可以分为2个文件了,这样就可以分为4个文件了,依次类推。其实就是一个二叉树的结构,这个二叉树最多可以有33层,也就是2^32,但是我们没有必要分那么细,因为2^32=42亿了,已经超过了我们要处理的数据,我们可以折衷分为2^5=32个文件即可,刚好512M内存足够存放一个文件的内容。这样我们需要检索一个数据的时候,可以先判断一下它的前5位,然后通过二叉树查找到对应文件,把该文件映射到内存中,然后在内存中检索。
来源:CSDN
作者:青鱼入云
链接:https://blog.csdn.net/u011305680/article/details/80142930