实验8 MapReduce-Join操作

8.3.1概述

对于RDBMS中的Join操作,写SQL的时候要十分注意细节,稍有差池就会耗时很长造成很大的性能瓶颈,而在Hadoop中使用MapReduce框架进行Join操作时同样耗时,但是由于Hadoop的分布式设计理念的特殊性,因此对于这种Join操作也具备了一定的特殊性。

8.3.2原理

使用MapReduce实现Join操作有多种实现方式:

在Reduce端连接为最常见的模式:

Map端的主要工作:为来自不同表(文件)的key/value对打标签以区别不同来源的记录,然后用连接字段作为key,其余部分和新加的标志作为value,最后进行输出。

Reduce端的主要工作:在Reduce端以连接字段作为key的分组已经完成,我们只需要在每一个分组当中将那些来源于不同文件的记录(在map阶段已经打上标志)分开,最后进行笛卡尔就可以了。


在Map端进行连接:

使用场景:一张表十分小,一张表很大。

用法:在提交作业的时候首先将小表文件放到该作业的DistributedCache中,其次从DistributedCache中取出该小表进行Join key/value解释分割放到内存中(可以放大Hash Map等容器中)。最后扫描大表,看大表中的每条记录的Join key/value值是否能够在内存中找到相同Join key记录,如果有则直接输出结果。


SemiJoin:

SemiJoin就是所谓的半连接,其实仔细一看就是Reduce Join的一个变种,就是在map端过滤掉一些数据,在网络中之传输参与连接的数据,不参与连接的数据不必在网络中传输,从而减少了shuffle的网络传输量,使整体效率提高,其他思想和Reduce Join是一模一样的。也就是将小表中参与Join的key单独抽出来通过DistributedCache分发到相关节点,然后将其取出放到内存中(可以放到HashSet中),在map阶段扫描连接表,将Join key不再内存HashSet中的记录过滤掉,让那些参与Join的记录通过shuffle传输到Reduce端进行Join操作,其他和Reduce Join都一样。


8.3.3实验

完整代码:

package lab8;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Partitioner;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.input.FileSplit;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;

public class MRJoin {
	public static class MR_Join_Mapper extends Mapper<LongWritable, Text, TextPair, Text>{
		@Override
		protected void map(LongWritable key, Text value,Context context)throws IOException,InterruptedException{
			//获取输入文件的全路径和名称
			String pathName=((FileSplit)context.getInputSplit()).getPath().toString();
			if(pathName.contains("data.txt")) {
				String values[]=value.toString().split("\t");
				if(values.length<3) {
					//data数据格式不规范,字段小于3,抛弃数据
					return;
				}else {
					//数据格式规范,区分标识为1
					TextPair tp=new TextPair(new Text(values[1]),new Text("1"));
					context.write(tp, new Text(values[0]+"\t"+values[2]));
					
				}
			}
			if(pathName.contains("info.txt")) {
				String values[]=value.toString().split("\t");
				if(values.length<2) {
					//info数据格式不规范,字段小于2,抛弃数据
					return;
				}else {
					//数据格式规范,区分标识为0
					TextPair tp=new TextPair(new Text(values[0]),new Text("0"));
					context.write(tp, new Text(values[1]));
					
				}
			}
		}
	}
	public static class MR_Join_Partitioner extends Partitioner<TextPair,Text>{
		@Override
		public int getPartition(TextPair key,Text value,int numPartition) {
			return Math.abs(key.getFirst().hashCode()*127)%numPartition;
		}
	}
	public static class MR_Join_Comparator extends WritableComparator{
		public MR_Join_Comparator() {
			super(TextPair.class,true);
		}
		public int compare(WritableComparable a, WritableComparable b) {
			TextPair t1=(TextPair)a;
			TextPair t2=(TextPair)b;
			return t1.getFirst().compareTo(t2.getFirst());
		}
	}
	public static class MR_Join_Reduce extends Reducer<TextPair,Text,Text,Text>{
		@Override
		protected void reduce(TextPair key,Iterable<Text> values,Context context)throws IOException,InterruptedException{
			Text pid=key.getFirst();
			String desc=values.iterator().next().toString();
			while(values.iterator().hasNext()) {
				context.write(pid,new Text(values.iterator().next().toString()+"\t"+desc));
			}
		}
	}
	public static void main(String agrs[])throws IOException,InterruptedException,ClassNotFoundException{
		Configuration conf=new Configuration();
		GenericOptionsParser parser =new GenericOptionsParser(conf,agrs);
		String[] otherArgs=parser.getRemainingArgs();
		if(agrs.length<3) {
			System.err.println("Usage:MRJoin<in_path_one><in_path_two><output>");
			System.exit(2);
		}
		Job job=new Job(conf,"MRJoin");
		//设置运行的Job
		job.setJarByClass(MRJoin.class);
		//设置Map相关内容
		job.setMapperClass(MR_Join_Mapper.class );
		job.setMapOutputKeyClass(TextPair.class);
		job.setMapOutputValueClass(Text.class );
		
		job.setPartitionerClass(MR_Join_Partitioner.class );
		//在分区之后按照指定条件分组
		job.setGroupingComparatorClass(MR_Join_Comparator.class );
		
		job.setReducerClass(MR_Join_Reduce.class );
		//设置Reducer的输出
		job.setOutputKeyClass(Text.class );
		job.setOutputValueClass(Text.class );
		FileInputFormat.addInputPath(job, new Path(otherArgs[0]));
		FileInputFormat.addInputPath(job,new Path(otherArgs[1]));
		FileOutputFormat.setOutputPath(job, new Path(otherArgs[2]));
		//执行知道结束就退出
		System.exit(job.waitForCompletion(true)?0:1);
		
	}
}


class TextPair implements WritableComparable<TextPair>{
	private Text first;
	private Text second;
	public TextPair() {
		set(new Text(),new Text());
	}
	public TextPair(String first,String second) {
		set(new Text(first),new Text(second));
	}
	public TextPair(Text first,Text second) {
		set(first,second);
	}
	public void set(Text first,Text second) {
		this.first=first;
		this.second=second;
	}
	public Text getFirst() {
		return first;
	}
	public Text getSecond() {
		return second;
	}
	public void write(DataOutput out)throws IOException{
		first.write(out);
		second.write(out);
	}
	public void readFields(DataInput in)throws IOException{
		first.readFields(in);
		second.readFields(in);
	}
	public int compareTo(TextPair tp) {
		int cmp=first.compareTo(tp.first);
		if(cmp!=0) {
			return cmp;
		}
		return second.compareTo(tp.second);
	}
}

执行方式一:

hadoop jar MRJoin.jar lab8.MRJoin /input1/data.txt /input2/info.txt /output

注:提交到hadoop执行时必须打开HDFS和YARN:start-dfs.sh,start-yarn.sh

执行方式二:

在eclipse中本地执行(默认)确保src文件夹下面没有hdfs-site.xml和core-site.xml

配置参数:./input1/data.txt ./input2/info.txt ./output

注:input1和input2文件夹和src文件夹处在同一级,如果src文件夹下有hadoop的相关配置文件,那么eclipse会把任务提交到HDFS中运行

文件data.txt:

201001    1003    abc
201002    1005    def
201003    1006    ghi
201004    1003    jkl
201005    1004    mno
201006    1005    pqr

文件info.txt:

1003    kaka
1004    da
1005    jue
1006    zhao

运行结果:

1003    201004    jkl    kaka
1003    201001    abc    kaka
1004    201005    mno    da
1005    201006    pqr    jue
1005    201002    def    jue
1006    201003    ghi    zhao

程序执行过程分析:

map()后:

[1003 1] , 201001 abc

[1003 1] , 201004 jkl

...

[1003 0] , kaka

...

以上的三个<k,v>会发送到同一个Reducer

reduce处理过程:

以上三个<k,v>会被汇聚成一组(GroupingComparator:具有相同key.first的<k,v>一起处理),然后被排序为(因为TextPair实现了WritableComparable接口):

[1003 0] , kaka

[1003 1] , 201001 abc

[1003 1] , 201004 jkl

该组中desc=1003,第2和第3个数据的value会被分别遍历


实验中可能的错误:

Mapreduce Error: Type mismatch in key from map


java.lang.Exception: java.io.IOException: Type mismatch in key from map: expected lab8.TextPair, received org.apache.hadoop.io.LongWritable
    at org.apache.hadoop.mapred.LocalJobRunner$Job.runTasks(LocalJobRunner.java:462)
    at org.apache.hadoop.mapred.LocalJobRunner$Job.run(LocalJobRunner.java:522)
Caused by: java.io.IOException: Type mismatch in key from map: expected lab8.TextPair, received org.apache.hadoop.io.LongWritable
    at org.apache.hadoop.mapred.MapTask$MapOutputBuffer.collect(MapTask.java:1069)
    at org.apache.hadoop.mapred.MapTask$NewOutputCollector.write(MapTask.java:712)
    at org.apache.hadoop.mapreduce.task.TaskInputOutputContextImpl.write(TaskInputOutputContextImpl.java:89)
    at org.apache.hadoop.mapreduce.lib.map.WrappedMapper$Context.write(WrappedMapper.java:112)
    at org.apache.hadoop.mapreduce.Mapper.map(Mapper.java:124)
    at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:145)
    at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:784)
    at org.apache.hadoop.mapred.MapTask.run(MapTask.java:341)
    at org.apache.hadoop.mapred.LocalJobRunner$Job$MapTaskRunnable.run(LocalJobRunner.java:243)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
17/11/12 12:45:08 INFO mapreduce.Job: Job job_local1488163749_0001 running in uber mode : false
17/11/12 12:45:08 INFO mapreduce.Job:  map 0% reduce 0%
17/11/12 12:45:08 INFO mapreduce.Job: Job job_local1488163749_0001 failed with state FAILED due to: NA
17/11/12 12:45:08 INFO mapreduce.Job: Counters: 0

出现这个错误的原因:

1.map和reduce中的输入输出格式不对。

你看一下你map的输出和reduce的输入是不是对应的,然后看看你的map和reduce里的参数和下面的是不是设置的一样。
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(IntWritable.class);
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(IntWritable.class);

2.新旧api混用。你的map()方法 没有按新api写, 结果系统不认为它是一个重载,而是一个新方法,不会被调用。

解决办法:在的map(), reduce()前面加上@Override,并按照新的API来写map(), reduce()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值