MR排序和输入输出格式

mapreduce作业提交流程:
    1、配置文件        //输入输出格式(TextInput(output)Format)
    2、job.waitforcompletion
    3、submit
    4int map = split.size
        1)、看文件格式,textFile
            判断文件的压缩编解码器(文件名后缀),如果是压缩,则判断是否是可切割的压缩
            如果不是压缩,则肯定可切割

        2)、getMaxSplitSize    //最大切片默认是long的最大值
            getMinSplitSize    //最小切片默认是1
            blockSize        //块大小
        
    5、开始作业真正提交过程
        ExecutorService mapService = createMapExecutor();
        runTasks(mapRunnables, mapService, "map");        //执行MapTaskRunnables对象的run函数

        //map中除了partition、combiner还有sort阶段

        使用maptask的runNewMapper方法开始正式的map阶段
            1、根据自定义map类名,获得自定义map对象
            2、调用Mapper的run函数来运行用户自定义的map方法
                //设置相关变量或者参数,一个map只调用一次
                setup(context);
                try {
                  while (context.nextKeyValue()) {
                //使用while循环调用自定义map的方法
                map(context.getCurrentKey(), context.getCurrentValue(), context);
                  }
                } finally {
                  //清理过程,包括清理一些没用的k-v
                  cleanup(context);
                }

        Spill:溢出    //当map中的数据超出内存空间的80%,超出的数据就会被本地化

        map端的输出称为IFile:key-len, value-len, key, value
        
        shuffle在调用的时候对ifile进行处理

    4、通过shuffle进行网络间分发,reduce的调用过程类似于map过程

    5、细节:FileInputFormat ====> RecordReader ==> 
         map ===> partition ===> sort ===> combiner ===> shuffle ====> 
         reduce ===> RecordWriter ====> FileOutputFormat

MR的数据倾斜处理:
    大量数据涌入单个节点,造成单个节点的负载量变大
    1、重新设计key
    2、随机分区

    均使用两个job


排序:
    部分排序:对每个分区中的key分别进行排序

    全排序:  对所有分区中的key进行排序
         1、使用一个reduce
         2、自定义分区函数    
         3、采样        //设置采样路径,要放在输入输出路径之后
                    //保证map的输入key和输出key一致!!!
                    
                    
            如果有n个reduce,那么对应的会产生n-1个key集

            1)随机采样                随机取得样本
                //freq :每个key被选中的几率    样本数/总数< freq 则继续
                //numSamples:获得样本总数
                //maxSplitsSampled: 最大切片数
                //只要一个条件成立则停止采样
                RandomSampler(double freq, int numSamples, int maxSplitsSampled)
                
            2)间隔采样:适用场景(key有序的情况)    每隔一个相等的间隔对样本进行采样
                //freq :每个key被选中的几率
                //maxSplitsSampled: 最大切片数
                //

            3)切片采样                对每个切片的数据头部取得数据



        采样过程代码顺序:保证全分区和采样设置在最后
                
            1、设置输入输出路径
            2、设置输出k-v
            3、设置输入输出文件类型
            4、指定map和reduce的类
            5、任务数设置
            6、设置全分区函数
            7、设置采样器


            Configuration conf = job.getConfiguration();
            Job job = Job.getInstance();
            job.setJarByClass(App.class);
            job.setJobName("word count");

            // 设置输入输出路径
            FileInputFormat.addInputPath(job, new Path("D:/Temp/seq/1.seq"));
            FileOutputFormat.setOutputPath(job, new Path("D:/Temp/out"));

            FileSystem fs = FileSystem.get(conf);
            if (fs.exists(new Path("D:/Temp/out"))) {
                fs.delete(new Path("D:/Temp/out"), true);
            }
            
            job.setNumReduceTasks(6);

            // 设置输出kv
            job.setOutputKeyClass(Text.class);
            job.setOutputValueClass(IntWritable.class);
            //设置输入类型
            job.setInputFormatClass(SequenceFileInputFormat.class);

            // 指定map和reduce的类
            job.setMapperClass(WCMapper.class);
            
            // 指定分区函数
            job.setPartitionerClass(TotalOrderPartitioner.class);
            TotalOrderPartitioner.setPartitionFile(conf, new Path("D:/Temp/par/par.seq"));
            
            /**
             * freq 一个key被选中的几率
             * numSamples 样本总数.
             * maxSplitsSampled 一个切片检查的最大数量. 
             */
            InputSampler.Sampler<Text, Text> sampler = new InputSampler.RandomSampler(0.1, 500,10);
            //InputSampler.Sampler<Text, Text> sampler = new InputSampler.IntervalSampler(0.1, 500);
            //InputSampler.Sampler<Text, Text> sampler = new InputSampler.SplitSampler(500,10);
            InputSampler.writePartitionFile(job, sampler);
            
            
            System.exit((job.waitForCompletion(true) ? 0 : 1));


    二次排序:对key进行全排序的基础上,对value进行排序


    当Map和Reduce的输出K-V不同时,要进行分别配置

        //设置reduce的输出K-V
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

        //设置Map输出k-v
        job.setMapOutputKeyClass(KVPair.class);
        job.setMapOutputValueClass(NullWritable.class);


    分组对比器:
        public class GroupComparator extends WritableComparator {
            protected GroupComparator() {
            super(KVPair.class,true);
            }
            //将相同年份的KVPair识别为一组
            @Override
            public int compare(WritableComparable a, WritableComparable b) {
            KVPair kv1 = (KVPair)a;
            KVPair kv2 = (KVPair)b;

            String year1 = kv1.getYear();
            String year2 = kv2.getYear();
            return year1.compareTo(year2);
            }
        }
    
InputFormat OutputFormat:
=====================================
    TextInputFormat:    文本类型,以行为单位处理,k:LongWritable, v:Text
    KeyValueTextInputFormat    k-v文本类型,以制表符为k-v的分隔,默认k-v均为text格式

    SequenceFileInputFormat:k-v序列文件,k-v类型以sequenceFile为主
        1、创建源文件
        2、在App中指定文件格式
            job.setInputFormatclass()
        3、对Map的输入k-v类型进行修改
            

    NLineInputFormat:    文本类型,以n行为单位进行处理,一次切片按照指定的行号进行切分
                在处理一行文本数据量小的情况下可以使用,减少分发过程
                k:LongWritable, v:Text
        
    

    DBInputFormat:对SQL中的数据进行数据迁移或分布式计算、k:LongWritable    v:DBWritable

        DBWritable:

            public class MyWritable implements Writable, DBWritable {

                 private String line;
                 
                
                 public void write(DataOutput out) throws IOException {
                   out.writeUTF(line);
                   
                 }
                 
                 public void readFields(DataInput in) throws IOException {
                   line = in.readUTF();
                 }
                
                 //使用ppst对数据进行写入
                 public void write(PreparedStatement statement) throws SQLException {
                   statement.setString(1, line);
                 }
                
                 //jdbc读取数据返回的是结果集对象(ResultSet)
                 public void readFields(ResultSet resultSet) throws SQLException {
                   line = resultSet.getString(1);
                  
                 } 
               }
原文地址:https://www.cnblogs.com/zyde/p/8944209.html