hadoop框架之MapReduce介绍

  1. MapReduce简单介绍
    MapReduce是一个分布式的计算框架,核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算框架,并发运行在hadoop集群上。引入MapReduce框架后,开发人员可以将绝大部分的工作集中于业务逻辑上的开发,具体的计算只需要交给框架就可以。用于处理海量的数据分析计算工作,但目前因为性能问题,正在被spark替代。
  2. MapReduce过程介绍
    MapReduce分为两个阶段(细分是5部分,下面详细介绍),map阶段和reduce阶段。
    map阶段:map任务是并行执行的,互不干扰,map任务是需要容器(就是包含内存、磁盘资源的容器),一个切片对应一个map任务,map任务是由application master(负责任务调度,也就是map任务和reduce任务的调度执行)根据切片数量计算出来的,由resourceManager(负责资源调度)分配给map、reduce任务nodeManager(类似于datanode,nodeManager包含容器container),所有的map任务并行执行。切片中的一行数据就会调用一次map方法(map方法框架默认会对其进行排序)。
    reduce阶段:reduce阶段的计算都依赖于map阶段的输出结果,所以reduce是在所有的map任务执行完成后,才开始执行的。reduce会从每个nodeManager上拉取map的输出结果(这就会产生网络io)到本地,因为中间有一个shuffle阶段(下面会详细介绍),所以到达reduce阶段的数据是分好组的、分好区的、有序的。一组数据会调用一次reduce方法来完成相应的逻辑。
  3. MapReduce编程规范
    用户编写的程序分为三部分:Mapper、Reducer、Driver(驱动)
    Mapper阶段:
    自定义的map类要继承Mapper类
    Mapper的输入输出数据是KV对的形式(Mapper的输入数据始终都是LongWritable(偏移量)和Text(文本内容))
    Mapper的业务逻辑都编写在map方法中
    每个KV对(也就是一行)调用一次map方法
    Reducer阶段:
    自定义的reduce类要继承Reducer类
    Reducer的输入类型是map阶段的输出类型,输出类型是根据自己的业务逻辑自定义
    Reducer的业务逻辑都编写在reduce方法中
    每一组相同的key调用一次reduce方法
    Driver阶段:
    整个程序只需要一个Driver来提交,提交的是一个描述了各种必要信息的job对象。这里详细介绍一下这个驱动程序编写过程。
    1)首先要new一个configuration(配置文件)对象conf
    2)如果要在hdfs上运行,就需要用conf调用set方法设置(参数是:k是fs.defaultFS,value是:hdfs://192.168.9.25:9000)
    3)通过Job类调用getInstance方法获取到Job类的对象job
    4)通过job来调用setJarByClass方法来把当前程序注册给master(参数是:当前自定义类的class对象,通过反射来拿到,也就是.class)
    5)通过job调用setMapperClass来把自定义的Mapper注册给master(参数是:自定义Mapper类的class对象)
    6)通过job调用setMapOutputKeyClass和setMapOutputValueClass方法把mapper的输出key和value类型注册给master
    7)通过job调用setReducerClass把自定义的Reducer注册给master(参数是:自定义的Reducer类的class对象)
    8)通过job调用setOutputKeyClass和setOutputValueClass把reducer的输出类型注册给master
    9)通过FileInputFormat的addInputPath方法(参数是:job和一个path路径,程序读取数据的路径)指定程序的输入路径
    10)通过FileInputFormat的setOutputPath方法(参数是:job和一个path路径,程序输出数据的路径)指定程序的输出路径
    11)job调用waitForCompletion(参数是true,内部对submit方法进行了封装)来提交job
  4. MapReduce具体执行过程详解(涉及了yarn)
    1)MapReduce读取文件,将大文件切割成一个一个的切片(也就是小文件,默认大小是128M,可以自定义,但如果超过128M就可能出现跨机器的情况,因为每个DN只保留一个block块对应一个block块)。切片是逻辑概念上的,就是对文件做一个任务规划。
    2) 在客户端提交job任务之前获取待处理的数据信息,根据集群参数,形成一个任务规划,
    3)然后client将这些切片信息(job.split、job.xml、jar包等文件)提交给yarn,yarn中的resourceManager启动applicationMaster(rM会在一个相对空闲的NodeManager上将applicationMaster作为一个任务分配给他一个容器,让他运行)
    4)applicationMaster会根据切片信息计算出对应的mapTask数量,然后向rM申请分配对应数量的NodeManager(容器container)来运行mapTask
    5)默认TextInputFormat的子类RecorderReader会把数据处理为KV对的形式,让mapTask来使用
    6)调用map方法来做逻辑运算,会把结果输出到一个环形缓冲区
    7)每当这个环形缓冲区达到80%的时候,就会触发溢写,形成小文件(这些小文件中的数据是按分区分的,分区内部是按字典顺序排序的)
    8)这时会有一个大合并,就是把每个输出来的小文件按照分区进行合并(分区内依旧是按照字典顺序进行的排序),这样按照分区排序后,如果有多个reduce任务调用,reduce就知道要调用那个分区的数据进行处理(因为这里的分区号标记是按照每行数据的key的hashcode码%reduce的数量得到的,这里的分区规则是可以自定义的,reduce也是可以自己设置的)
    9)reduce把对应分区的所有的map结果拉取到本地后,还会进行一次合并,这次合并的是所有的数据,是按照相同的key为一个组,迭代器中是标记数,
    10)每一个组调用一次reduce方法,进行相关的业务逻辑处理,将reduce处理完后的数据进行输出,默认是TextOutputFormat的子类RecordWriter进行输出,每个reduce对应一个part类型的文件
    注:shuffle阶段其实就是7-9这个阶段,所以排序阶段都是shuffle阶段,拉取map结果是处于shuffle阶段,所以网络io都是shuffle阶段产生的,排序、分区、分组也都是shuffle阶段产生的。特别需要注意的是,reduce阶段的拉取数据,是先拉取到内存中的,当内存达到一定的阀值后,才溢写到磁盘上的,如果到最后剩余的不够阀值,仍旧是保留在内存中的,如果磁盘上文件数目达到一定阈值,则进行一次合并以生成一个更大文件,当所有数据拷贝完毕后,Reduce Task统一对内存和磁盘上的所有数据进行一次合并。
  5. MapReduce的序列化
    序列化就是把内存中的对象,转换成字节序列,以便于持久化到磁盘和网络传输
    反序列化就是将收到的字节序列或硬盘持久化数据,转换为内存中的对象
    hadoop有自己的一套序列化机制:除了String的类型是Text外,其他都是类型首字母大写加上Writable,例如LongWritable
    往往自定义bean对象,想要实现网络传输是,要实现序列化接口,有以下注意事项:
    1)实现Writable接口
    2)反序列化时,需要反射调用空参构造函数,所以必须有空参构造
    3)重写序列化方法
    4)重写反序列化方法
    5)序列化与反序列化的顺序必须一致
    6)如果需要将自定义的bean放在key中传输,还需要实现comparable接口,因为MapReduce框架中的shuffle阶段一定会对key进行排序
  6. MapReduce框架中的一些机制介绍
    1)shuffle阶段其实是最重要的,我们已经做了详细的介绍
    2)自定义分区:通过继承Partitioner类,重写getPartition方法来自定义分区,并在Driver中通过job设置自定义分区类,即job.setPartitionerclass(参数是:自定义Partitioner类的class对象),job.setNumReduceTask来设置reduce的数量,一般是和分区数对应的,否则会出现一些问题。(如果设置的reduce数量过多,会导致有些输出的part文件中没有数据,设置的少于分区数,会导致一部分数据无处安放,导致异常。默认reduce个数是一,如果有多个分区文件,也都会走这一个,如果有时候不需要reduce阶段(shuffle阶段),可以设置reduce个数为0,但这样shuffle阶段也就没有了,意味着不会对数据进行排序等工作)
    3)WritableComparable排序:让自定义bean对象实现该接口,并重写compareTo方法,可采用三目运算,返回-1是从大到小排序,返回1是从小到大排序
    4)GroupingComparator分组:对reduce阶段数据根据某个字段进行分组
    5)Combiner合并:是Mapper和Reducer之外的一种组件,父类是Reducer,区别在于,combiner运行在mapTask节点上,也就是提前对map阶段输出的数据进行一次业务逻辑处理(局部汇总),代码和reducer阶段的一模一样,继承Reducer类,重写reduce方法,需要在Driver中设置,job.setCombinerClass(参数是:自定义combiner类),但combiner类的使用是分场景的,前提是不能影响最终的业务逻辑,输入输出类型必须一致
    6)数据倾斜:举个例子给大家说明数据倾斜问题:比如有10个mapTask但只有一个ReduceTask,这就会导致reduceTask的压力特别大,就容易导致数据倾斜。解决方法:提前处理业务逻辑,减少reduce的压力(具体是:在mapper的setup阶段,将文件读取到缓存集合中,在驱动函数中加载缓存)
  7. 关于yarn的运行过程
    其实在上面的MapReduce具体执行过程详解中的1-5就是yarn的部分过程,剩下的是等到所有的mapTask执行完成后,将资源释放,application master再向rM申请NodeManager(容器container)来运行reduceTask任务,等到reduceTask任务也执行完成后,释放资源,application master向rM汇报,所有任务都执行完成,然后也释放自己的资源。

你可能感兴趣的:(hadoop框架)