MapReduce分布式离线计算框架学习摘要(一)

17 篇文章 0 订阅
16 篇文章 1 订阅
一、MapReduce简单概述
在Hadoop中有两个核心的模块,一个是大数据量文件的存储HDFS,另一个是能够做快速的数据分析,则为MapReduce。
百度百科介绍:
二、MapReduce的特点
它适合做“离线”(存储在本地)的海量数据计算,通常计算的数据量在PB级别或者ZB级别
MapReduce的主要特点如下:
  1. 易于编程
  2. 扩展性良好
  3. 高容错性
 
三、MapReduce的应用场景
    MapReduce对于离线的大数据量分析计算还是需要一定时间的,不能实时做计算分析,它的典型应用有以下一些:
  1. 简单的数据统计,比如网站的pv、uv
  2. 搜索引擎建立索引
  3. 在搜索引擎中统计最流行的搜索词
  4. 统计搜索的词频率
  5. 复杂的数据分析算法实现
    MapReduce也有不适用的的场景
        1. 实时计算
        2. 流式计算(MapReduce对于数据集要求是静态的,不能是动态的)
  1. DAG计算,当多个应用程序存在依赖关系,且后一个应用的输入来自于前一个输出这个情况是不适合用MapReduce的
 
四、MapReduce是如何运行的(通过实例来理解这个过程)
        单词统计,分析的过程如下图所示
        
        图中主要分为五个过程
            Split:把大文件进行切割成多份
            Map:分别针对切出来的小文件,解析出每一个字符并在后面记上数字1
            Shuffle:每一份中的字符进行分组到一起并做统计
            Reduce:把对应的字符进行累加
            Final result:输出最终的结果
    
        上面我们总结出了大致的执行过程,接下来看看具体的执行过程如何
            1. 把数据切割成数据片段
            2. 数据片段以key和value的形式被读进来处理,默认情况是以行的下标作为key,行的内容做为value
            3. 数据传入Map中进行处理(处理逻辑由用户定义),在Map中处理完成后还是以key和value的形式输出
            4. 输出的数据给到Shuffle,它进行数据的排序合并等操作,但是它不会修改传入的数据,这个时候还是key2和value
            5. 数据接下来传给Reduce进行处理,它处理完后,生成key3和value3
            6. Reduce处理完的数据会被写到HDFS的某个目录中
        接下来针对 Split、Map、Reduce及Shuffle做分别的简单介绍
        1. Split(MapReduce的文件切片)
            第一个问题,它按多大的大小进行切片?
            默认与block对应,这个大小也可以由用户自行控制,MapReduce的Split大小计算公式如下: max(min.split, min(max.split, block))
             max.split = totalSize/numSplit(totalSize:文件大小;numSplit:用户设定的map task的个数,默认为1)
            min.split = InputSplit的最小值(可以在配置文件中进行配置参数,mapred.min.split.size,在不配置时默认为1B,block是HDFS中块的大小)
 
        2. Map过程与Reduce过程
            Map和Reduce的实现逻辑都是由程序员完成的.
            Map个数与Split的个数对应起来,一个Split切片对应一个Map任务
            Reduce的默认数是1,这个是可以自行设置的。
            注意:一个程序可能只有一个Map任务却没有Reduce任务,也可能是多个MapReduce程序串连起来,比如,第一个MapReduce的输出结果作为第二个MapReduce的输入,第二个MapReduce的输出又是第三个MapReduce的输入,最终所有的MapReduce完成才完成一个任务。
 
        3. Shuffle过程
            它又叫“洗牌”,它起到了连接Map任务与Reduce任务的作用。
            Shuffle分为两个部分,一部分在Map端,另一部分在Reduce端
            Map处理后的数据会以key、value的形式存在缓冲区中,这个缓冲区大小是128MB,当这个缓冲区快要溢出时(默认为80%),会把数据写到磁盘中生成文件,这个过程叫做溢写操作。
            溢写磁盘的工作由一个线程来完成,溢写之前包括Partition(分区)、Sort(排序)都有默认的实现
            Partition:默认按hash值%reduce数量进行分区
            Sort:默认按字符顺序进行排序
            在完成溢写后,在磁盘上会生成多个文件,多个文件会通过merge线程完成文件合并
            合并完成后的数据(以key和value的形式存在),会基于Partition被发送到不同的Reduce上
 
五、MapReduce实例
 
实例一:对文件中的单词进行统计,字符之间使用空格进行分隔
相关的JAR包
    hadoop-3.1.3\share\hadoop\common 下的jar及hadoop-3.1.3\share\hadoop\common\lib下的jar包
    hadoop-3.1.3\share\hadoop\mapreduce下的jar及hadoop-3.1.3\share\hadoop\mapreduce\lib下的jar包
    hadoop-3.1.3\share\hadoop\hdfs下及hadoop-3.1.3\share\hadoop\hdfs\lib下的jar包
    一个完整的MapReduce分为两个部分:一个是Mapper;另一个是Reducer
    
    Mapper
    它是用来对文件进行切割并且循环输出给到Reducer
    
public class WordCountMapper extends Mapper<LongWritable,Text,Text,IntWritable>{
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //使用空隔进行分词
        String[] str = value.toString().split(" ");
        //for循环处理
        for(int i = 0; i < str.length; i++){
            //new Text,new IntWritable进行可序列化
            context.write(new Text(str[i]),new IntWritable(1));
        }
    }
}

 

    
    Readucer
    它将从Map转入的词汇进行分组合并,并通过文本和单词统计量的方式输出
public class WordCountReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        //数据进行分组合并输出
        int sum = 0;
        for(IntWritable i:values){
            sum = sum+i.get();
        }
        context.write(key,new IntWritable(sum));
    }
}

 

 
    主方法所在类
    主方法中用来连接HDFS、要读取的文件及处理后的文件在HDFS中的路径。同时需要指明我们所要进行Map和Reduce过程的类
public class MRRunJob {
    public static void main(String[] args) {
        Configuration conf = new Configuration();
        //NameNode入口
        conf.set("fs.defaultFS","hdfs://192.168.2.4:8020");
        Job job = null;
        try {
            job = Job.getInstance(conf,"mywc");
        } catch (IOException e) {
            e.printStackTrace();
        }


        //主类
        job.setJarByClass(MRRunJob.class);
        //Mapper类
        job.setMapperClass(WordCountMapper.class);
        //Reducer类
        job.setReducerClass(WordCountReducer.class);
        //Map输出的value类型
        job.setOutputKeyClass(Text.class);
        //Map输出的value类型
        job.setOutputValueClass(IntWritable.class);


        try {
            //读取文件位置
            job.setWorkingDirectory(new Path("/"));
            System.out.println(job.getWorkingDirectory());
            FileInputFormat.addInputPath(job,new Path("/usr/input/data/wc/"));
            //处理完成后数据存储的位置(注意:如果输出文件夹存在则会报错)
            FileOutputFormat.setOutputPath(job,new Path("/usr/output/data/wc/"));
            job.waitForCompletion(true);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

 

    注意:上面的类一定是要在HDFS中的路径是否则程序会报错提示找不到文件位置。切记!!
 
实例二:数据清洗ETL实例(对时间进行格式化处理)
Mapper
public class ETLMapper extends Mapper<LongWritable,Text,Text,NullWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //使用,号对文本内容进行分隔
        String[] strArray = value.toString().split(",");
        String strContent = "";


        for(int i=0;i<strArray.length;i++){
            //把下标为第三个数据转换为yyyy-MM-dd HH:mm:ss的时间格式
            if(i == 3){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                //把原数据的秒转为毫秒
                String str = sdf.format(Long.parseLong(strArray[i]+"000"));
                strContent = strContent + str + ",";
            } else {
                strContent = strContent + strArray[i] + ",";
            }
        }
        context.write(new Text(strContent),NullWritable.get());
    }
}

 

Reducer
public class ETLReducer extends Reducer<Text,NullWritable,NullWritable,Text> {
    @Override
    protected void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
        //把Mapper传过来的内容写入磁盘
        context.write(NullWritable.get(),key);
    }
}

 

主方法
public class MRRunJob {
    public static void main(String[] args) {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS","hdfs://192.168.2.4:8020");
        //初始化fs
        FileSystem fs = null;
        try {
            fs = FileSystem.get(conf);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Job job = null;
        try {
            job = Job.getInstance(conf,"mywc");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //主方法
        job.setJarByClass(MRRunJob.class);
        //Mapper方法
        job.setMapperClass(ETLMapper.class);
        //Reducer方法
        job.setReducerClass(ETLReducer.class);
        //Map输出的key类型
        job.setOutputKeyClass(Text.class);
        //Map输出的value类型
        job.setOutputValueClass(NullWritable.class);


        try {
            //读取文件位置
            Path inputPath = new Path("/usr/input/data/etl/");
            //如果这个文件不存在则新增
            if(!fs.exists(inputPath)){
                fs.mkdirs(inputPath);
            }
            //需要把本地的文件上传到HDFS
            Path src = new Path("D:\\IdeaProjects\\MapReduceTest\\ETLDemo\\etl01.txt");
            fs.copyFromLocalFile(src,inputPath);


            FileInputFormat.addInputPath(job,inputPath);


            //处理完成后文件输出位置
            Path outputPath = new Path("/usr/output/data/etl01/");
            //如果这个输出的目录存在则删除
            if(fs.exists(outputPath)){
                fs.delete(outputPath,true);
            }
            FileOutputFormat.setOutputPath(job,outputPath);
            boolean f = job.waitForCompletion(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

注意:如果在window环境中需要运行成功的话,需要修改原码 NativeIO.java
把这个源码复制到自己的工程当中,并修改如下内容:
把原来的return注释掉,直接reurn true即可
如果不做这个操作,则在运行时会报错: org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z
 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值