数据倾斜的问题其实就是map端输出的数据分发到reduce端不均匀,使得在reduce端有的reducer处理的数据量过大,有的reducer处理的数据量过少,造成数据倾斜于一方。
解决方法是在map端进行——》map side join操作
代码如下:
package com.jym.hadoop.mr.mapsidejoin;
import java.io.BufferedReader; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.URI; 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的方法来解决的, * 在该程序中省略了reduce部分 * */ public class MapSideJoin { static class MapSideJoinMapper extends Mapper<LongWritable, Text, Text, NullWritable> { //用一个HashMap来保存产品信息表 Map<String,String> pdInfo = new HashMap<>(); Text k = new Text(); /** * 通过阅读父类Mapper的源码,发现 * setup方法是在MapTask处理数据之前调用一次 * 所以可以用来做一些初始化工作 * */ @Override protected void setup (Mapper<LongWritable, Text, Text, NullWritable>.Context context) throws IOException, InterruptedException { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("G:/wordcount/mapjoincache/pdts.txt")));
//因为已经将文件加载到本地缓存中,也即文件就在本地,所以直接写其文件名就好 String line; while(StringUtils.isNotEmpty(line = br.readLine())) { String[] fields = line.split(","); pdInfo.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 = pdInfo.get(fields[1]); k.set(orderLine+"\t"+pdName); context.write(k, NullWritable.get()); } } public static void main(String[] args) throws Exception { 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("G:/wordcount/mapjoininput")); FileOutputFormat.setOutputPath(job, new Path("G:/wordcount/mapjoinoutput1")); //指定需要缓存一个文件到所有的MapTask运行节点工作目录————(此处指的是DistributedCache组件) /**job.addArchiveToClassPath(archive);*/ //缓存jar包到task运行节点的classpath中 /**job.addCacheArchive(uri);*/ //缓存压缩包文件到task运行节点的工作目录中 /**job.addCacheFile(uri);*/ //缓存普通文件到task运行节点的工作目录中 /**job.addFileToClassPath(file);*/ //缓存普通文件到task运行节点的classpath中 //将产品信息表文件缓存到task工作节点的工作目录中 job.addCacheFile(new URI("file:/G:/wordcount/mapjoincache/pdts.txt")); //此处是添加的一个本地文件 //map端join的逻辑不需要reduce阶段,设置reduceTask数量为0; job.setNumReduceTasks(0); boolean res = job.waitForCompletion(true); System.exit(res?0:1); } }
测试数据为:
测试结果为: