- 如果存在大量的小数据文件,可以使用SequenceFile、自定义的CombineFileInputFormat。
每一个文件都会启一个Mapper任务,如果有大量的小文件,就会造成资源浪费,Mapper任务或者Reducer任务本身都是java进程,在启动任务的时候其实是启动java进程,启动进程是需要耗费大量的资源的。进程和线程是不一样的,进程是重量级的,线程是轻量级的,这也就意味着,如果小文件很多,启动进程的时间比任务运行的时间可能还要大,比如一个任务总共需要10秒,启动、关闭、销毁进程可能就需要7秒左右,所以我们可以使用SequenceFile和自定义的CombineFileInputFormat把小文件合成一个大文件,然后交给一个Mapper去处理。
那么是不是合并后,效率就会提升很多呢,其实不一定,比如5个小文件,合并后让一个Mapper处理,但是这个5个文件可能不在一台机器上,那么就会产生更多的网络传输,也会占用大量的时间,但是相比较而言,这种方法还是比一个Mapper处理一个文件快,所以最好的方法还是避免产生大量的小文件。
2. 推测执行在整个集群上关闭,特定需要的作业单独开启,一般可以省下约5%~10%的集群资源
mapred.map.task.speculative.execution=false;
mapred.reduce.task.speculative.execution=false;
什么是推测执行呢?任务在运行的过程中,如果某个任务很慢的话,集群会担心这个任务太慢会影响整体的进度,那么集群会自动开启一个同样的任务运行相同的数据,也就是说对于一份数据源有两个任务在同时运行,谁先运行完,就把另一个杀死,表面上看这是好事啊,其实不然,因为重启一个任务,会占用额外的资源,比如有很多的任务在同时执行,其他的还没执行,你这里开启了推测执行,导致后面的任务没有足够的资源去运行,所以需要关掉推测执行。
所以说如果任务多,资源紧的情况下,关掉推测执行;如果任务少,资源充足的话,开启推测执行。
3. 开启jvm重用
mapred.job.reuse.jvm.num.tasks=1
因为从第一点中我们得知,一个任务就是一个进程,而开启一个进程,就相当于新开一个虚拟机,为了节省资源的占用,我们可以开启jvm重用,就不必要新启一些进程了,只需要再启mapper和reduce任务即可。
4.增加InputSplit的大小
mapred.min.split.size=268435456
一个InputSplit对应一个Mapper任务,InputSplit的数量少了,对应的Mapper任务就少了,占用的资源就少,但是InputSplit变大了,意味着Mapper每次处理的数据量也大了吗?那不矛盾了,是这样的,当我们的Mapper任务开启后,意味着进程的初始化已经完成了,完成后,我们就要尽量的重用这个进程,此时Mapper的数据量变大,意味着吞吐量就上来了,性能就提升了。
5.增大map输出的缓存
io.sort.mb=300
reduce在把map的输出拿走之前,map输出的数据默认是存在内存中的,大小是100M,如果内存中存不小后就会写到磁盘上,每次写到磁盘就是一次文件IO操作,所以增大缓存,写到磁盘上的次数就变少了。
6.增加合并spill文件数量
io.sort.factor=50
内存中装不下,map输出的数据写到Linux磁盘的过程叫spill,写到磁盘中时是一个一个的小文件,等最后记录写完,然后再合并这些小文件为一个分区且排序的文件,如果每次合并的文件数量越大,合并的次数就越少,就能减少合并的时间,从而加快传输到reduce的时间。
7.map端输出压缩,推荐LZO压缩算法
mapred.compress.map.out = true
mapred.map.output.compression.codec = com.hadoop.compression.lzo.LzoCodec;
map端输出压缩后,占用的磁盘少,前面的spill、合并速度就快,送到reduce就快,shuffle的过程也快。
8.增大shuffle线程复制数
mapred.reduce.parallel.copies=15
数据从map端送到reduce端的过程叫shuffle, shuffle的时候是reduce端发起请求,通过http协议从map端复制过去,在复制的时候,如果线程数增大,意味着同时拷贝多份,吞吐量就上去了,这样可以增加shuffle执行的速度,减少shuffle运行的时间,那么并发量也大了,会增加网络带宽的压力,但是不用担心,这个过程执行发生在内网,所以网络问题不会太大。
9.设置单个节点的map和reduce执行数量(默认值是2)
执行数量变大,意味着单机tasktracker可以并行执行多个任务,占用内存会增加,因为每个map任务就是一个进程,通常情况下,只要内存够,可以把任务数量增加。