-
job提交后,需根据参数启动Mapper、Reducer任务,–那么Map task 的个数怎么设置
如果Map task过多,即每个任务处理一小部分数据并生成一个中间文件,将引起IO过多,文件过多;
如果Map task过少,则并行度过低,不能有效利用集群资源,处理时间长;影响Map task个数的因素有三个:
(1)读取的文件个数:默认情况下一个文件起始会启动一个map task
但是当有许多小文件时,如每个文件5M,单独启动一个task消耗过大
对于hive可通过设置hive参数将低于128M的小文件合并
set mapred.max.split.size=256000000 // 每个map最大的输入大小
set mapred.min.split.size.per.node=128000000 //一个节点上split块大小最低限度
//低于128M的小文件,数据在一个节点会合并,在多个不同的节点会把数据抓过来进行合并。
设置合并器:
set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat;
set hive.merge.mapFiles=true; //map only任务结束时启动合并
set hive.merge.mapredFiles=true; //reduce任务结束时启动合并
set hive.merge.size.per.task=256000000; //per task合并文件的大小
(2)读取的文件大小:默认情况下一个map读取文件大小为splitSize;当某个文件大小超过splitSize时,会启动多个task;splitSize参数如下:
mapreduce.input.fileinputformat.split.minsize //启动map最小的split size大小,默认0
mapreduce.input.fileinputformat.split.maxsize //启动map最大的split size大小,默认256M
splitSize = Math.max(minSize, Math.min(maxSize, blockSize));
默认splitSize等于blockSize,即每128M 数据,会启动一个map task;但是如果最后一个文件块小于128*1.1时只会启动一个task;
提交job:
hadoop jar XXXX.jar wordcont -Dmapreduce.input.fileinputformat.split.maxsize=xxx -Dmapreduce.input.fileinputformat.split.minsize=xxx
或者通过api设置
FileInputFormat.setMaxInputSplitSize(job, 20971520l);
FileInputFormat.setMinInputSplitSize(job, 1000);
(3)job参数设置
见以上默认情况与可调整参数;
- Map任务结束后根据key对数据进行partition,返回partition值
partition数据有一些几点需注意:
1)数据是否需要全局有序,默认情况下partition值是对key的hash值除以reduce个数;并对一个partition内的数据进行排序;如果要确保数据全局有序,有两种方法,一是设定只有一个reduce任务,但是这种方式对性能影响过大;二是对重写partition类,确保各个partition之间的数据是有序的,例如第一个partition的key范围为1-5,第二个为6-10;
2)数据倾斜:由于数据分布不均匀引起partition后数据不均衡,某一些partition数据过多,有些过少
3)key值为对象类型,包含多个成员变量,比如field1、field2,需要对field1排序后,对field2进行二次排序;
默认的partition中getPartition方法:
public class HashPartitioner<K, V> extends Partitioner<K, V> {
/** Use {@link Object#hashCode()} to partition. */
public int getPartition(K key, V value,
int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
重写partition类:
public class ProvincePartitioner extends Partitioner<Text, FlowBean>{
@Override
public int getPartition(Text key, FlowBean value, int numPartitions) {
return 你的partition逻辑;
}
}
在main方法指定maptask数据分区逻辑类,默认使用HashPartitioner
job.setPartitionerClass(ProvincePartitioner.class);
- partition数据输出到环形缓冲区,溢写到磁盘
环形缓冲区为一个叫Kvbuffer的字节数组,value和索引两部分,索引为4元组:value的起始位置、key的起始位置、partition值、value的长度;默认buffer大小为100M,当达到80%buffer大小时开始溢写;当数据溢写到磁盘后,将形成一个个小文件,可设置小文件一次性合并的个数,默认为10个;
同时可设置对map输出进行压缩,以减少磁盘IO;
//buffer大小设置
mapreduce.task.io.sort.mb=100
mapreduce.map.sort.spill.percent=0.8
mapreduce.task.io.sort.factor=10
mapreduce.map.output.compress.codec=org.apache.hadoop.io.compress.DefaultCodec //压缩方式
mapreduce.map.output.compress=false //对map输出进行压缩
-
溢写过程进行根据partition和key进行sort排序
-
reduce拉取map输出
一个map任务完成后回想ApplicationMaster报告,同时reduce任务会不断地向ApplicationMaster询问任务完成情况,一旦某个map任务完成达到一定比例(5%),reduce任务将到map机器copy需要的数据;map数据不会立刻删除,而是等到所有任务结束后再删除,为了防止重跑;
那么reduce到map拉取数据如果一次拉取一个还是同时启动多个线程去拉取呢?默认为5个,如果同时又100个map任务完成一个reduce同时只拉取5个,可调大这个值:
mapreduce.reduce.shuffle.parallelcopie=5 //设置拉取数据个数
mapreduce.reduce.shuffle.read.timeout=18000 //设置拉取数据超时时间
- reduce合并拉取的数据
reduce端对于拉取的数据有一个合并过程,同时也需要存储map数据,如果内存不够将溢写到磁盘,那么可
(1)调整内存大小默认是(0.7) 倍JVM堆内存
(2)调整溢写到磁盘的时堆内存占用比例
(3)溢写到磁盘过程中数据的合并:内存中合并后存入内存、内存中合并后存入磁盘、磁盘到磁盘的合并
mapreduce.reduce.shuffle.input.buffer.percent=0.7
mapreduce.reduce.shuffle.merge.percent=0.66
mapreduce.reduce.merge.memtomem.enabled=false //内存到内存合并
-
用户自定义reduce函数调用
默认情况下,是将所有shuffle后的数据都存入磁盘后,再启动reduce函数,即reduce数据全部都是由磁盘读取,可设置将reduce数据一部分存入内存,提高读取速度;
mapreduce.reduce.input.buffer.percent=0.0 //缓存比例,默认0
set mapreduce.job.reduces=<number> //hive设置reduce作业个数
set hive.exec.reducers.max=<number> //最大reduce个数,默认1009