1.maptask:运行map部分的任务,我们就叫做maptask。
2.并行度:同时运行的maptask的任务的个数,一个maptask肯定只运行在一台节点上。
3.例如文件大小是500M:
存储为三块:
blk_1:0-128 blk_2:128-256 blk_3:256-384 blk_4:384-500
启动一个maptask合适吗?剩下的两台机器的没有任务,不合理!
maptask应该最终对应解决了多少数据量?
一个maptask解决100M合理吗?
第1个maptask:0-100M 第一个块的0-100M
第2个maptask:100-200M 第一个块100-128M 第二个块128-200M
第3个maptask:200-300M 第二个块的200-256M 第三个块256-300M
第4个maptask:300-400M ….
第5个maptask:400-500M …
如果两个块存储的是不同的节点,这个时候需要跨节点访问数据,存在网络传输问题,性能低 !
一个maptask解决200M问题合理吗?
第1maptask:0-200M 第一个块0-128M 第二个快 128-200M
仍然存在和上面相同的问题我们分析,如果一个maptask解决的数据正好是128M,这样最好 。
4.实际上:一个maptask任务解决的数据量对应的是一个文件切片。 分片的数量等于启动的MapTask的数量。
5.切片不是一个物理概念,是一个逻辑概念,仅仅划分一个偏移量的 并没有真正的进行文件切分。逻辑切片理论上大小=一个block块的大小=128m。
protected long computeSplitSize(long blockSize, long minSize,
long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}
通过以上方法最终算的切片的大小是block大小,128M
6.切片和切块的关系:只是在默认情况大小相同
切块:针对于数据存储的,物理上的
切片:针对于maptask的并行度,逻辑上的概念
7.想自定义切片的大小:
1)修改配置文件,改变的是整个集群的
<128M protected long computeSplitSize(long blockSize, long minSize,long maxSize) {
return Math.max(minSize, Math.min(maxSize, blockSize));
}
maxSize= mapreduce.input.fileinputformat.split.maxsize >128M
minSize=mapreduce.input.fileinputformat.split.minsize
2)在代码中修改
FileInputFormat.setMaxInputSplitSize(job, 100);//设置最大值
//FileInputFormat.setMinInputSplitSize(job, 130*1024*1024);//设置最下值
8.例如有4个小文件,存4个块—4个逻辑切片,任务的启动的时候,启动4个maptask任务
运行mr程序的时候,产生进程:
MRAppMaster:程序的管理者
YarnChild—–一个maptask/reducetask任务
……
有小文件的运行的时候,应该合并,让多个小文件启动一个任务
//多个小文件运行的时候: 修改输入类
job.setInputFormatClass(CombineTextInputFormat.class);
//修改切片大小
CombineTextInputFormat.setMinInputSplitSize(job, 130*1024*1024);
1.reducetask并行度就是将原来的一个大任务,分成多个小任务,每一个任务负责一部分计算数据。
2.reduce任务有几个,最直观的的显示就是结果文件的个数。一个结果文件对应于一个reducetask的执行结果。底层分reducetask任务的时候,是按照分区规则分的,每一个reducetask最终对应一个分区的数据。reducetsak的个数和用户设定的有关。
3.默认的分区算法:
public class HashPartitioner<K, V> extends Partitioner<K, V> {
/** Use {@link Object#hashCode()} to partition. */
//K key map输出的lkey, V value map输出的value int numReduceTasks job.setNumReducetask(3)
public int getPartition(K key, V value,int numReduceTasks(3)) {
//key.hashCode() & Integer.MAX_VALUE 防止溢出
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
}
hash%reducetask的个数,假设分区三个,余数为0 1 2
分区3个 余数为0的分为一个区 相同的单词肯定会分到一个区中----启动reducetask1任务-------part-r-00000
余数为1的分为一个区-----启动reducetask2任务-----part-r-00001
余数为2的分为一个区-----启动reducetask3任务-----part-r-00002
4.自定义分区:按照用户的业务进行分区
步骤:
1)继承Partitioner
2)重写getPartition
3)Driver类中:
//添加自定义分区
job.setPartitionerClass(MyPartitioner.class);
// 这个参数如果不指定,默认reducetask 1
job.setNumReduceTasks(5);
5.自定义分区的时候:
最终启动几个reducetask任务?由job.setNumReducetask()设定
每个reducetask任务的数据分配是谁决定的?自定义的分区决定的。
自定义分区的个数是4个,reducetask可以是几个?1个或者大于等于4个。
注意:自定义分区的时候,分区编号从0开始,最好是连续的整数。如果不连续的话,job.setNumReducetask至少比最大的返回值+1,如果没有返回值的分区号 会返回空文件。
6.一个maptask或者reducetask只能在一个节点上运行,一个节点上可以运行多个maptask或者reducetask任务,这是yarn负责分配的
1.对于maptask,1个逻辑切片对应1个block块,数据比较均匀,并行度比较高
2.对于reducetask存在数据倾斜的风险:数据倾斜指的是每个reducetask的数据分配不均匀,产生数据倾斜。
有的reducatesk分配的任务比较多,就会造成当前的reducetask处理的时间长;
有的reducatesk分配的任务比较少,就会造成当前的reducetask处理的时间短。
3.存在数据倾斜的原因:分区算法中对map端输出的数据分配的不均匀
4.如何避免数据倾斜:合理的设计分区算法。
import java.io.IOException;
import java.io.Serializable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
public class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{
@Override
protected void map(LongWritable key,
Text value,
Context context)
throws IOException, InterruptedException {
//拿到每一行的内容 进行分割
//将text---String
String line = value.toString();
//拆分单词
String[] words = line.split("\t");
//循环遍历每一个单词 进行打标机 1 发送给reduce进行统一统计
for(String w:words){
//参数1:key 参数2:value
//String--text
Text k=new Text(w);
IntWritable v=new IntWritable(1);
context.write(k, v);
}
}
}
import java.io.IOException;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
public class WordCountReducer extends Reducer<Text, IntWritable, Text, IntWritable>{
@Override
protected void reduce(Text key,
Iterable values,
Context context) throws IOException, InterruptedException {
//进行词频统计
int sum=0;
//循环变遍历values 求和
for(IntWritable v:values){
//v.get() 这个是将intwritable转换为int
sum+=v.get();
}
context.write(key, new IntWritable(sum));
}
}
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
public class MyPartitioner extends Partitioner<Text, IntWritable>{
@Override
public int getPartition(Text key, IntWritable value, int numPartitions) {
String k = key.toString();
if(k.charAt(0)>='a'&&k.charAt(0)<='f'){
return 0;
}else if(k.charAt(0)>='g'&&k.charAt(0)<='n'){
return 1;
}else if(k.charAt(0)>='o'&&k.charAt(0)<='t'){
return 2;
}else{
return 4;
}
}
}
import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.CombineFileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.CombineTextInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
public class Driver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
System.setProperty("HADOOP_USER_NAME", "hadoop");
//加载配置文件
Configuration conf=new Configuration();
//启动一个job 一个map reduce程序 这里叫做一个job
Job job=Job.getInstance(conf);
//指定job运行的主类
job.setJarByClass(Driver.class);
//指定这个job的mapper类和reduce类
job.setMapperClass(WordCountMapper.class);
job.setReducerClass(WordCountReducer.class);
//指定map的输出的key 和 value的类型
//这里为什么还要指定 泛型的只在编译的时候有作用 运行会自动擦除 所以在这里需要指定一下
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//指定reduce输出的key和value类型
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//多个小文件运行的饿时候: 修改输入类
//添加自定义分区
job.setPartitionerClass(MyPartitioner.class);
//job.setNumReduceTasks(3); 这个参数 如果不指定 默认reducetask 1
//job.setNumReduceTasks(5);
//设定最终需要几个reducetask执行的
//job.setNumReduceTasks(3);
FileInputFormat.addInputPath(job, new Path("hdfs://hadoop01:9000/in"));
//添加输出路径 输出路径一定不能存在 怕如果存在会进行覆盖
FileOutputFormat.setOutputPath(job, new Path("hdfs://hadoop01:9000/out18"));
//提交job
job.waitForCompletion(true);
}
}