文章目录
MapReduce是一种分布式计算模型,是Google提出来的,主要用于搜索领域,解决海量数据的计算问题。我自己在学习的过程中遇到了很多疑问,例如:
MapReduce的全套过程分为三个大阶段,分别是Map、Shuffle和Reduce。结合多篇资料,我最终确定划分11个小步骤来描述这个过程,在后续的内容中我也会结合一部分源码来进行剖析。

map就是对数据进行局部汇总,reduce就是对局部数据进行最终汇总。
首先,一个Block的默认大小为128M,之所以会有博客写为64M,是因为在Hadoop2.x中修改了这个默认设置。Split的默认大小为128M,但并不是每一个Split都是128M,具体分析过程如下,请看如下源码:
SplitSize = Math.max(minSize, Math.min(maxSize, BlockSize));
也就是说Split的默认大小取决于minSize、maxSize以及BlockSize这三个变量。
其中minSize的相关源码为:Math.max(getFormatMinSplitSize(), getMinSplitSize(job));这行源码中getFormatMinSplitSize()的值为1,getMinSplitSize(job)的值为0,因此minSize的值为1;
maxSize的相关源码为:maxSize=getMaxSplitSize(job)=Long.MAX_VALUE,也就是说maxSize的值为Long.MAX_VALUE;
BlockSize的值默认为128M。
所以最终SplitSize=128M。
上面说的是Split的默认大小与Block相同,都是128M,但并不是说一个Block就对应一个Split,这里仅描述大小关系。但是之所以说并不是每一个Split都是128M,因为文件总不可能都是128M的整数倍,那么多出的那一部分怎么处理?源码中会判断剩余待切分文件大小/splitsize是否大于SPLIT_SLOP(值为1.1),如果大于1.1,那么会继续切分;如果小于1.1,会将剩下的部分切到同一个Split。
举几个例子帮助理解:
- 一个1G的文件,会产生多少个Split?
Block块默认是128M,所以1G的文件会产生8个Block块,默认情况下Split的大小和Block块的大小一致,也就是8个Split。- 1000个文件,每个文件100KB,会产生多少个Split?
一个文件,不管再小,都会占用一个Block,所以这1000个小文件会产生1000个Block,默认情况下Split的大小和Block块的大小一致,那最终会产生1000个Split。- 一个140M的文件,会产生多少个Split?
这个有点特殊,140M的文件虽然会产生2个Block,但140M/128M=1.09375<1.1,所以这个文件只会产生一个Split,这个文件其实再稍微大1M就可以产生2个Split。
默认一个Split对应一个Map,框架调用Mapper类中的map(…)函数,map函数的输入是<k1,v1>,输出是<k2,v2>。

Shuffle是介于Map和Reduce之间的一个过程,可以分为Map端的shuffle和Reduce端的Shuffle。MapReduce中,Map阶段处理的数据如何传递给Reduce阶段,是框架中最关键的一个流程,这个流程就叫Shuffle。
分区默认使用HashPartitioner,使用哈希方法对key进行分区,getPatition方法相关源码为:(key.hashcode()&Inyeger.MAX_VALUE)%numReduceTask,其中numReduceTask默认为1,而任何书向1取余都为0,因此默认只有一个分区,又因为一个分区对应一个Reduce任务,所以只有也一个Ruduce。若要提高并行度,增加Reduce任务数,只需要修改numReduceTask数值即可。
但是使用这种哈希方法分区有可能会导致数据倾斜问题,就比如现在一个文件中包含100万条数据,每个数据都是一个十以内数字,其中数字5出现了900万次,现在设置numReduceTask为10,那么根据哈希方法分区,其中的900万条数据都被分到分区5对应的Reduce任务下,这无疑是严重的影响了系统的运行效率。这种问题的解法并不会在这里展开说明,后续博客中会更新相关内容。
按照Key,采用字典顺序进行排序,Sort操作无论是否需要,在逻辑上都必须执行。
分组是根据map<key, value>中的key进行分组,目的是提高Reduce的并行度。
规约是可选操作,在map端输出中先做一次合并,相当于做了一个局部的reduce操作。规约操作会将map输出的<k1,v1>,<k1,v2>,<k2,v3>这样的数据转化为<k1,{v1,v2}>,<k2,{v3}>。
序列化(Serialization)是指把结构化对象转化为字节流,当要在进程间传递对象或持久化对象的时候,就需要进行这个操作。这里进行序列化是为了把map的执行结果写入内存。
反序列化(Deserialization)是序列化的逆过程,把字节流转为结构化对象,当要将接收到或从磁盘读取的字节流转换为对象,就要进行这个操作。这里进行反序列化是为了读取数据到不同的reduce节点。
reduce端接收到的是多个map的输出,对多个map任务中相同分区的数据进行合并、排序、分组。虽然之前在map中已经做了排序和分组,这边也做这些操作其实并不重复,因为map端是局部的操作,而reduce端是全局的操作,之前是每个map任务内进行排序,是有序的,但是多个map任务之间就是无序的了。
框架调用Reducer类中的reduce方法,reduce方法的输入是<k2,{v2}>,输出是<k3,v3>。一个<k2,{v2}>调用一次reduce函数。程序员需要覆盖reduce函数,实现具体的业务逻辑。
框架会把reduce的输出结果保存到HDFS中。
以上就是博客的全部内容,MapReduce的相关操作其实并不繁琐,至少逻辑顺序是非常清晰明了的,希望大家都能有所收获。
1.1.1 YARN的介绍 为克服Hadoop1.0中HDFS和MapReduce存在的各种问题⽽提出的,针对Hadoop1.0中的MapReduce在扩展性和多框架⽀持⽅⾯的不⾜,提出了全新的资源管理框架YARN. ApacheYARN(YetanotherResourceNegotiator的缩写)是Hadoop集群的资源管理系统,负责为计算程序提供服务器计算资源,相当于⼀个分布式的操作系统平台,⽽MapReduce等计算程序则相当于运⾏于操作系统之上的应⽤程序。 YARN被引⼊Hadoop2,最初是为了改善MapReduce的实现,但是因为具有⾜够的通⽤性,同样可以⽀持其他的分布式计算模
我需要从json记录中获取一些值并像下面这样提取curr_json_doc['title']['genre'].map{|s|s['name']}.join(',')但对于某些记录,curr_json_doc['title']['genre']可以为空。所以我想对map和join()使用try函数。我试过如下curr_json_doc['title']['genre'].try(:map,{|s|s['name']}).try(:join,(','))但是没用。 最佳答案 你没有正确传递block。block被传递给参数括号外的方法
Enumerable#each和Enumerable#map的区别在于返回的是接收者还是映射后的结果。回到接收者是微不足道的,你通常不需要在each之后继续一个方法链,比如each{...}.another_method(我可能没见过这样的案例。即使你想回到接收者那里,你也可以通过tap来实现)。所以我认为所有或者大部分使用Enumerable#each的情况都可以用Enumerable#map代替。我错了吗?如果我是对的,each的目的是什么?map是否比each慢?编辑:我知道当您对返回值不感兴趣时使用each是一种常见的做法。我对这种做法是否存在不感兴趣,但感兴趣的是,除了从
1.回顾.TransportServicepublicclassTransportServiceextendsAbstractLifecycleComponentTransportService:方法:1publicfinalTextendsTransportResponse>voidsendRequest(finalTransport.Connectionconnection,finalStringaction,finalTransportRequestrequest,finalTransportRequestOptionsoptions,TransportResponseHandlerT>
map遍历数组是否比each更快?两者有速度差异吗?mapresult=arr.map{|a|a+2}每个result=[]arr.eachdo|a|result.push(a+2)end 最佳答案 我认为是的。我试过这个测试require"benchmark"n=10000arr=Array.new(10000,1)Benchmark.bmdo|x|#Mapx.reportdon.timesdoresult=arr.map{|a|a+2}endend#Eachx.reportdon.timesdoresult=[]arr.each
我想念Ruby中的Hash方法来仅转换/映射散列值。h={1=>[9,2,3,4],2=>[6],3=>[5,7,1]}h.map_values{|v|v.size}#=>{1=>4,2=>1,3=>3}你如何在Ruby中归档它?更新:我正在寻找map_values()的实现。#moreexamplesh.map_values{|v|v.reduce(0,:+)}#=>{1=>18,2=>6,3=>13}h.map_values(&:min)#=>{1=>2,2=>6,3=>1} 最佳答案 Ruby2.4引入了方法Hash#tran
假设我有一个函数defodd_or_evennifn%2==0return:evenelsereturn:oddendend我有一个简单的可枚举数组simple=[1,2,3,4,5]然后我用我的函数在map中运行它,使用一个do-endblock:simple.mapdo|n|odd_or_even(n)end#=>[:odd,:even,:odd,:even,:odd]如果不首先定义函数,我怎么能做到这一点?例如,#doesnotworksimple.mapdo|n|ifn%2==0return:evenelsereturn:oddendend#Desiredresult:#=>[
我想获取一个数组并将其作为订单列表。目前我正在尝试以这种方式进行:r=["a","b","c"]r.each_with_index{|w,index|puts"#{index+1}.#{w}"}.map.to_a#1.a#2.b#3.c#=>["a","b","c"]输出应该是["1.a","2.b","3.c"]。如何让正确的输出成为r数组的新值? 最佳答案 a.to_enum.with_index(1).map{|element,index|"#{index}.#{element}"}或a.map.with_index(1){|
我看到有关未找到文件min.map的错误消息:GETjQuery'sjquery-1.10.2.min.mapistriggeringa404(NotFound)截图这是从哪里来的? 最佳答案 如果ChromeDevTools报告.map文件的404(可能是jquery-1.10.2.min.map、jquery.min.map或jquery-2.0.3.min.map,但任何事情都可能发生)首先要知道的是,这仅在使用DevTools时才会请求。您的用户不会遇到此404。现在您可以修复此问题或禁用sourcemap功能。修复:获取文
在Ruby中,Enumerable模块混合到集合类中,并依赖于提供each方法的类,该方法产生集合中的每个项目。好吧,如果我想在我自己的类中使用Enumerable,我将只实现each[1]:classColorsincludeEnumerabledefeachyield"red"yield"green"yield"blue"endend>c=Colors.new>c.map{|i|i.reverse}#=>["der","neerg","eulb"]这按预期工作。但是,如果我用Enumerable重写现有类上的each方法,它不会破坏map等函数。为什么不呢?classArrayde