上次和朋友讨论到mapreduce,join应该发生在map端,理由太想当然到sql里面的执行过程了 wheremap端 join在map之前(笛卡尔积),但实际上网上看了,mapreduce的笛卡尔积发生在reduce端,下面哥们有个实现过程可以参考(http://blog.csdn.net/xyilu/article/details/8996204)。有空再看看 实际上实现过程是不是和他写的代码一样。
 
 
 
 
 
 
前阵子把MapReduce实现join操作的算法设想清楚了,但一直没有在代码层面落地。今天终于费了些功夫把整个流程走了一遭,期间经历了诸多麻烦并最终得以将其一一搞定,再次深切体会到,什么叫从计算模型到算法实现还有很多路要走。
 

数据准备

首先是准备好数据。这个倒已经是一个熟练的过程,所要做的是把示例数据准备好,记住路径和字段分隔符。
准备好下面两张表:
(1)m_ys_lab_jointest_a(以下简称表A)
建表语句为:
  1. create table if not exists m_ys_lab_jointest_a (
  2. id bigint,
  3. name string
  4. )
  5. row format delimited
  6. fields terminated by '9'
  7. lines terminated by '10'
  8. stored as textfile;
数据:
id     name
1     北京
2     天津
3     河北
4     山西
5     内蒙古
6     辽宁
7     吉林
8     黑龙江
(2)m_ys_lab_jointest_b(以下简称表B)
建表语句为:
  1. create table if not exists m_ys_lab_jointest_b (
  2. id bigint,
  3. statyear bigint,
  4. num bigint
  5. )
  6. row format delimited
  7. fields terminated by '9'
  8. lines terminated by '10'
  9. stored as textfile;

数据:

id     statyear     num
1     2010     1962
1     2011     2019
2     2010     1299
2     2011     1355
4     2010     3574
4     2011     3593
9     2010     2303
9     2011     2347

我们的目的是,以id为key做join操作,得到以下表:

m_ys_lab_jointest_ab
id     name    statyear     num
1       北京    2011    2019
1       北京    2010    1962
2       天津    2011    1355
2       天津    2010    1299
4       山西    2011    3593
4       山西    2010    3574

计算模型

整个计算过程是:
(1)在map阶段,把所有记录标记成<key, value>的形式,其中key是id,value则根据来源不同取不同的形式:来源于表A的记录,value的值为"a#"+name;来源于表B的记录,value的值为"b#"+score。
(2)在reduce阶段,先把每个key下的value列表拆分为分别来自表A和表B的两部分,分别放入两个向量中。然后遍历两个向量做笛卡尔积,形成一条条最终结果。
如下图所示:

代码

代码如下:
  1. import java.io.IOException;
  2. import java.util.HashMap;
  3. import java.util.Iterator;
  4. import java.util.Vector;
  5. import org.apache.hadoop.io.LongWritable;
  6. import org.apache.hadoop.io.Text;
  7. import org.apache.hadoop.io.Writable;
  8. import org.apache.hadoop.mapred.FileSplit;
  9. import org.apache.hadoop.mapred.JobConf;
  10. import org.apache.hadoop.mapred.MapReduceBase;
  11. import org.apache.hadoop.mapred.Mapper;
  12. import org.apache.hadoop.mapred.OutputCollector;
  13. import org.apache.hadoop.mapred.RecordWriter;
  14. import org.apache.hadoop.mapred.Reducer;
  15. import org.apache.hadoop.mapred.Reporter;
  16. /**
  17. * MapReduce实现Join操作
  18. */
  19. public class MapRedJoin {
  20. public static final String DELIMITER = "\u0009"; // 字段分隔符
  21. // map过程
  22. public static class MapClass extends MapReduceBase implements
  23. Mapper<LongWritable, Text, Text, Text> {
  24. public void configure(JobConf job) {
  25. super.configure(job);
  26. }
  27. public void map(LongWritable key, Text value, OutputCollector<Text, Text> output,
  28. Reporter reporter) throws IOException, ClassCastException {
  29. // 获取输入文件的全路径和名称
  30. String filePath = ((FileSplit)reporter.getInputSplit()).getPath().toString();
  31. // 获取记录字符串
  32. String line = value.toString();
  33. // 抛弃空记录
  34. if (line == null || line.equals("")) return;
  35. // 处理来自表A的记录
  36. if (filePath.contains("m_ys_lab_jointest_a")) {
  37. String[] values = line.split(DELIMITER); // 按分隔符分割出字段
  38. if (values.length < 2) return;
  39. String id = values[0]; // id
  40. String name = values[1]; // name
  41. output.collect(new Text(id), new Text("a#"+name));
  42. }
  43. // 处理来自表B的记录
  44. else if (filePath.contains("m_ys_lab_jointest_b")) {
  45. String[] values = line.split(DELIMITER); // 按分隔符分割出字段
  46. if (values.length < 3) return;
  47. String id = values[0]; // id
  48. String statyear = values[1]; // statyear
  49. String num = values[2]; //num
  50. output.collect(new Text(id), new Text("b#"+statyear+DELIMITER+num));
  51. }
  52. }
  53. }
  54. // reduce过程
  55. public static class Reduce extends MapReduceBase
  56. implements Reducer<Text, Text, Text, Text> {
  57. public void reduce(Text key, Iterator<Text> values,
  58. OutputCollector<Text, Text> output, Reporter reporter)
  59. throws IOException {
  60. Vector<String> vecA = new Vector<String>(); // 存放来自表A的值
  61. Vector<String> vecB = new Vector<String>(); // 存放来自表B的值
  62. while (values.hasNext()) {
  63. String value = values.next().toString();
  64. if (value.startsWith("a#")) {
  65. vecA.add(value.substring(2));
  66. } else if (value.startsWith("b#")) {
  67. vecB.add(value.substring(2));
  68. }
  69. }
  70. int sizeA = vecA.size();
  71. int sizeB = vecB.size();
  72. // 遍历两个向量
  73. int i, j;
  74. for (i = 0; i < sizeA; i ++) {
  75. for (j = 0; j < sizeB; j ++) {
  76. output.collect(key, new Text(vecA.get(i) + DELIMITER +vecB.get(j)));
  77. }
  78. }
  79. }
  80. }
  81. protected void configJob(JobConf conf) {
  82. conf.setMapOutputKeyClass(Text.class);
  83. conf.setMapOutputValueClass(Text.class);
  84. conf.setOutputKeyClass(Text.class);
  85. conf.setOutputValueClass(Text.class);
  86. conf.setOutputFormat(ReportOutFormat.class);
  87. }
  88. }

技术细节

下面说一下其中的若干技术细节:
(1)由于输入数据涉及两张表,我们需要判断当前处理的记录是来自表A还是来自表B。Reporter类getInputSplit()方法可以获取输入数据的路径,具体代码如下:
String filePath = ((FileSplit)reporter.getInputSplit()).getPath().toString();
(2)map的输出的结果,同id的所有记录(不管来自表A还是表B)都在同一个key下保存在同一个列表中,在reduce阶段需要将其拆开,保存为相当于笛卡尔积的m x n条记录。由于事先不知道m、n是多少,这里使用了两个向量(可增长数组)来分别保存来自表A和表B的记录,再用一个两层嵌套循环组织出我们需要的最终结果。
(3)在MapReduce中可以使用System.out.println()方法输出,以方便调试。不过System.out.println()的内容不会在终端显示,而是输出到了stdout和stderr这两个文件中,这两个文件位于logs/userlogs/attempt_xxx目录下。可以通过web端的历史job查看中的“Analyse This Job”来查看stdout和stderr的内容。

mapreduce join操作的更多相关文章

  1. 使用MapReduce实现join操作

     在关系型数据库中,要实现join操作是非常方便的,通过sql定义的join原语就可以实现.在hdfs存储的海量数据中,要实现join操作,可以通过HiveQL很方便地实现.不过HiveQL也是转化成 ...

  2. MapReduce 实现数据join操作

    前段时间有一个业务需求,要在外网商品(TOPB2C)信息中加入 联营自营 识别的字段.但存在的一个问题是,商品信息 和 自营联营标示数据是 两份数据:商品信息较大,是存放在hbase中.他们之前唯一的 ...

  3. Hadoop基础-MapReduce的Join操作

    Hadoop基础-MapReduce的Join操作 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.连接操作Map端Join(适合处理小表+大表的情况) no001 no002 ...

  4. 0 MapReduce实现Reduce Side Join操作

    一.准备两张表以及对应的数据 (1)m_ys_lab_jointest_a(以下简称表A) 建表语句: create table if not exists m_ys_lab_jointest_a ( ...

  5. 案例-使用MapReduce实现join操作

    哈喽-各位小伙伴们中秋快乐,好久没更新新的文章啦,今天分享如何使用mapreduce进行join操作. 在离线计算中,我们常常不只是会对单一一个文件进行操作,进行需要进行两个或多个文件关联出更多数据, ...

  6. Mapreduce中的join操作

    一.背景 MapReduce提供了表连接操作其中包括Map端join.Reduce端join还有半连接,现在我们要讨论的是Map端join,Map端join是指数据到达map处理函数之前进行合并的,效 ...

  7. hive:join操作

    hive的多表连接,都会转换成多个MR job,每一个MR job在hive中均称为Join阶段.按照join程序最后一个表应该尽量是大表,因为join前一阶段生成的数据会存在于Reducer 的bu ...

  8. mapreduce join

    MapReduce Join 对两份数据data1和data2进行关键词连接是一个很通用的问题,如果数据量比较小,可以在内存中完成连接. 如果数据量比较大,在内存进行连接操会发生OOM.mapredu ...

  9. SQL join中级篇--hive中 mapreduce join方法分析

    1. 概述. 本文主要介绍了mapreduce框架上如何实现两表JOIN. 2. 常见的join方法介绍 假设要进行join的数据分别来自File1和File2. 2.1 reduce side jo ...

随机推荐

  1. shell常用命令及正则辅助日志分析统计

    https://www.cnblogs.com/wj033/p/3451618.html 正则日志分析统计 3 grep 'onerror'  v3-0621.log | egrep  -v '(\d ...

  2. Jodd - Java界的瑞士军刀轻量级工具包!

    Jodd介绍 Jodd是对于Java开发更便捷的开源迷你框架,包含工具类.实用功能的集合,总包体积不到1.7M. Jodd构建于通用场景使开发变得简单,但Jodd并不简单!它能让你把事情做得更好,实现 ...

  3. Pycharm VS VS Code(个人使用感受)

    Pycharm IDE (community enough!) 简单介绍:Pycharn的确是我刚开始学习python时,除了Visual Studio之外,上手的第二个IDE,最初是因其好看的界面, ...

  4. 大型SQL文件导入mysql方案

    一. 场景 现有俩个体积较大的单表sql文件,一个为8G,一个为4G,要在一天内完整导入到阿里云的mysql中,需要同时蛮子时间和空间这俩种要求. 二. 思路 搜索了网上一堆的方案,总结了如下几个: ...

  5. centos6 nginx安装好以后,添加拓展ssl

    前言 安装nginx的时候,只是执行最简单的安装,--user=nobody --group=nobody --prefix=/usr/local/nginx_1.8.1,没有安装http_ssl_m ...

  6. 操作系统命令工具Util

    import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.i ...

  7. 关于不同数据库的连接配置(MySql和Oracle)

    mysql: driverClass=com.mysql.jdbc.Driver #在和mysql传递数据的过程中,使用unicode编码格式,并且字符集设置为utf-8,插入数据库防止中文乱码 ur ...

  8. jmeter参数化之用户参数

    1.     用badboby进行录制,录制完成后保存,用JMeter格式进行保存,如:登陆.jmx 2.     在jmeter中打开保存的文件登陆.jmx. 3.在step1上右击-添加-前置处理 ...

  9. VC控件DateTimePicker使用方法

    出自http://www.cnblogs.com/52yixin/articles/2111299.html 使用DateTimePicker控件一般是获 取其时间替代手工输入带来的不便,而DateT ...

  10. MFC安装与部署(程序打包)

    (发现csdn传照片实在是太麻烦, 不能够直接拖拽进来;所以我直接使用云笔记生成一张图片 直接完成!) (懒癌晚期-)