使用MapReduce求解join问题
来源:互联网 发布:网络摄像头怎样安装 编辑:程序博客网 时间:2024/06/07 10:02
背景
有两张表,以文件的形式存储在hdfs中,如下:
学生基本信息001 jyw nan002 lq nv003 jl n学生考试成绩信息001 english 90001 math 92002 chinese 99
现在需要求解每一个学生参加的考试的成绩以及学生信息
sql就类似于:
select stu.*,score.grade from stu join score on stu.id = score.stuid
使用MapReduce解决这个问题
第一种方法,将id作为map输出的key,这样读出来的信息,id相同的就会被分配到同一个组中,然后就可以在reduce中进行关联
public class Demo1 { public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException { Configuration configuration = new Configuration(); Job job = Job.getInstance(configuration); job.setJarByClass(Demo1.class); job.setMapperClass(Demo1Mapper.class); job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(InfoBean.class); job.setReducerClass(Demo1Reducer.class); job.setOutputKeyClass(InfoBean.class); job.setOutputValueClass(NullWritable.class); FileInputFormat.setInputPaths(job,new Path("F:\\wc\\input")); FileOutputFormat.setOutputPath(job,new Path("F:\\wc\\output")); job.waitForCompletion(true); }}class Demo1Mapper extends Mapper<LongWritable,Text,Text,InfoBean>{ InfoBean infoBean = new InfoBean(); Text k = new Text(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //每次读取一个文件的时候,应该先要能拿到这个文件的文件名 // 从而区分开学生信息以及考试信息 FileSplit inputSplit = (FileSplit) context.getInputSplit(); String fileName = inputSplit.getPath().getName(); String[] split = value.toString().split("\t"); //学生的基本信息 if(fileName.startsWith("stu")){ infoBean.setInfoBean(split[0],split[1],split[2],"","","0"); }else{ infoBean.setInfoBean(split[0],"","",split[1],split[2],"1"); } k.set(split[0]); context.write(k,infoBean); }}class Demo1Reducer extends Reducer<Text,InfoBean,InfoBean,NullWritable>{ @Override protected void reduce(Text key, Iterable<InfoBean> values, Context context) throws IOException, InterruptedException { InfoBean stuInfo = new InfoBean(); ArrayList<InfoBean> scoreBeans = new ArrayList<InfoBean>(); //需要先将学生信息与考试信息区分开来 for(InfoBean infoBean:values){ if(infoBean.getFlag().equals("0")){ stuInfo.setInfoBean(infoBean.getId(),infoBean.getName(),infoBean.getSex(),"","",""); }else{ scoreBeans.add(new InfoBean(infoBean.getId(),"","",infoBean.getCname(),infoBean.getScore(),"")); } } //将学生信息和考试信息join起来 for(InfoBean infoBean:scoreBeans){ infoBean.setName(stuInfo.getName()); infoBean.setSex(stuInfo.getSex()); context.write(infoBean,NullWritable.get()); } }}
这种方法的一个缺点:
在reduce端很有可能会造成数据的倾斜 ,有的学生可能修了很多的课,但是有的学生可能这一学期并没有修什么课(例如大四的学生,基本不会去上课),这就造成了有的reduce端任务繁重(因为有很多学生的选课信息被发送到了这个reduce端),而有的reduce端就会很清闲,因为获取到的学生可能并没有选课
第二种方法使用map端的join
在这里我们可以使用DistributeCache先将较小的那个文件加载到map端(读取到内存中)太大的时候也可以考虑使用其它的存储方式,例如数据库,redis等技术,然后在map的时候直接对大表进行join,不需要reduce端
public class Demo2 { public static void main(String[] args) throws Exception { Configuration configuration = new Configuration(); Job job = Job.getInstance(configuration); job.setJarByClass(Demo2.class); job.setMapperClass(Demo2Mapper.class); job.setMapOutputKeyClass(InfoBean.class); job.setMapOutputValueClass(NullWritable.class); job.setNumReduceTasks(0); //将文件上传到每一个task运行节点的工作目录下 job.addCacheFile(new URI("file:/F:/stu01.txt")); FileInputFormat.setInputPaths(job,new Path("F:\\wc\\input")); FileOutputFormat.setOutputPath(job,new Path("F:\\wc\\output")); job.waitForCompletion(true); }}class Demo2Mapper extends Mapper<LongWritable,Text,Text,NullWritable>{ Map<String,String> stuInfo = new Hashtable<String, String>(); Text t = new Text(); @Override protected void setup(Context context) throws IOException, InterruptedException { //获取到所有的上传的文件的URI URI[] cacheFiles = context.getCacheFiles(); //从当前工作目录下读取出学生信息,数据较少的情况下,可以直接放在内存中 BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(cacheFiles[0])))); String line; while((line=reader.readLine())!=null){ String[] split = line.split("\t"); stuInfo.put(split[0],split[1]+"\t"+split[2]); } reader.close(); } @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String[] split = value.toString().split("\t"); if(stuInfo.containsKey(split[0])){ t.set(stuInfo.get(split[0])+"\t"+value.toString()); context.write(t,NullWritable.get()); } }}
阅读全文
0 0
- 使用MapReduce求解join问题
- 做mapreduce join时遇到的问题
- 图解MapReduce各种join的使用
- mapreduce join
- MapReduce Join
- MapReduce Join
- Hadoop MapReduce进阶 使用DataJoin包实现Join
- Hadoop MapReduce进阶 使用分布式缓存进行replicated join
- Hadoop MapReduce进阶 使用DataJoin包实现Join
- Hadoop MapReduce进阶 使用分布式缓存进行replicated join
- Hadoop MapReduce进阶 使用DataJoin包实现Join
- Hadoop MapReduce进阶 使用分布式缓存进行replicated join
- Hadoop MapReduce进阶 使用分布式缓存进行replicated join
- Hadoop MapReduce进阶 使用DataJoin包实现Join
- Hadoop MapReduce进阶 使用分布式缓存进行replicated join
- Hadoop MapReduce进阶 使用分布式缓存进行replicated join
- Hadoop MapReduce进阶 使用DataJoin包实现Join
- [ Hadoop | MapReduce ] 使用 CompositeInputSplit 来提高Join效率
- 树状数组
- IIS站点:应用程序中的服务器错误-访问被拒绝
- c#属性,以及特性类
- Linux定时任务-Crontab
- 把第三方jar 转换成Maven可添加依赖jar
- 使用MapReduce求解join问题
- Android Studio、Git 解决合并冲突
- Android线程—多线程之死锁解决办法
- PHP Web开发技巧
- RSA算法原理
- 关于Android studio 使用aar 文件中引用了其他aar文件报错的问题
- 获取Sah1值
- Activiti——工作流程-核心API(二)
- 2017年你不能错过的Java类库