Map Join和Reduce Join的区别
来源:互联网 发布:mac怎么看隐藏文件 编辑:程序博客网 时间:2024/05/17 01:43
MapReduce Join
对两份数据data1和data2进行关键词连接是一个很通用的问题,如果数据量比较小,可以在内存中完成连接。
如果数据量比较大,在内存进行连接操会发生OOM。mapreduce join可以用来解决大数据的连接。
1 思路
1.1 reduce join
在map阶段, 把关键字作为key输出,并在value中标记出数据是来自data1还是data2。因为在shuffle阶段已经自然按key分组,reduce阶段,判断每一个value是来自data1还是data2,在内部分成2组,做集合的乘积。
这种方法有2个问题:
1, map阶段没有对数据瘦身,shuffle的网络传输和排序性能很低。2, reduce端对2个集合做乘积计算,很耗内存,容易导致OOM。
1.2 map join
两份数据中,如果有一份数据比较小,小数据全部加载到内存,按关键字建立索引。大数据文件作为map的输入文件,对map()函数每一对输入,都能够方便地和已加载到内存的小数据进行连接。把连接结果按key输出,经过shuffle阶段,reduce端得到的就是已经按key分组的,并且连接好了的数据。
这种方法,要使用Hadoop中的DistributedCache把小数据分布到各个计算节点,每个map节点都要把小数据库加载到内存,按关键字建立索引。
这种方法有明显的局限性:有一份数据比较小,在map端,能够把它加载到内存,并进行join操作。
1.3 使用内存服务器,扩大节点的内存空间
针对map join,可以把一份数据存放到专门的内存服务器,在map()方法中,对每一个的输入对,根据key到内存服务器中取出数据,进行连接
1.4 使用BloomFilter过滤空连接的数据
对其中一份数据在内存中建立BloomFilter,另外一份数据在连接之前,用BloomFilter判断它的key是否存在,如果不存在,那这个记录是空连接,可以忽略。
1.5 使用mapreduce专为join设计的包
在mapreduce包里看到有专门为join设计的包,对这些包还没有学习,不知道怎么使用,只是在这里记录下来,作个提醒。
jar: mapreduce-client-core.jarpackage: org.apache.hadoop.mapreduce.lib.join
2 实现map join
相对而言,map join更加普遍,下面的代码使用DistributedCache实现map join
2.1 背景
有客户数据customer和订单数据orders。
customer
客户编号 姓名 地址 电话
1 hanmeimei ShangHai 110
2 leilei BeiJing 112
3 lucy GuangZhou 119
* order*
订单编号 客户编号 其它字段被忽略
1 1 50
2 1 200
3 3 15
4 3 350
5 3 58
6 1 42
7 1 352
8 2 1135
9 2 400
10 2 2000
11 2 300
要求对customer和orders按照客户编号进行连接,结果要求对客户编号分组,对订单编号排序,对其它字段不作要求
客户编号 订单编号 订单金额 姓名 地址 电话
1 1 50 hanmeimei ShangHai 110
1 2 200 hanmeimei ShangHai 110
1 6 42 hanmeimei ShangHai 110
1 7 352 hanmeimei ShangHai 110
2 8 1135 leilei BeiJing 112
2 9 400 leilei BeiJing 112
2 10 2000 leilei BeiJing 112
2 11 300 leilei BeiJing 112
3 3 15 lucy GuangZhou 119
3 4 350 lucy GuangZhou 119
3 5 58 lucy GuangZhou 119
在提交job的时候,把小数据通过DistributedCache分发到各个节点。map端使用DistributedCache读到数据,在内存中构建映射关系--如果使用专门的内存服务器,就把数据加载到内存服务器,map()节点可以只保留一份小缓存;如果使用BloomFilter来加速,在这里就可以构建;map()函数中,对每一对,根据key到第2)步构建的映射里面中找出数据,进行连接,输出。
2.2 程序实现
public class Join extends Configured implements Tool {
// customer文件在hdfs上的位置。
// TODO: 改用参数传入
private static final String CUSTOMER_CACHE_URL = “hdfs://hadoop1:9000/user/hadoop/mapreduce/cache/customer.txt”;
private static class CustomerBean {
private int custId;
private String name;
private String address;
private String phone;
public CustomerBean() {} public CustomerBean(int custId, String name, String address, String phone) { super(); this.custId = custId; this.name = name; this.address = address; this.phone = phone; } public int getCustId() { return custId; } public String getName() { return name; } public String getAddress() { return address; } public String getPhone() { return phone; }}private static class CustOrderMapOutKey implements WritableComparable<CustOrderMapOutKey> { private int custId; private int orderId; public void set(int custId, int orderId) { this.custId = custId; this.orderId = orderId; } public int getCustId() { return custId; } public int getOrderId() { return orderId; } @Override public void write(DataOutput out) throws IOException { out.writeInt(custId); out.writeInt(orderId); } @Override public void readFields(DataInput in) throws IOException { custId = in.readInt(); orderId = in.readInt(); } @Override public int compareTo(CustOrderMapOutKey o) { int res = Integer.compare(custId, o.custId); return res == 0 ? Integer.compare(orderId, o.orderId) : res; } @Override public boolean equals(Object obj) { if (obj instanceof CustOrderMapOutKey) { CustOrderMapOutKey o = (CustOrderMapOutKey)obj; return custId == o.custId && orderId == o.orderId; } else { return false; } } @Override public String toString() { return custId + "\t" + orderId; }}private static class JoinMapper extends Mapper<LongWritable, Text, CustOrderMapOutKey, Text> { private final CustOrderMapOutKey outputKey = new CustOrderMapOutKey(); private final Text outputValue = new Text(); /** * 在内存中customer数据 */ private static final Map<Integer, CustomerBean> CUSTOMER_MAP = new HashMap<Integer, Join.CustomerBean>(); @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { // 格式: 订单编号 客户编号 订单金额 String[] cols = value.toString().split("\t"); if (cols.length < 3) { return; } int custId = Integer.parseInt(cols[1]); // 取出客户编号 CustomerBean customerBean = CUSTOMER_MAP.get(custId); if (customerBean == null) { // 没有对应的customer信息可以连接 return; } StringBuffer sb = new StringBuffer(); sb.append(cols[2]) .append("\t") .append(customerBean.getName()) .append("\t") .append(customerBean.getAddress()) .append("\t") .append(customerBean.getPhone()); outputValue.set(sb.toString()); outputKey.set(custId, Integer.parseInt(cols[0])); context.write(outputKey, outputValue); } @Override protected void setup(Context context) throws IOException, InterruptedException { FileSystem fs = FileSystem.get(URI.create(CUSTOMER_CACHE_URL), context.getConfiguration()); FSDataInputStream fdis = fs.open(new Path(CUSTOMER_CACHE_URL)); BufferedReader reader = new BufferedReader(new InputStreamReader(fdis)); String line = null; String[] cols = null; // 格式:客户编号 姓名 地址 电话 while ((line = reader.readLine()) != null) { cols = line.split("\t"); if (cols.length < 4) { // 数据格式不匹配,忽略 continue; } CustomerBean bean = new CustomerBean(Integer.parseInt(cols[0]), cols[1], cols[2], cols[3]); CUSTOMER_MAP.put(bean.getCustId(), bean); } }}/** * reduce * @author Ivan * */private static class JoinReducer extends Reducer<CustOrderMapOutKey, Text, CustOrderMapOutKey, Text> { @Override protected void reduce(CustOrderMapOutKey key, Iterable<Text> values, Context context) throws IOException, InterruptedException { // 什么事都不用做,直接输出 for (Text value : values) { context.write(key, value); } }}/** * @param args * @throws Exception */public static void main(String[] args) throws Exception { if (args.length < 2) { new IllegalArgumentException("Usage: <inpath> <outpath>"); return; } ToolRunner.run(new Configuration(), new Join(), args);}@Overridepublic int run(String[] args) throws Exception { Configuration conf = getConf(); Job job = Job.getInstance(conf, Join.class.getSimpleName()); job.setJarByClass(SecondarySortMapReduce.class); // 添加customer cache文件 job.addCacheFile(URI.create(CUSTOMER_CACHE_URL)); FileInputFormat.addInputPath(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); // map settings job.setMapperClass(JoinMapper.class); job.setMapOutputKeyClass(CustOrderMapOutKey.class); job.setMapOutputValueClass(Text.class); // reduce settings job.setReducerClass(JoinReducer.class); job.setOutputKeyClass(CustOrderMapOutKey.class); job.setOutputKeyClass(Text.class); boolean res = job.waitForCompletion(true); return res ? 0 : 1;}
}
运行环境
操作系统: Centos 6.4Hadoop: Apache Hadoop-2.5.0
==客户数据文件在hdfs上的位置硬编码为==
hdfs://hadoop1:9000/user/hadoop/mapreduce/cache/customer.txt, 运行程序之前先把客户数据上传到这个位置。
程序运行结果
- Map Join和Reduce Join的区别
- Hadoop的Map-side join和Reduce-side join
- reduce/map/semi join
- spark实现Map-side Join和Reduce-side Join
- map join的与Reduce Join效率对比
- <转> map join的与Reduce Join效率对比
- map-side-join /Reduce-side-join
- java fork join &map-reduce
- 【SQL】inner join、outer join、cross join和left join、right join、full join的区别
- left join和right join和inner join的区别
- left join和right join的区别
- join和inner join的区别
- inner join 和 outer join 的区别
- iner join和left join的区别
- inner join和left join的区别
- inner join和outer join的区别
- Inner join , Left join 和Right join 的区别
- JOIN ,LEFT JOIN ,ALL JOIN 等的区别和联系
- LINUX的第一次接触(笔记)安装和分区
- 第十五周 项目1 哈希表及其运算的实现
- 第十三周项目4 Floyd算法的验证
- 第十三周 项目五 拓扑排序
- 【第15周 项目3 - B-树的基本操作】
- Map Join和Reduce Join的区别
- 第十三周项目5--拓扑排序算法验证
- ios 动态字体
- u盘设置了clover引导后,识别不出部分容量,格式化u盘
- Unity学习之全景球的制作Part2
- 第十三周 项目3:Dijkstra算法的验证
- MapReduce简单分析
- 2017 回归
- 继承与初始化