Map端的join -- 商品跟订单合并

参考博客:
https://my.oschina.net/leejun2005/blog/111963

需求:
之所以存在reduce join,是因为在map阶段不能获取所需要的join字段,即同一个key对应的字段可能位于不同的map中。但是Reduce side join 是非常低效的,因为shuffle阶段要经过大量的数据传输。
解决办法:
Map side join 是针对一下场景进行优化:两个待连接的表中,有一个表非常大,而另一个表非常小,以至于小表可以直接存放到内存中。这样,我们可以将小表复制多份,让每个map task 内存中存在一份(比如存放到hash table中)然后只扫描大表:对于大表中的每一条记录key/value,在hash table中查找是否有相同的key 的记录,如果有,则连接后输出。为了支持文件的复制,Hadoop提供了一个类DistributedCache。

DistributedCache是hadoop框架提供的一种机制,可以将job指定的文件,在job执行前,先行分发到task执行的机器上,并有相关机制对cache文件进行管理.

主要的注意事项有:

1.DistributedCache只能应用于分布式的情况,包括伪分布式,完全分布式.有些api在这2种情况下有移植性问题.

2.需要分发的文件,必须提前放到hdfs上.默认的路径前缀是hdfs://的,不是file://

3.需要分发的文件,最好在运行期间是只读的.

4.不建议分发较大的文件,比如压缩文件,可能会影响task的启动速度.

Hadoop DistributedCache详解 :
http://dongxicheng.org/mapreduce-nextgen/hadoop-distributedcache-details/

package com.thp.bigdata.mapSideJoin;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

/**
 * 在Map端实现join,不需要在join端实现join,这个可以减少数据倾斜
 * 适用于关联表中有小表的情况
 * 可以将小表分发到所有的map节点,这样,map节点就可以在本地对自己所读到的大表数据进行join并输出最终结果
 * 可以大大提高join操作的并发操作,加快处理速度
 * 
 * 【博客链接】
 * 		http://dongxicheng.org/mapreduce-nextgen/hadoop-distributedcache-details/
 * 		https://blog.csdn.net/xiaolang85/article/details/11782539
 * 
 * @author 汤小萌
 *
 */
public class MapSideJoin {
	
	static class MapSideJoinMapper extends Mapper<LongWritable, Text, Text, NullWritable> {
		// 使用一个HashMap来加载保存产品信息表
		Map<String, String> pdInfoMap = new HashMap<String, String>();
		
		Text k = new Text();
		
		// 在maptask处理数据之前调用一次,可以用来做一些初始化的操作
		@Override
		protected void setup(Context context)
				throws IOException, InterruptedException {
			// 已经加载到当前 maptask的工作目录下了,可以直接写文件名读取文件
			// hadoop 所有读取数据都是使用的utf-8
			BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("pdts.txt")));
			String line;
			while(StringUtils.isNotEmpty(line = br.readLine())) {
				String[] fields = line.split("\t");
				pdInfoMap.put(fields[0], fields[1]);
			}
			br.close();
		}
		
		// 由于已经持有完整的产品信息表,所以可以直接在map方法中就能实现join的逻辑
		@Override
		protected void map(LongWritable key, Text value, Context context)
				throws IOException, InterruptedException {
			String orderLine = value.toString();
			String[] fields = orderLine.split("\t");
			String pdName = pdInfoMap.get(fields[1]);
			k.set(orderLine + "\t" + pdName);
			context.write(k, NullWritable.get());
		}
		
		
		public static void main(String[] args) throws IOException, URISyntaxException, ClassNotFoundException, InterruptedException {
			Configuration conf = new Configuration();
			
			Job job = Job.getInstance(conf);
			
			
			job.setJarByClass(MapSideJoin.class);
			
			job.setMapperClass(MapSideJoinMapper.class);
			
			job.setOutputKeyClass(Text.class);
			job.setOutputValueClass(NullWritable.class);
			
			
			FileInputFormat.setInputPaths(job, new Path("F:/mapSideJoin/input"));
			FileOutputFormat.setOutputPath(job, new Path("F:/mapSideJoin/output"));
			
			// 指定需要缓存一个文件到所有的maptask运行节点工作目录
			/* job.addArchiveToClassPath(archive); */// 缓存jar包到task运行节点的classpath中
			/* job.addFileToClassPath(file); */// 缓存普通文件到task运行节点的classpath中
			/* job.addCacheArchive(uri); */// 缓存压缩包文件到task运行节点的工作目录
			/* job.addCacheFile(uri) */// 缓存普通文件到task运行节点的工作目录
			
			// 将产品表文件缓存到task工作节点的工作目录中去
			job.addCacheFile(new URI("file:/F:/mapSideJoin/pdts.txt"));
			
			// map端的join的逻辑不需要reduce阶段,设置reducetask数量为0,不设置是会出现问题的
			job.setNumReduceTasks(0);
			
			boolean res = job.waitForCompletion(true);
			
			System.exit(res ? 0 : 1);
			
		}
		
	}
	
	
}

我在运行的时候会出现这个问题:
hadoop在本地调试出现的错误——创建符号链接失败

failed 1 with: CreateSymbolicLink error (1314): ???????????

网上有好几种这个问题的解决办法,我是在启动eclipse的时候直接以管理员的身份启动的,这个权限问题就可以解决。

还有一个问题就是编码的问题,我的pdts.txt文件里面出现了中文,但是我使用的是记事本编辑的,默认保存的编码不是uft-8,所以最终在输出文件的时候会出现乱码,因为hadoop所有的文件处理都是使用utf-8,我们在保存文件的时候,将编码格式改为utf-8就可以解决乱码问题。

代码地址:
https://gitee.com/tanghongping/hadoopMapReduce/tree/master/src/com/thp/bigdata/mapSideJoin

猜你喜欢

转载自blog.csdn.net/qq_38200548/article/details/84229548