Hadoop的mapreduce之分区Partitioner

1. 本文讲讲Hadoop的mapreduce之分区Partitioner

1.1默认情况下MR输出文件个数

在默认情况下,不管map阶段有多少个并发执行task,到reduce阶段,所有的结果都将有一个reduce来处理,并且最终结果输出到一个文件中。
默认分区

1.2 修改reducetask个数

在MapReduce程序的驱动类中,通过job提供的方法,可以修改reducetask的个数。
修改分区
就可以得到六个分区
六个分区结果

1.3 数据量

这个是等会用到的数据量可以下载供参考
链接:https://pan.baidu.com/s/1wByTj5jzKuQmQ9HpBWq00Q
提取码:rdcv

2. 程序代码

2.1 StatePartitioner程序

package com.niit.covid.MeiTuanstater;

import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;

import java.util.HashMap;

/**
 * @author:严同学
 * @date: 2022年08月12日 14:55
 * @desc:
 */
public class StatePartitioner extends Partitioner<Text,Text> {

    //模拟美国各州的数据字典,实际中可以从redis进行读取加载,如果数据量不大,也可以创建数据集合保存
    public static HashMap<String,Integer> stateMap= new HashMap<String, Integer>();
    static {
        stateMap.put("广西壮族自治区",0);
        stateMap.put("广东省",1);
        stateMap.put("云南省",2);
        stateMap.put("湖南省",3);
        stateMap.put("贵州省",4);
    }

    /**
     *  todo 自定义分区器中分区规则的实现方法  只要getPartition返回的int一样,数据就会被分到同一个分区
     *  所谓同一个分区指的是数据到同一个reducetask处理
     *  k:state省份 -------字符串===Text
     *  v:这一行内容  -------字符串整体,无需封装==Text
     */
    @Override
    public int getPartition(Text key, Text value, int i) {
        Integer code = stateMap.get(key.toString());  //
        if(code !=null ){
            return code;
        }else{
            return 5;// 其他省份 放到 第六个分区文件
        }

    }
}

2.2 mapper程序

package com.niit.covid.MeiTuanstater;

import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.IOException;

/**
 * @author:严同学
 * @date: 2022年08月12日 15:07
 * @desc:
 */
public class StatePartitionMapper extends Mapper<LongWritable, Text,Text,Text> {

    Text outKey = new Text();
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //1、切割,为了拿到州
        String[] lines = value.toString().split(",");

        //2、以省份作为key,参与分区,通过自定义分区,同一个省份的数据到同一个分区同一个reducetask处理
        String state = lines[0];

        //3、赋值
        outKey.set(state);

        //4、写出去
        context.write(outKey,value);


    }

}

2.3 reduce程序

package com.niit.covid.MeiTuanstater;

/**
 * @author:严同学
 * @date: 2022年08月12日 15:10
 * @desc:
 */

import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;

import java.io.IOException;

/**
 * 因为state在那一行数据里,再输出那行数据没有意义,可以使用nullWritable
 */
public class StatePartitionReducer extends Reducer<Text, Text,Text, NullWritable> {

    @Override
    protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
        for (Text value : values){
            context.write(value,NullWritable.get());
        }
    }
}

2.4 Driver程序

package com.niit.covid.MeiTuanstater;

import com.niit.covid.statePartition.StatePartitionMapper;
import com.niit.covid.statePartition.StatePartitionReducer;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
 * @author:严同学
 * @date: 2022年08月12日 15:14
 * @desc:
 */
public class StatePartitionDriver {

    public static void main(String[] args) throws Exception {

        //创建驱动类
        Configuration conf = new Configuration();

        //构造job作业的实例,参数(配置对象,job名字)
        Job job = Job.getInstance(conf, StatePartitionDriver.class.getSimpleName());

        //设置mr程序运行的主类
        job.setJarByClass(StatePartitionDriver.class);

        //设置本次mr程序的mapper类型、reducer类型
        job.setMapperClass(StatePartitionMapper.class);
        job.setReducerClass(StatePartitionReducer.class);

        //指定mapper阶段输出的key value数据类型
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        //指定reducer阶段输出的key value数据类型,也是mr程序最终的输出数据类型
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);

        //todo 设置程序Partition类; 注意:分区组件能够生效的前提是MapReduce程序中的reduceTask的个数 >=2
        /**
         * 探究: reducetask个数和分区个数之间的关系
         * 正常情况下: reducetask个数 == 分区个数
         * 特殊情况下: reducetask个数 > 分区个数  ====> 程序可以运行,但多有空文件产生,浪费性能
         *            reducetask个数 < 分区个数  ====> 程序直接报错:非法分区
         */
        job.setPartitionerClass(StatePartitioner.class);
        job.setNumReduceTasks(6);// 5 是已经在StatePartitioner已经分好的区 1:其他州的数据  5 + 1 = 6


        //配置本次作业的输入数据路径和输出数据路径
        Path inputPath = new Path("input/covid/meituan");
        Path outputPath = new Path("output/covid/meituanpartitoner");

        //todo 默认组件 TextInputFormat TextOutputFormat
        FileInputFormat.setInputPaths(job, inputPath);
        FileOutputFormat.setOutputPath(job,outputPath);

        //todo 判断输出路径是否已经存在,如果已经存在,先删除
        FileSystem fs = FileSystem.get(conf);
        if(fs.exists(outputPath)){
            fs.delete(outputPath,true); //递归删除
        }

        //最终提交本次job作业
        //job.submit();
        //采用waitForCompletion提交job,参数表示是否开启实时监视追踪作业的执行情况
        boolean resultFlag = job.waitForCompletion(true);
        //退出程序 和job结果进行绑定, 0是正常退出,1是异常退出
        System.exit(resultFlag ? 0: 1);

    }

}

mapreduce运行结果

分区结果
运行完成即可得出六个分区结果,程序将相同的省份分在一起,其他省份放在第六分区

至此apreduce分区就结束了,有疑问的到评论区讨论!😚😚😚

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要按年份分区,可以使用自定义Partitioner类来实现。以下是一个例子,假设您有一个输入文件,其中每行包含一个日期和一些数据: ``` 2019-01-01 10 2018-12-31 20 2019-02-01 30 ``` 您需要将此数据按照年份分区,并且每个分区的数据都应该按照日期排序。为此,您可以编写以下Partitioner类: ```java import org.apache.hadoop.mapreduce.Partitioner; public class YearPartitioner extends Partitioner<Text, IntWritable> { @Override public int getPartition(Text key, IntWritable value, int numPartitions) { String year = key.toString().substring(0, 4); return Integer.parseInt(year) % numPartitions; } } ``` 这个Partitioner类会从键中提取年份,并将其用作分区键。这样,相同年份的数据都会被分配到相同的分区中。在这里,我们使用了numPartitions参数,该参数由Hadoop运行时环境传递,表示总分区数。这意味着,如果您的集群有10个节点,那么您会得到10个分区。 为了确保每个分区内的数据按照日期排序,您可以在Mapper中对键进行排序: ```java import java.io.IOException; import org.apache.hadoop.io.IntWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.mapreduce.Mapper; public class YearMapper extends Mapper<Object, Text, Text, IntWritable> { private Text year = new Text(); private IntWritable data = new IntWritable(); @Override public void map(Object key, Text value, Context context) throws IOException, InterruptedException { String[] parts = value.toString().split(" "); year.set(parts[0]); data.set(Integer.parseInt(parts[1])); context.write(year, data); } } ``` 在这个Mapper中,我们将键设置为日期,值设置为数据。这样,对于相同年份的数据,它们会按照日期排序。最后,我们将分区键和数据写入输出。 接下来,您需要在Job中设置Partitioner类: ```java 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.FileInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; public class YearJob { public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "year partitioning"); job.setJarByClass(YearJob.class); job.setMapperClass(YearMapper.class); job.setPartitionerClass(YearPartitioner.class); job.setNumReduceTasks(4); // set the number of reducers job.setOutputKeyClass(Text.class); job.setOutputValueClass(IntWritable.class); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); System.exit(job.waitForCompletion(true) ? 0 : 1); } } ``` 在这个Job中,我们设置了Partitioner类,并将其设置为使用4个reduce任务。这意味着,我们将得到4个输出文件,每个文件包含一个分区的数据。最后,我们运行Job并等待它完成。 以上就是按年份分区MapReduce程序的实现方法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

严同学正在努力

老板发财!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值