Reduce连接(reduce-side joins)

如果没有一个 map-side join 技术适合我们的数据集,那么就需要在 MapReduce 中使用 shuffle 来排序和连接两个数据集。这称为 reduce-side joins,也叫”重分区连接”。

【例】基本的重分区连接(repartition join/reduce-side join)

    重分区连接是 reduce 端连接。它利用 MapReduce 的排序-合并机制来分组数据。它只使用一个单独的 MapReduce 任务,并支持 N-way join,这里 N 指的是被连接的数据集的数量。

    Map 阶段负责从多个数据集中读取数据,决定用于每条记录的连接值,并将连接值作为输出 key。输出 value 则包含在 reduce 阶段所合并的数据集的数据。

    Reduce 阶段,一个 reduce 接收 map 函数传来的每一个 join key 的所有输出值,并将数据分为 N 个分区,这里 N 指的是被连接的数量。在该 reducer 接收到用于该 join value 的所有输入记录并在内存中对 他们分区之后,它对所有分区执行一个笛卡尔积(Cartersian product),并输出每个 join 的结果。下图演示 了重分区 join:

a319e6871e0624258b1d488c15707874d2c.jpg    要支持这个技术,MapReduce 代码需要满足以下条件:

    ■它需要支持多个 map 类,每个 map 处理一个不同的输入数据集。这是通过使用 MultipleInputs 来 完成的。

    ■ 它需要一个方式来标记由 mapper 所输出的记录,这样它们才能与它们原始的数据集相关联。这 里我们将使用 htuple 项目来简化 MapReduce 中组合数据(composite data)的处理。

    文件 users.txt:

    6f283a1525680e58ab121336daa6c01024a.jpg

9aa040028822492e5019ad98454893c09a4.jpg

package com.edu360.mapreduce;

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.MultipleInputs;
import org.apache.hadoop.mapreduce.lib.input.TextInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.Tool;
import org.apache.hadoop.util.ToolRunner;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * java 一出 谁与争锋
 * <p>
 * .............................................
 * 佛祖保佑             永无BUG
 *
 * @Auther: caozhan
 * @Date: 2018/11/4 13:11
 * @Description:
 */
public class RepartitionJoin extends Configured implements Tool {
    //声明代表不同数据表的标志变量
    public static final int USERS=0;//代表记录来自用户信息表
    public static final int USER_LOGS=1;//代表记录来自用户日志记录
    //处理来自 users.txt的输入记录
    public static  class UserMap extends Mapper<LongWritable, Text,Text, TupleWritable>{
        @Override
        protected void map(LongWritable key, Text value, Context context)
                throws IOException, InterruptedException {
            //提取用户名
            String username = value.toString().split("\t")[0];
            TupleWritable outputValue = new TupleWritable();
            outputValue.setTable(USERS);
            outputValue.setRecord(value.toString());
            context.write(new Text(username),outputValue);
        }
    }
    //处理来自logs.txt 的输入记录
    public static class UserLogMap extends Mapper<LongWritable,Text,Text, TupleWritable>{
        @Override
        protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
            //提取用户名
            String username=value.toString().split("\t")[0];
            TupleWritable outValue = new TupleWritable();
            outValue.setTable(USER_LOGS);
            outValue.setRecord(value.toString());
            context.write(new Text(username),outValue);
        }
    }
    public static  class ReduceJoin extends Reducer<Text,TupleWritable,Text,Text>{
        private List<String> users,userLogs;
        private Text keyInfo = new Text();
        private Text valueInfo = new Text();

        @Override
        protected void reduce(Text key, Iterable<TupleWritable> values, Context context)
                throws IOException, InterruptedException {
            users = new ArrayList<>();
            userLogs=new ArrayList<>();

            //解析从mapper收到的输入,分别放入相应的list集合中
            for(TupleWritable tupleWritable:values){
                System.out.println("Tuple:"+tupleWritable);
                switch (tupleWritable.getTable()){
                    case USERS:{
                        users.add(tupleWritable.getRecord());
                        break;
                    }
                    case USER_LOGS:{
                        userLogs.add(tupleWritable.getRecord());
                        break;
                    }
                }
            }
            //笛卡尔乘机
            for(String user:users){
                for(String userLog:userLogs){
                    keyInfo.set(user);
                    valueInfo.set(userLog);
                    context.write(keyInfo,valueInfo);
                }
            }
        }
    }
    @Override
    public int run(String[] args) throws Exception {
        Path usersPath = new Path(args[0]);
        Path userLogsPath = new Path(args[1]);
        Path outputPath = new Path(args[2]);
        Job job=Job.getInstance(getConf(),"Simple Redpartition Jii");
        job.setJarByClass(ReduceJoin.class);
        
        //分别为不同的输入文件指定不同的inputformat
        MultipleInputs.addInputPath(job,usersPath, TextInputFormat.class,UserMap.class);
        MultipleInputs.addInputPath(job,userLogsPath,TextInputFormat.class,UserLogMap.class);
        
        job.setReducerClass(ReduceJoin.class);
        
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(TupleWritable.class);
        
        job.setOutputKeyClass(Text.class);
        job.setMapOutputValueClass(Text.class);

        FileOutputFormat.setOutputPath(job,outputPath);
        return job.waitForCompletion(true)?0:1;
    }

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

发生的ERRO:

a6315ff31d31ae4ce1da2492aadc2769381.jpg

查看你的map和reduce 是不是没有被static修饰,拿不到实例类。

4377a8592d31e52084e7ac8f1d5f24d0c20.jpg

最终结果

转载于:https://my.oschina.net/u/4009325/blog/2636322

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值