hadoop08--maptask、reducetask的并行度&数据倾斜问题

maptask的并行度

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);

reducetask的并行度

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.如何避免数据倾斜:合理的设计分区算法。

wordcount分区代码

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);
    }

}

你可能感兴趣的:(hadoop)