第三章 MapReduce

3.1 MapReuduce

3.1.1 MapReduce概念

MapReduce是一个分布式计算框架。

3.1.2 MapReduce底层原理

MapReduce 采用的是移动计算的方式,根据split产生Map task,然后通过shuffle,将map任务的输出拷贝到不同的reduce节点。由reduce阶段进行全局汇总。
原理图
在这里插入图片描述

block 是HDFS文件物理上的分割,split 是HDFS文件逻辑上的分割,严格意义上来说是一个split 产生一个map 任务。

3.1.3 MapReduce执行过程

在这里插入图片描述

3.2 WordCount

代码详见github https://github.com/Adopat/hadoop_demo

3.3 MapReduce 高级

3.31 MapReduce日志查看
  • 日志查看前置步骤

    启动historyserver进程,开启日志聚合功能。默认情况下任务的日志是散落在nodemanager节点上的想要查看需要找到对应的nodemanager节点上去查看,这样就很不方便,通过日志聚合功能我们可以把之前本来散落在nodemanager节点上的日志统一收集到hdfs上的指定目录中,这样就可以在yarn的web界面中直接查看了。

    # 修改 yarn-site.xml配置 ,增加以下参数配置
    # 1.停止集群
    [root@bigdata01 hadoop-3.2.0]# sbin/stop-all.sh
    Stopping namenodes on [bigdata01]
    Last login: Mon Jan 17 15:11:28 CST 2022 on pts/0
    Stopping datanodes
    Last login: Mon Jan 17 16:08:35 CST 2022 on pts/0
    Stopping secondary namenodes [bigdata01]
    Last login: Mon Jan 17 16:08:36 CST 2022 on pts/0
    Stopping nodemanagers
    Last login: Mon Jan 17 16:08:38 CST 2022 on pts/0
    Stopping resourcemanager
    Last login: Mon Jan 17 16:08:41 CST 2022 on pts/0
    [root@bigdata01 hadoop-3.2.0]# jps
    4136 Jps
    2607 JobHistoryServer
    You have new mail in /var/spool/mail/root
    [root@bigdata01 hadoop-3.2.0]# pwd
    /data/soft/hadoop-3.2.0
    # 修改 yarn-site.xml 在配置后面增加 yarn.log-aggregation-enable yarn.log.server.url
    [root@bigdata01 hadoop-3.2.0]# cat etc/hadoop/yarn-site.xml 
    <?xml version="1.0"?>
    <!--
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      You may obtain a copy of the License at
    
        http://www.apache.org/licenses/LICENSE-2.0
    
      Unless required by applicable law or agreed to in writing, software
      distributed under the License is distributed on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      See the License for the specific language governing permissions and
      limitations under the License. See accompanying LICENSE file.
    -->
    
    <configuration> 
        <property> 
            <name>yarn.nodemanager.aux-services</name> 
            <value>mapreduce_shuffle</value> 
        </property> 
        <property> 
            <name>yarn.nodemanager.env-whitelist</name> 
            <value>JAVA_HOME,HADOOP_COMMON_HOME,HADOOP_HDFS_HOME,HADOOP_CONF_DIR,CLASSPATH_PREPEND_DISTCACHE,HADOOP_YARN_HOME,HADOOP_MAPRED_HOME</value>
        </property>
        <property> 
            <name>yarn.resourcemanager.hostname</name>
            <value>bigdata01</value>
        </property>
        <property> 
            <name>yarn.log-aggregation-enable</name>
            <value>true</value>
        </property>
        <property>
            <name>yarn.log.server.url</name>
            <value>http://bigdata01:19888/jobhistory/logs/</value>
        </property> 
    </configuration>
    # 3.重新启动集群
    [root@bigdata01 hadoop-3.2.0]# sbin/start-all.sh
    Starting namenodes on [bigdata01]
    Last login: Mon Jan 17 16:08:43 CST 2022 on pts/0
    Starting datanodes
    Last login: Mon Jan 17 16:15:21 CST 2022 on pts/0
    Starting secondary namenodes [bigdata01]
    Last login: Mon Jan 17 16:15:23 CST 2022 on pts/0
    Starting resourcemanager
    Last login: Mon Jan 17 16:15:27 CST 2022 on pts/0
    Starting nodemanagers
    Last login: Mon Jan 17 16:15:32 CST 2022 on pts/0
    You have new mail in /var/spool/mail/root
    [root@bigdata01 hadoop-3.2.0]# jps
    4613 SecondaryNameNode
    4854 ResourceManager
    5174 Jps
    4346 NameNode
    2607 JobHistoryServer
    # 启动historyserver服务命令 bin/mapred --daemon start historyserver
    [root@bigdata01 hadoop-3.2.0]# bin/mapred --daemon start historyserver
    [root@bigdata01 hadoop-3.2.0]# hdfs dfs -ls /
    Found 9 items
    -rw-r--r--   2 root    supergroup       1361 2022-01-05 16:05 /README.txt
    -rw-r--r--   2 root    supergroup       1361 2022-01-07 15:44 /README.txt.bak
    drwxr-xr-x   - root    supergroup          0 2022-01-05 16:08 /abc
    drwxr-xr-x   - root    supergroup          0 2022-01-10 15:44 /log
    drwxr-xr-x   - root    supergroup          0 2022-01-17 15:15 /out
    drwxr-xr-x   - root    supergroup          0 2022-01-15 15:32 /test
    drwx------   - root    supergroup          0 2022-01-17 14:38 /tmp
    drwx------   - root    supergroup          0 2022-01-10 09:16 /user
    -rw-r--r--   3 ZhongRF supergroup  305555522 2022-01-06 15:46 /user.txt
    You have new mail in /var/spool/mail/root
    [root@bigdata01 hadoop-3.2.0]# history | grep jar
      854  hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out
      879  history | grep hadoop jar
      893  history | grep hadoop jar
      899  history | grep hadoop jar
      913  history | grep hadoop jar
      919  history | grep hadoop jar
      921  hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out
      937  hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out1
      945  hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out1
      950  hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out2
      975  history | grep jar
      979   hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out
      989  history | grep jar
      # 重新提交 mapreduce 任务
    [root@bigdata01 hadoop-3.2.0]# hadoop jar hadoop_demo-1.0-SNAPSHOT-jar-with-dependencies.jar com.imooc.mr.WordCountJob /test/hello.txt /out1
    

    注意要将修改后的yarn-site.xml 同步到集群中的其他节点,此外还要在每一个节点中开启 historyserver服务

  • 页面查看

    进入yarn的8088界面
    在这里插入图片描述

  • 命令行方式查看

    [root@bigdata01 hadoop-3.2.0]# yarn logs -applicationId application_1642407336043_0002 | grep k3,v3
    2022-01-17 16:51:54,429 INFO client.RMProxy: Connecting to ResourceManager at bigdata01/192.168.35.100:8032
    <k3,v3>=<hello,2>
    <k3,v3>=<me,1>
    <k3,v3>=<you,1>
    

在这里插入图片描述

3.32 停止Hadoop集群中的任务
yarn application -kill application_1642407336043_0002

如果需要对执行时间很长的mapreduce任务进行修改,可以使用命令中止任务。

3.33 MapReuduce扩展

MapReduce任务分为Map 和Reduce ,Reduce任务并不是必需的。

//禁用reduce阶段 
job.setNumReduceTasks(0)

具体代码见 https://github.com/Adopat/hadoop_demo

# 只有map 阶段的MapReduce任务输出 k2,v2 
[root@bigdata01 hadoop-3.2.0]# hdfs dfs -cat /out1/part-m-00000
hello   1
you     1
hello   1
me      1
[root@bigdata01 hadoop-3.2.0]# hdfs dfs -ls /out
Found 2 items
-rw-r--r--   2 root supergroup          0 2022-01-18 09:24 /out/_SUCCESS
-rw-r--r--   2 root supergroup         19 2022-01-18 09:24 /out/part-r-00000
You have new mail in /var/spool/mail/root
# Map + Reduce 阶段的MapReduce任务输出 k3,v3
[root@bigdata01 hadoop-3.2.0]# hdfs dfs -cat /out/part-r-00000
hello   2
me      1
you     1
[root@bigdata01 hadoop-3.2.0]# 

注意只有map 阶段的MapReduce的输出文件是带有m,如part-m-00000,含有map,reduce两个阶段的MapReduce的输出文件是带有r。如part-r-00000

3.4 Shuffle执行过程及源码分析

3.4.1 Shuffle执行过程

shuffer是一个网络拷贝的过程,是指通过网络把数据从map 端拷贝到reduce端的过程。shuffle其实是横跨map端和reduce端的,它主要是负责把map端产生的数据通过网络拷贝到reduce阶段进行统一聚合计算。

3.4.2 Hadoop中的序列化和反序列化

因为MapReduce是一种移动计算的方式,所以影响MapReduce的主要执行效率是磁盘IO。磁盘IO涉及到序列化和反序列化。所以为了优化MapReduce执行效率可以从序列化和反序列化入手,采用Hadoop序列化。

  • 序列化

    序列化是指把内存中的对象信息转换成二进制的形式,方便存储到文件中或者方便传输。

  • 反序列化

    反序列化指的是把文件中的信息加载到内存,方便计算。

  • JAVA 序列化

    package com.imooc.mr;
    
    import java.io.*;
    
    /**
     * 使用Java 序列化生成文件,用于比较和Hadoop 序列化生成文件的区别,主要是存储空间大小,生成文件 StudentJava.txt 185字节
     * 单独执行时需要将pom.xml中的provide 属性注释,否则会提示找不到对应的包
     */
    public class JavaSerialize {
        public static void main(String[] args) throws Exception {
            //创建对象
            StudentJava studentJava = new StudentJava(1L,"justin");
            // 序列化
            FileOutputStream fos = new FileOutputStream("E:\\StudentJava.txt");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(studentJava);
            oos.close();
            fos.close();
            //反序列化,从文件中加载到内存
            FileInputStream fis = new FileInputStream("E:\\StudentJava.txt");
            ObjectInputStream ois = new ObjectInputStream(fis);
            StudentJava studentJava1 = (StudentJava) ois.readObject();
            System.out.println("反序列化后的对象是:"+studentJava1);
            ois.close();
            fis.close();
    
    
        }
    }
    
    /**
     * 实现 Serializable 接口代表 StudentJava 类是可以序列化和反序列化的
     */
    class StudentJava implements Serializable{
        // 设置版本号
        private static final long serialVersionUID = 1L;
        private Long id ;
        private String name;
    
        public StudentJava(Long id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "StudentJava{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • Hadoop 序列化

    package com.imooc.mr;
    
    import org.apache.hadoop.io.Writable;
    
    import java.io.*;
    
    /**
     * 使用Hadoop 序列化生成文件,用于比较和Java 序列化生成文件的区别,主要是存储空间大小 生成文件 HadoopStudent.txt 22字节
     * 单独执行时需要将pom.xml中的provide 属性注释,否则会提示找不到对应的包
     */
    public class HadoopSerialize {
        public static void main(String[] args){
            // 执行序列化方法
            //serialization();
            // 执行反序列化方法
            deserialization();
        }
        /**
         * 序列化方法 ,从内存保存到文件
         */
        public static  void serialization(){
            try (FileOutputStream fos = new FileOutputStream("E:\\HadoopStudent.txt");
                 ObjectOutputStream oos = new ObjectOutputStream(fos)
                    ){
                // 创建对象
                StudentWritable studentWritable = new StudentWritable(1L,"justin");
                studentWritable.write(oos);
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
        /**
         * 反序列化方法 ,从文件中加载到内存
         */
        public static void deserialization(){
            try (FileInputStream fis = new FileInputStream("E:\\HadoopStudent.txt");
                 ObjectInputStream ois = new ObjectInputStream(fis)){
                StudentWritable studentWritable1 = new StudentWritable();
                studentWritable1.readFields(ois);
                System.out.println(studentWritable1);
            }catch (Exception e){
                e.printStackTrace();
            }
    
        }
    }
    class StudentWritable implements Writable{
        private Long id;
        private String name;
    
        public StudentWritable() {
        }
    
        public StudentWritable(Long id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    
        @Override
        public void write(DataOutput out) throws IOException {
            out.writeLong(this.id);
            out.writeUTF(this.name);
        }
    
        @Override
        public void readFields(DataInput in) throws IOException {
            this.id = in.readLong();
            this.name = in.readUTF();
    
        }
    
        @Override
        public String toString() {
            return "StudentWritable{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • Hadoop 序列化的优势

    Java中的序列化会占用较大的存储空间,而Hadoop中的序列化可以节省很多存储空间,这样在海量数据计算的场景下,可以减少数据传输的大小,极大的提高计算效率。

3.4.3 InputFormat分析
  • 类继承关系
    在这里插入图片描述

  • InputFormat

    InputFormat描述Map-Reduce作业的输入规范。Map-Reduce框架依赖于作业的InputFormat来:将输入文件分割成逻辑的inputsplit,然后将每个inputsplit分配给单个Mapper。提供RecordReader实现,用于从逻辑InputSplit收集输入记录,以供Mapper处理。

    /**
    *在逻辑上分割作业的输入文件集。
    *然后将每个InputSplit分配给一个单独的Mapper进行处理。
    *getSplit() 在其子类FileInputFormat中重写
    */
    public abstract 
        List<InputSplit> getSplits(JobContext context
                                   ) throws IOException, InterruptedException;
    /**
    *针对每一个InputSplit生成 k1,v1 在TextInputFormat中有重写改方法。
    */
    public abstract 
        RecordReader<K,V> createRecordReader(InputSplit split,
                                             TaskAttemptContext context
                                            ) throws IOException, 
                                                     InterruptedException;
    
  • FileInputFormat(操作文件类型数据)

    • CombineFileInputFormat

      处理小文件,用于解决小文件存储

    • TextInputFormat

      是默认的处理类,处理普通文本文件,他会把文件中每一行作为一个记录,将每一行的起始偏移量作为key,每一行的内容作为value,这里的key和value就是我们之前所说的k1,v1
      它默认以换行符或回车键作为一行记录

    • NLineInputFormat

      可以动态指定一次读取多少行数据

  • DBInputFormat(操作数据库)

  • DelegatingInputFormat(处理多个输入)

3.4.4 InputFormat中注意事项
  • block 数量和InputSplit数量不相等

    前面讲过一个Block块大小等于一个InputSplit大小,这种说法是不严谨的,也就是说block数量和InputSplit数量是不对等。(文件剩余大小/128)>1.1 继续切割,(文件剩余大小/128)<1.1部分割。比如一个140M和141M的文件都是两个Block,但是140M文件只会产生一个split,141M文件会产生两个split,对应两个map任务。允许10%溢出。

    private static final double SPLIT_SLOP = 1.1;   // 10% slop
    

    FileInputFormat.java 中 getSplits()有定义

  • 一行数据被拆分到两个split会不会有问题

    不会。如果这个InputSplit不是第一个InputSplit,我们将会丢掉读取出来的第一行 因为我们总是通过next()方法多读取一行(会多读取下一个InputSplit的第一行)

       // If this is not the first split, we always throw away first record
        // because we always (except the last split) read one extra line in
        // next() method.
        if (start != 0) {
          start += in.readLine(new Text(), 0, maxBytesToConsume(start));
        }
        this.pos = start;
      }
    

    源码LineRecorderReader.java

3.4.5 OutputFormat分析
  • 类继承关系
    在这里插入图片描述

  • OutputFormat

    OutputFormat描述Map-Reduce作业的输出规范。Map-Reduce框架依赖于作业的OutputFormat:验证作业的输出规范。例如,检查输出目录是否已经存在。提供RecordWriter实现,用于写出作业的输出文件。输出文件存储在文件系统中。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值