海量数据处理 经典案例

本篇涉及到的问题:海量日志数据,如何提取出某日访问淘宝次数最多的IP;上亿数据,统计其中出现次数最多的前N个数据;海量数据分布在100台电脑上,想办法高效地统计出这些数据的TOP10;5亿个int,找出他们的中位数;两个文件,各存放50亿条URL,每个URL占用64个字节,内存限制是4GB,找出两个文件中相同的URL;有40亿个不重复的undesigned int的整数,没排过序,现在给一个数,如何快速判断这个数是否在这40亿个数当中;

提示:分治、Hash映射、堆排序、双层桶划分、Bloom filter、bitmap、数据库索引、mapreduce。

 

 

 

海量日志数据,如何提取出某日访问淘宝次数最多的IP

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

1.IP地址最多有2^32=4G种取值情况,所以不能完全加载到内存中处理; 
2.可以考虑采用“分而治之”的思想,按照IP地址的Hash(IP)%1024值,把海量IP日志分别存储到1024个小文件中。这样,每个小文件最多包含4MB个IP地址; 
3.对于每一个小文件,可以构建一个IP为key,出现次数为value的Hash map,同时记录当前出现次数最多的那个IP地址;
4.可以得到1024个小文件中的出现次数最多的IP,再依据常规的排序算法得到总体上出现次数最多的IP;

https://blog.csdn.net/zhanglei0107/article/details/12137279#commentsedit这篇写的很详细,还附有Java代码!

     这里解释一下为什么用Hash(IP) % 1024值,如果不用,而直接分类的话,可能会出现这样一种情况,就是有个IP在每个小文件中都存在,而且这个IP并不一定在那个小文件中是数量最多的,那么最终可能选择的结果会有问题,所以这里用了Hash(IP)%1024值,这样的话,通过计算IP的Hash值,相同IP肯定会放到一个文件中,当然了不同的IP的Hash值也可能相同,就存在一个小文件中。

举个小例子模仿上述过程:0  10  33  15  22  10  14  7  8  15  10  11  8  10  29  5  14  7  这18个数(模拟为ip)分别对5取模,得到5个值:

上述5个值(value)经过排序算法得到最大值即出现次数最多的

 



上亿数据,统计其中出现次数最多的前N个数据

 分两种情况,

  1 如果数据能够在内存中放下,比如如果海量数据是ip地址,最多有4G个ip地址,每个ip地址占4个字节,需要内存16G,如果内存在几十G,则完全可以全部装入内存, 直接读取大文件,然后创建一个hash表,统计次数,最后再用堆统计最大的n个。

2  如果不能在内存放下,比如海量数据是字符串,不同的字符串个数无限,内存中可能存不下,那么需要先将海量数据进行分堆,按照hash值进行分堆,分成适宜在内存中操作的小文件,比如内存1G,字符串有20G,那么就分成20个以上的小文件,为什么要用hash分堆,这样是为了保证同一个字符串只会出现在同一个文件。

 

海量数据分布在100台电脑上,想办法高效地统计出这些数据的TOP10

    在每台电脑上求出TOP10,可以用包含10个元素的堆完成(最小TOP10就用最大堆,最大TOP10就用最小堆)。以最大TOP10为例,首先取前10个数整理成最小堆,依次扫描后面的数与堆顶元素比较,如果比堆顶元素大,就用该元素替换掉堆顶元素,再调整成最小堆,直到扫描到最后一个元素,也就找到了这台电脑上的最大TOP10。

    求出每台电脑上的TOP10之后,然后把这100台电脑上的TOP10组合起来,也就是1000个数,再利用类似的方法就可以找出最终的TOP10了。

感觉很贴近生活,比如:一般的比赛,先分别在小范围内选择前n名,这些各地前n名的人在去一个大地方终极PK出前n名。

 

 

 

10亿个int,找出他们的中位数

    中位数的定义:数字排序之后,位于最中间的那个数。将10亿个数字进行排序之后,位于第5亿个位置的那个数就是中位数。

①内存够:内存够还慌什么啊,直接把10亿个全部排序了,你用冒泡都可以,然后找到中间那个就可以了。但是你以为面试官会给你内存??

②内存不够:题目说是整数,我们认为是带符号的int,所以4字节,占32位。

     假设10亿个int保存在一个大文件中,依次读一部分文件到内存(不超过内存的限制),将每个int用二进制表示,比较二进制的最高位(第32位,符号位,0是正,1是负),如果数字的最高位为0,则将这个数字写入 file_0文件中;如果最高位为 1,则将该数字写入file_1文件中。如10000000 00000000 00000000 00000010为-2放到file_1中。

     从而将10亿个int分成了两个文件,假设 file_0文件中有6亿个int,file_1文件中有4亿个int。那么中位数就在 file_0 文件中,并且是 file_0 文件中所有int排序之后的第 1亿 个int。(file_1中的数都是负数,file_0中的数都是正数,也即这里一共只有40亿个负数,那么排序之后的第50亿个数一定位于file_0中)

     现在,我们只需要处理 file_0 文件了(不需要再考虑file_1文件)。对于 file_0 文件,有6亿个int,还是很多,所以同样采取上面的措施处理:将file_0文件依次读一部分到内存(不超内存限制),将每个int用二进制表示,比较二进制的次高位(第31位),如果int的次高位为0,写入file_0_0文件中;如果次高位为1,写入file_0_1文件 中。(次高位为1的为为0的大喔!)

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

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

 


以下这种桶排序也可以参考一下:
   int型在Java中是32位的,根据每个整数二进制前的5位(00000000 00000000 00000000 00000000),可以划分为32个不同的桶,如果某个桶的个数在内存中放不下,则继续划分,知道内存可以放下为止;然后统计每个桶中的数的个数,就可以中位数一定出现在哪个桶中,而且知道是该桶中第几大数,因为桶的划分是根据二进制前缀来进行划分的,桶之间是排好序的。

 

 

两个文件(A、B),各存放50亿条URL,每个URL占用64个字节,内存限制是4GB,找出两个文件中相同的URL。

分析:我们先来看如果要把这些URL全部加载到内存中,需要多大的空间。

1MB = 2^20 = 10^6 = 100W, 1GB = 2^30 = 10^9 = 10亿, 50亿 = 5G * 64 Byte = 320G,明显是不可能全部加载到内存中的。我们可采用以下方法解决:

方法1:

     采用Bloom filter,假设Bloom过滤器的错误率为0.01,则位数组大小m约为输入元素个数n的13倍,此时需要的哈希函数k约为8个。元素个数:n = 5G,位数组大小:m = 5G * 13 = 65G = 650亿 即需要650亿个bit位才能达到错误率0.01。而我们拥有的内存可容纳bit位个数:4G * 8bit = 32G bit = 320亿,按此实现错误率大于0.01。

方法2:

     分别扫描A,B两个文件,根据hash(url)%k(k为正整数,比如k = 1000,那么每个小文件占用300M,内存完全可以放得下)将url划分到不同的k个文件中,比如a0,a1,....a999;b0,b1,...b999;

    这样处理后相同的url肯定在对应的小文件中(a0 vs b0,a1 vs b1,...a999 vs b999)因为相同的url%1000的值肯定相同,不对应的小文件不可能有相同的url;然后我们只要求出1000对小文件中相同的url即可。比如对于a0 vs b0,我们可以遍历a0,将其中的url存放到hashmap中,然后遍历b0,如果b0中的某个url在hashmap中,则说明此url在a和b中同时存在,记录并保存下来即可。

 

 

 

有40亿个不重复的undesigned int的整数,没排过序,现在给一个数,如何快速判断这个数是否在这40亿个数当中。

解题思路:

(1)直接存进内存

    40亿个无符号数,如果放到内存,就需要开辟4*4G=16G的空间,因为数的范围不确定,所以开辟尽可能大的空间来存放,需42亿9千万×4字节的空间。

(2)位图   

    位图:在一块内存区域的每个比特存0或1,表示其对应的元素不存在或者存在。位图优点: 速度快,内存空间占用小,能表示大范围的数据。undesigned int的取值范围是0~2^32,我们可以申请来连续的2^32 / 8=512M的内存,每一个bit对应一个undesigned int,首先512M全部初始化为0,然后每处理一个数字就把对应的bit置为1,当需要查询的时候,直接找到对应的bit,看是0还是1。

 

    举个栗子:现有1,5,10,8,4,9,11,6,7,0,20,19,13,15,18,看14在不在这些数里面。按上面的想法就是,这些数比较集中,在0~20之间,所以我们先申请一段21bit长的空间,假设下面一个格子就是1bit。14对应的bit为0,所以14不存在。

  

#扩展:上面用1个bit来表示两个状态(有或无),当需要表示三个/四个状态时,我们可以用两个bit来表示,即00、01、10、11,试情况而定。

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值