Mapreduce中Map与Reduce的个数制定
操作:
MapReduce框架将文件分为多个splits,并为每个splits创建一个Mapper,所以Mappers的个数直接由splits的数目决定。而Reducers的数目可以通过job.setNumReduceTasks()函数设置
1、Map任务的个数:
理论值:
1、Mapper数据过大的话,会产生大量的小文件,过多的Mapper创建和初始化都会消耗大量的硬件资源Mapper数太小,并发度过小,Job执行时间过长,无法充分利用分布式硬件资源
2、map并行度是大约每个节点10-100个map,且最好每个map的执行时间至少一分钟
合理map个数:
经验总结:
1、hdfs上的数据进行大块化
1)如果某个input的文件非常的大,比如 1TB,可以考虑将hdfs上的每个block size设大,这样map和reduce的数据可以减小。还可以通过命令 :hadoop distcp -Ddfs.block.size=$[256*1024*1024] /path/to/inputdata /path/to/inputdata-with-largeblocks的方式来将已经存在hdfs上的数据进行大块化。然后删除掉原先的文件。
2)通常对于每一个输入的文件会有一个map split。如果输入文件太大,超过了hdfs块的大小(128M)那么对于同一个输入文件我们会有多余2个的map运行起来,会有一个比例进行运算来进行切片,为了减少资源的浪费;
例如一个文件大小为260M,在进行MapReduce运算时,会首先使用260M/128M,得出的结果和1.1进行比较大于则切分出一个128M作为一个分片,剩余132M,再次除以128,得到结果为1.03,小于1.1则将132作为一个切片,即最终260M被切分为两个切片进行处理,而非3个切片
2、MapReduce读取HBase表时会通过配置信息获取HBase表名,然后构造一个HTable对象,未设定开始和结束rowkey,mapper任务数默认是和表分区数相等,详细参考TableInputFormat的父类TableInputFormatBase;
重新实现一个TableInputFormat类,重写其中的getSplits()方法,可以自定义实现一个region对应N个Mapper任务
2、reduce任务的个数:
理论值
Reduce任务是一个数据聚合的步骤,数量默认为1。而使用过多的Reduce任务则意味着复杂的shuffle,并使输出文件的数量激增。
在真正的集群环境下,如果默认,那么所有的中间数据会发送给唯一的Reducer,导致任务变得非常缓慢。通常reduce数量是0.95或者1.75*( nodes * mapred.tasktracker.reduce.tasks.maximum);mapred.tasktracker.tasks.reduce.maximum(mapreduce.tasktracker.reduce.tasks.maximum):一个节点Reduce任务数量上限(默认是2),实际中一般设置为各节点cpu core数量,即能同时计算的slot数量。
合理reduce个数:
可以采用以下两种方式决定Reduce任务的合理数量:
1.每个reducer都可以在Map任务完成后立即启动: 0.95 * (节点数量 * mapreduce.tasktracker.reduce.tasks.maximum)
2.较快的节点在完成第一个Reduce任务后,可以开始第二轮的reduce任务:1.75 * (节点数量 * mapreduce.tasktracker.reduce.tasks.maximum)
究竟设多少个Reducers合适呢?为了解决这个问题,首先来了解一下slots的概念
1、slots有点类似一个资源池,每个任务(map和reduce)执行时都必须获得一个slot才能继续,否则只能等待。当一个任务完成后,该任务就归还slot,这个过程有点类似释放资源到资源池中。显然,每一个获得资源的任务都可以立即执行,无需等待。另一方面,mapreduce的任务由tasktracker节点负责执行的,所以slots可进一步理解为tasktrackers能够并发执行多个任务。slots分为mapper slots和reducer slots,分别对应最大可并行执行的mapper和reducer数
2、reducers 数目的最佳值和reducer slots的总数有关,通常情况下,让reducers的数目略小于reducer slots的总数,这样的目的:首先reducers可以并行执行,减少排队时间;其次对于未执行reducer的slots可以在其他reducer发生故障时,立即分配给新创建的reducer,不会明显 加长任务总时间。
3、出现reducers > mappers的情况就不合理了,这样有些mappers会工作消耗资源开销,但是对任务没有任何帮助。
经验总结:
1)job的每个map或者reduce task的运行时间都只有30-40秒钟,那么就减少该job的map或者reduce数,每一个task(map|reduce)的setup和加入到 调度器中进行调度,这个中间的过程可能都要花费几秒钟,所以如果每个task都非常快就跑完了,就会在task的开始和结束的时候浪费太多的时间
2)只要每个task都运行至少30-40秒钟,就可以考虑将mapper数扩大,比如集群的map slots为100个,那么就不要将一个job的mapper设成101,这样前100个map能够并行完成,而最后一个map要在前100个 mapper结束后才开始,因此在reduce开始运行前,map阶段的时间几乎就要翻倍
3)尽量不要运行太多的reduce task。对大多数job来说,最好rduce的个数最多和集群中的reduce持平,或者比集群的 reduce slots小。这个对于小集群而言,尤其重要。
3、JVM重用技术:调节mapred.job.reuse.jvm.num.tasks参数值
1)mapred.job.reuse.jvm.num.tasks默认是1,表示一个JVM上最多可以顺序执行的task数目(属于同一个Job)是1。也就是说一个task启一个JVM为每个task启动一个新的JVM将耗时1秒左右,对于运行时间较长(比如1分钟以上)的job影响不大,但如果都是时间很短的task,那么频繁启停JVM会有开销。
2)如果我们想使用JVM重用技术来提高性能,那么可以将mapred.job.reuse.jvm.num.tasks设置成大于1的数。这表示属于同一job的顺序执行的task可以共享一个JVM,也就是说第二轮的map可以重用前一轮的JVM,而不是第一轮结束后关闭JVM,第二轮再启动新的JVM。
3)那么最多一个JVM能顺序执行多少个task才关闭呢?这个值就是mapred.job.reuse.jvm.num.tasks。如果设置成-1,那么只要是同一个job的task(无所谓多少个),都可以按顺序在一个JVM上连续执行。
4)如果task属于不同的job,那么JVM重用机制无效,不同job的task需要不同的JVM来运行。