Hadoop(六) -- MapReduce(四)join

一、Mapper中的方法

        1. setup()

                map方法的前置方法,每一个maptask任务初始化时会调用一次。

                作用:准备map函数需要的参数(准备数据)

                reduce join:读取文件路径、文件名、准备参数

                map join:将小文件读取至内存,准备数据

        2. cleanup()

                收尾工作,每一个maptask任务结束时调用

        一个切片对应一个maptask,其中:setup和cleanup每个maptask调用一次,map一行调用一次。

public void run(Context context) throws IOException, InterruptedException {
    // 调用setup方法
    setup(context);
    try {
      // context.nextKeyValue() 判断是否还有下一组k v
      // k - 偏移量  v - 一行内容
      while (context.nextKeyValue()) {
        map(context.getCurrentKey(), context.getCurrentValue(), context);
      }
    } finally {
      // 调用cleanup方法
      cleanup(context);
    }
 }

setup方法中获取文件名

String fileName = null;
/**
* 参数  上下文对象,读取文件信息(每个maptask任务对应的是切片信息)
*/
@Override
protected void setup(Context context) throws IOException, InterruptedException {
    // 获取当前maptask的切片信息
	InputSplit inputSplit = context.getInputSplit();
	FileSplit fs = (FileSplit) inputSplit;
	fileName = fs.getPath().getName();
}

二、reduce join

       1)map端

                setup()  -- 获取文件名

                map中:key -- 关联键    value -- 标记 + 其它字段

        2)reduce端

                接收数据,相同关联键的所有数据。

                首先创建两个集合,存放两个表中的剩余字段。循环遍历values放入不同集合中。循环遍历集合,拼接。(若a表中没有b表中的关联字段,则a集合为空,不仅循环  -- 内连接)

/**
 * 数据:
 * 1. order表
 * 		oid		data		pid		num
 * 		1001	20150710	p0001	2
 * 		1002	20150710	p0001	3
 * 2. product表
 * 		pid		产品		产品编号	price
 * 		p0001	小米5	C01		2000
 * 		p0002	锤子T1	C01		3500
 */
public class ReduceJoin {
	static class MyMapper extends Mapper<LongWritable, Text, Text, Text> {
		String fileName = null;
		@Override
		protected void setup(Context context) throws IOException, InterruptedException {
			InputSplit inputSplit = context.getInputSplit();
			FileSplit fs = (FileSplit) inputSplit;
			fileName = fs.getPath().getName();
		}

		@Override
		protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
			String[] infos = value.toString().split("\t");
			if (fileName.startsWith("order")) {
				// 给不同表的数据打上标记后传入reduce
				context.write(new Text(infos[2]), new Text("o," + infos[0] + "," + infos[1] + "," + infos[3]));
			} else {
				context.write(new Text(infos[0]), new Text("p," + infos[1] + "," + infos[2] + "," + infos[3]));
			}
		}
	}

	static class MyReducer extends Reducer<Text, Text, Text, Text> {
		@Override
		protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException {
			// reduce端创建两个容器存储数据
			ArrayList<String> orders = new ArrayList<String>();
			ArrayList<String> products = new ArrayList<String>();
			for (Text value : values) {
				String[] infos = value.toString().split(",");
				if (infos[0].equals("o")) orders.add(value.toString().substring(2));
				else products.add(value.toString().substring(2));
			}
			for (String order : orders) {
				for (String product : products) {
					// 拼接两个表的数据
					context.write(key, new Text(order + "," + product));
				}
			}
		}
	}

reduce join的缺点:

        1)reduce端进行join的时候容易造成数据倾斜

        2)所有可以关联的数据全部发送到reduce端,创建list集合进行存储,当一个key关联的数据特别多时会造成OOM(OutOfMemory)

        3)reduce端数据本身的并行度不高,reducetask任务经验个数 <= 节点数 * 0.95

三、map join

        在map()之前将另一个文件存入缓存,map()每次读取一条数据与缓存数据做关联。

        流:只能读取本地文件,需要保证在每一个运行maptask的节点上保存一份。

        在job中指定:job.addCacheFile(URL);  将指定URI的文件缓存到每一个maptask节点。

        整体逻辑:

                maptask读取一个表(大表),将另一个表(小表)缓存到每一个maptask节点。

                map端:

                        setup:创建本地流,读取本地缓存的小表,存储到map容器中

                        map:每次可以读到大表中的一条数据,在map集合中查找关联

        

static class MyMapper extends Mapper<LongWritable, Text, Text, Text> {
	Map<String, String> cache = new HashMap<String, String>();
	@Override
	protected void setup(Context context) throws IOException, InterruptedException {
		// 获取本地缓存路径  path数组: 缓存可能加载多个文件
		Path[] path = context.getLocalCacheFiles();
		// 创建本地字符流
		BufferedReader reader = new BufferedReader(new FileReader(path[0].toString()));
		String line = null;
		while ((line = reader.readLine()) != null) {
			String[] infos = line.split("\t");
			// 将小表中的数据存入cache集合中
			cache.put(infos[0], infos[1] + "\t" + infos[2]);
		}
	}

	@Override
	protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
		super.map(key, value, context);
	}
}

job设置

// reducetask个数不设置默认为1,若不需要reducetask将其设置为0
// 若不设置Reducer.class,默认执行Reducer类的代码
job.setNumReduceTask(0);
// 添加缓存文件
job.addCacheFile(new URI("/"));

mapjoin 缺点:需要将一个表放在缓存中,不何用与大表关联大表的操作

mapjoin 优点:

        1)不会产生数据倾斜

        2)并行度高,执行效率高

        3)没有reducetask,没有shuffle (没有排序|分组|分区|Combiner),执行效率高

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值