day44-hadoop-mapreduce

day44-hadoop-mapreduce

hadoop-mapreduce

Reduce Join

reduce端合并的数据倾斜问题

处理的数据集大小不均,一部分大,一部分小
交给某个reduce的数据太大,导致无法解决

Map Join

  • 使用场景
Map Join适用于一张表十分小、一张表很大的场景
如果两张表都很大,就不适合使用mapjoin
  • 优点
思考:在Reduce端处理过多的表,非常容易产生数据倾斜。怎么办?
在Map端缓存多张表,提前处理业务逻辑,这样增加Map端业务,减少Reduce端数据的压力,尽可能的减少数据倾斜
  • 具体方法:采用DistributedCache
1. 在Mapper的setup阶段,将文件读取到缓存集合中
2. 在驱动函数中加载缓存
	缓存普通文件到Task运行节点
	job.addCacheFile(new URI(""))

Map Join 案例

  • MapJoinDriver
package lc.mapreduce.mapper.mapjoin;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;

import java.io.IOException;
import java.net.URI;

public class MapJoinDriver {
    // 通过IO的方式直接读取小表的数据

    public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
        //
        Configuration conf = new Configuration();

        Job job = Job.getInstance(conf);
        job.setJarByClass(MapJoinDriver.class);

        // 关联mapper
        job.setMapperClass(MapJoinMapper.class);

        //
        job.setMapOutputKeyClass(Text.class);
        job.setMapOutputValueClass(NullWritable.class);
        // 设置最终写的kv
        job.setOutputKeyClass(Text.class);
        job.setOutputValueClass(NullWritable.class);

        // 设置输入输出的文件
        FileInputFormat.setInputPaths(job, new Path("D:\\a测试文件\\input\\bigtable"));
        FileOutputFormat.setOutputPath(job, new Path("D:\\a测试文件\\output\\mapjoin4"));
        // 设置reduce的个数为0个
        job.setNumReduceTasks(0);
        // 设置小表的缓存数据
        job.addCacheFile(URI.create("file:///D:/a测试文件/input/smalltable/pd.txt"));

        // 提交job
        job.waitForCompletion(true);


    }
}
  • MapJoinMapper
package lc.mapreduce.mapper.mapjoin;

import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;

import java.io.BufferedReader;
import java.io.Console;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;

public class MapJoinMapper extends Mapper<LongWritable, Text,Text, NullWritable> {

    private Map<String,String> pdMap=new HashMap<>();

    private Text outk=new Text();
    /**
     * 只会执行一次
     * @param context
     * @throws IOException
     * @throws InterruptedException
     */
    @Override
    protected void setup(Mapper<LongWritable, Text, Text, NullWritable>.Context context) throws IOException, InterruptedException {
        // 将小表的数据缓存到内存中
        // 获取待缓存数据
        URI[] cacheFiles = context.getCacheFiles();
        URI uriFile=cacheFiles[0];// pd.txt
        // 获取文件系统对象
        FileSystem fs=FileSystem.get(context.getConfiguration());
        FSDataInputStream open = fs.open(new Path(uriFile));
        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(open,"utf-8"));

        String line;
        while ((line = bufferedReader.readLine()) != null) {
            // 处理每行读取到的数据
            // 01   小米
            String[] split = line.split("\t");
            pdMap.put(split[0],split[1]);

        }
        pdMap.forEach((x,y)->{
            System.out.println(x+":"+y);
        });
        // 关闭流
        IOUtils.closeStream(bufferedReader);
    }

    @Override
    protected void map(LongWritable key, Text value, Mapper<LongWritable, Text, Text, NullWritable>.Context context) throws IOException, InterruptedException {
        // 正常处理大数据
        // 获取到一行数据
        // 1001 01 1
        String row=value.toString();

        // 切割
        String[] split = row.split("\t");

        // join
        split[1]=pdMap.get(split[1]);

        // 封装k
        outk.set(split[0]+"\t"+split[1]+"\t"+split[2]);
        // 写出
        context.write(outk,NullWritable.get());

    }
}

计数器应用

hadoop为每个作业维护若干内置计数器,以描述多项指标。例如:某些计数器记录已处理的字节数和记录数,使用户可监控已处理的输入数量和已产生的输出数据量
  • 应用
context.getCounter("Map join","setup").increment(1);
// context.getCount(groupName,counterName) 获取计数器对象,并且设置组和计数器名
// increment(1) 每次计数的增长

数据清洗(ETL)

在运行核心业务MapReduce程序之前,往往需要对数据进行清洗,清理掉不符合用户要求的数据。清理的过程往往只需要运行Mapper程序,不需要运行Reduce程序

满足业务需求的数据,就是干净的数据
不满足业务需求的数据,就是脏数据
数据清洗,就是对脏数据的一个清洗
posted @ 2022-05-01 23:20  黎白昼  阅读(26)  评论(0)    收藏  举报