MR实现将同一个key的内容分配到同一个输出文件

本文介绍了一种在Hadoop MapReduce中实现根据特定Key进行数据分组输出的方法,通过使用MultiplyOutputs类来确保所有拥有相同Key的数据都被写入到同一个文件中,从而简化了后续的数据处理流程。

MapReduce程序默认的输出文件个数:

首先,根据setNumReduceTasks(int num)这个方法,

其次,根据Map的输出文件个数。

一般情况下,同一个key的数据,可能会被分散到不同的输出文件中。倘若我们要对某一个特定的key的所有value值进行遍历,则需要将包含该key的所有文件作为输入文件。当数据比较庞大时,这样的操作会浪费资源。如果同一个Key的所有的value值都会被分配到同一个文件中,就会比较理想。

在Hadoop-core包中,有个类MultiplyOutputs可以实现以上功能(其实就是在reduce中加一两句话,其他不变)。代码如下:

 

package io;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.conf.Configured;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.mapreduce.lib.output.MultipleOutputs;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;

public class MultipleOut extends Configured implements Tool {

    static class Map extends Mapper<LongWritable, Text, Text, Text> {
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            String line = value.toString();
            int index = line.indexOf(" ");
            if (index != -1) {
                context.write(new Text(line.substring(0, index)),
                        new Text(value.toString().substring(index + 1)));
            }
        }
    }


// 只需要在reduce中添加几句代码,其他部分不需要改动
    static class Reduce extends Reducer<Text, Text, Text, Text> {
        @Override
        protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
            MultipleOutputs mo = new MultipleOutputs(context);
            for (Text val : values) {
             //key.toString():表示输出文件名以Key命名,注意是相对路径
                mo.write(key, val, key.toString() + "/");
            }
            //一定要close,否则无数据
            mo.close();
        }
    }

    @Override
    public int run(String[] strings) throws Exception {
        String path = "你的输入路径";
        if (strings.length != 1) {
            System.out.println("input:" + path);
            System.out.print("arg:<out>");
            return 1;
        }
        Configuration conf = getConf();
        Job job = new Job(conf, "MultipleOut");
        job.setJarByClass(MultipleOut.class);
        job.setMapperClass(Map.class);
        job.setReducerClass(Reduce.class);

        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(Text.class);

        FileInputFormat.addInputPath(job, new Path(path));
        FileOutputFormat.setOutputPath(job, new Path(strings[0]));

        boolean success = job.waitForCompletion(true);
        return success ? 0 : 1;
    }

    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        int rst = ToolRunner.run(conf, new MultipleOut(), args);
        System.exit(rst);
    }
}

 结果如图片所示,目录1,2,3,4,5是五个Key。

 



 

### 使用MapReduce实现文件合并的大数据编程示例 #### 实现概述 为了展示如何利用 MapReduce 进行文件合并,下面提供了一个简单的例子来说明这一过程。此案例主要关注于两个不同源文件内容合并,并假设这些文件具有共同字段用于连接。 #### 编写Mapper类 在 `setup()` 函数里加载需要对比的小型静态文件到内存中以便后续映射阶段使用。对于每一个输入记录,在 `map` 函数内部执行分割操作以提取键值对;这里的关键在于确保来自不同文件但拥有相同标识符(即要被联接的列)会被分配给相同的 Reducer 处理单元[^3]。 ```java public class FileMergeMapper extends Mapper<LongWritable, Text, Text, IntWritable> { private List<String> staticFileContent = new ArrayList<>(); @Override protected void setup(Context context) throws IOException, InterruptedException { // 假设已经有一个辅助函数可以从指定路径读取小型静态文件内容至列表变量staticFileContent中 readStaticFileIntoMemory(); } public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); String[] fields = line.split(","); for (String item : staticFileContent){ if(item.equals(fields[0])){ // 假定第一个字段是要匹配的关键字 context.write(new Text(line), new IntWritable(1)); break; } } } } ``` #### 编写Reducer类 接收由 Mapper 发送过来的数据流之后,Reducer 的职责是对共享同一 Key 的所有 Value 集合进行聚合处理。在这个特定的例子当中,Reducer 将会把从不同源头传来的记录组合起来形成最终输出结果集的一部分[^2]。 ```java public class FileMergeReducer extends Reducer<Text, IntWritable, NullWritable, Text> { public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { StringBuilder combinedRecord = new StringBuilder(); Iterator<IntWritable> iterator = values.iterator(); while(iterator.hasNext()){ int count = iterator.next().get(); combinedRecord.append(key).append("\t").append(count); if(iterator.hasNext()) combinedRecord.append(", "); } context.write(NullWritable.get(), new Text(combinedRecord.toString())); } } ``` #### 主程序配置Job参数 最后一步是在主应用程序中设置好 Job 参数,指明使用的 Mapper 和 Reducer 类以及输入/输出路径等必要信息[^1]。 ```java Configuration conf = new Configuration(); Job job = Job.getInstance(conf, "file merge with MR"); job.setJarByClass(FileMergeDriver.class); // 设置Mapper和Reducer job.setMapperClass(FileMergeMapper.class); job.setCombinerClass(IntSumReducer.class); // 如果适用的话可以加一个combiner优化性能 job.setReducerClass(FileMergeReducer.class); // 定义输出Key和Value类型 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); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值