大数据问题一般解决方式:利用哈希函数进行分流来解决内存限制或者其他限制的问题。
1. 哈希函数又叫散列函数,哈希函数的输入域可以是非常大的范围,但是输出域是固定范围。假设为s。
哈希函数的性质:
1. 典型的哈希函数都拥有无限的输入值域。
2. 输入值相同时 ,返回值一样。
3. 输入值不同时,返回值可能一样,也可能不一样。
4. 不同输入值得到的哈希值,整体均匀的分布在输出域s上。(这是评价一个哈希函数优劣的关键)
不同输入值得到的哈希值越均匀分布在s上,该哈希函数越优秀。
MD5和SHA1算法都是经典的哈希函数算法,了解即可。!!
Map-Reduce:
1. Map阶段:利用哈希函数把大任务分成子任务。同样哈希值的任务会被分配到同一个节点(可能是一台机器、一个计算节点)进行处理。
2. Reduce阶段:分开处理然后合并结果。子任务并发处理,然后合并结果。
Map-Reduce的难点:
1. 备份的考虑,分布式存储的设计细节,以及容灾策略。
2. 任务分配策略与任务进度跟踪的细节设计,节点状态的呈现。
3. 多用户权限的控制。
案例一:
用map—reduce方法统计一篇文章中每个单词出现的个数。
1. 文章预处理:
去掉文章中的标点符号
对连字符'-'的处理,特别是单词结尾处没写完,用连字符连接的情况。
对于缩写的处理:例如i'm等
大小写的处理
通过上面将有效的单词抓取出来,就得到只包含单词之后的文本。
2. map阶段
只包含单词之后的文本:对每个单词生成词频为1的记录:如(dog,1) (pig,1)一个单词可能有多个词频为1的记录,此时还未进行合并。
3. 单个单词的单一词频记录:通过哈希函数得到每个单词的哈希值,并根据该值分成若干组任务。
4. 子任务:每个子任务中包含若干种单词,但同一种单词不会分配进不同的子任务中。
单个子任务中同一种单词的词频进行合并,生成一个记录。然后把所有记录返回就是整篇文章的词频统计。
每个子任务都是并行处理,所以节省时间。
案例二:常见海量处理题目解题关键:
1. 分而治之,通过哈希函数将大任务分流到机器,或分流成小文件。
2. 常用的hashmap以及bitmap。
难点:通讯、时间和空间的估算。
请对10亿个IPV4的ip地址进行排序,每个ip只会出现一次。
IPv4的ip数量一共是约等于42亿。
ip------->无符号整数
普通方法为先将10亿个IP地址全部转化为无符号整数,然后对整数进行排序,之后将整数转换为ip地址就可以了。
但是这种方式光是将ip地址转换成整数就要占用 4*10亿 也就是大约4G空间。
更节省空间的做法为:
1. 申请长度为pow(2,32)的bit类型的数组。(bitmap)
2. 数组每个位置上是一个bit,只可表示0或1两种状态。因此,这个数组一共占用过的空间大约是512m。
3. 如果整数1出现就将bitmap上面相应0位置置1。同理,如果整数k出现,就将k-1位置置1。
因为bitmap长度的原因,它可以表示任何一个长度为32bit的整数。所以10亿个ip位置,就可以在bitmap上置1十亿个位置,最后扫描
这个bitmap从位置0开始,把ip地址通过位置值还原就可以了。
请对10亿人的年龄进行排序。
年龄可以认为在0-200之间,然后用一个足够大的数组,对10亿个年龄进行计数排序就可以了。
案例三:
有一个包含20亿个全是32位整数的大文件,在其中找到出现次数最多的数,但是内存限制只有2G。
普通解法:用hashmap记录所有数出现的次数。
key:具体某一种数。整型。4字节。
value:这种数出现的次数。4字节。整型。之所以出现的次数也是整型是因为32位表示20亿也是可以的。
这样的话一条记录占用8字节,当记录条数为2亿时,大约1.6G内存。但是如果20亿个整数都不相同的话就会有20亿条记录,
所以占用的内存就是8*20亿。所以用哈希表来直接统计20亿个整数的方案可能会导致内存不足。
更好的解法:将20亿个32位整数的大文件利用哈希函数进行分流形成多个小文件,根据哈希函数的性质可知:
同一种数不会被分流到不同文件,这是哈希函数性质决定的。
对于不同的数,每个文件中含有整数的种数几乎一样,这也是哈希函数性质决定的。
再对每一个小文件利用哈希函数进行分流,统计每个小文件中出现次数比较大的数字。最后再统计这些小文件中产生的所有出现次数最多的数中的次数最多的就是最终的结果。
案例四:32位无符号整数的范围是0~4294967295,现在有一个正好包含40亿个无符号整数的文件,所以在整个范围中必然有没出现过的数。可以使用最多10M的内存,只用找到一个没有出现过的数即可。该如何找?
如果用哈希表来记录所有的数,最差情况下,将出现40亿个不同的数。每一条记录占用4字节,大约需要16G内存。因此这种方式是绝对不可以的。
如果用bitmap的话,申请pow(2,32)的空间,每个位置为1bit,只能表示0或1两种状态,为1的话代表该下标所表示的数出现了。大约占用500m空间。也是不满足情况的。
正确的方式应该是这样的额:
将0~pow(2,32)-1范围平均分成64个区间,然后统计每个区间的数出现的次数。因为总共的范围为42亿,但数一共为40亿,所以必然会有区间计数不足(pow(2,32)/64)。只要找到一个这样的区间,这个区间一定少了某一个数,对这个空间进行接下来的操作就可以了。假如这个空间为a:
对区间a,再遍历一次40亿个数,此时只关心区间a上的数,并用bitmap统计区间a上的数的出现情况。需要的空间为500m/64也就是差不多8m的空间。
系统总结一下上面的方法:
1. 根据内存限制决定区间大小,根据区间大小,得到有多少个变量,来记录每个区间的数出现的次数。(刚才之所以将区间分成64个,是因为我们先计算10M的bitmap可以表示多大的区间,10m*pow(10,6)*8bit=8*pow(10,7)bit。(因为bitmap中一个bit就表示一个数,所以计算出有多少个bit就可以了)然后用pow(2,32)/(8*pow(10,7))就是需要分的区间的个数。)
2.利用区间计数的方式找到区间上的数不足的区间,这个区间上肯定有没出现过的数。
3.利用bitmap对不满的区间,进行这个区间上的数的词频统计。
案例五:某搜索公司一天的用户搜索词汇是海量的,假设有百亿的数据量,请设计一种求出每天最热100次的可行方法。
首先使用哈希函数进行分流,将百亿搜索词汇分流到不同的机器上。如果对于单个的机器还是存在内存不足等情况,可以对机器上的数据继续使用进行分流,分流到不同的
文件上。然后处理每一个小文件,得到每个小文件中词汇的词频统计。需要注意的是,这里面hash表统计每种词,以及出现的频率。哈希表建立记录,然后遍历哈希表。遍历的过程中,利用大小为100的小根堆进行top100的筛选,选出每个小文件的top100。每个小文件都有自己词频的小根堆,小根堆里的词按照词频排序,得到每个小文件排序后的
top100,。再利用小根堆或者外排序合并获得机器上的top100。机器之间最后利用小根堆或者外排序获得总体的top100.
对于topk问题,除了利用哈希函数分流,利用哈希表进行词频统计之外,还可能利用堆排序或者外排序等手段进行处理。
案例六:工程师常使用服务器集群来设计和实现数据缓存,一下是常见的策略。
1. 无论是添加、查询还是删除数据,都先将数据的id通过哈希函数转换成一个哈希值,记为key。
2. 如果目前机器有N台,则计算key%N的值,这个值就是该数据所属的机器编号,无论是添加、删除还是查询操作,都只在这台机器上进行。请分析这种缓存策略可能带来的问题,并提出改进的方案。
潜在问题:如果增加或删除机器,数据迁移的代价很大。因为我们使用哈希函数进行分流之后,对哈希值结果需要%N计算相应的机器。但是当机器数N发生变化之后,所有数据必须重新计算哈希值,以及对新的机器数N取余,来决定各自数据的归属。并且机器之间需要进行大规模的数据迁移,代价很大。
因此引入一致性哈希算法(很好的数据缓存设计方案):