Hadoop学习笔记总结

01. MapReduce

1. Combiner(规约)

Combiner号称本地的Reduce。

问:为什么使用Combiner?

答:Combiner发生在Map端,对数据进行规约处理,数据量变小了,传送到reduce端的数据量变小了,传输时间变短,作业的整体时间变短。减少了reduce的输入。

问:为什么Combiner不作为MR运行的标配,而是可选步骤哪?

答:因为不是所有的算法都适合使用Combiner处理,例如求平均数。使用了规约,造成了最终结果的不同。

问:Combiner本身已经执行了reduce操作,为什么在Reducer阶段还要执行reduce操作哪?

答:combiner操作发生在map端的,处理一个任务所接收的文件中的数据,不能跨map任务执行;并且,处理的是局部有序数据,不是全局有序数据。

  1. job.setCombinerClass(MyCombiner.class);//其实就是一个另一个Reducer函数

2. Shuffle机制

Shuffle描述着数据从map task输出到reduce task输入的这段过程。(包括了分组、排序、规约和缓存机制)

(1) split切片概念
  1. map的数量不是由Block块数量决定,而是由split切片数量决定。
  2. 切片是一个逻辑概念,指的是文件中数据的偏移量范围
  3. 切片的具体大小应该根据所处理的文件的大小来调整。
  4. 最佳的分片大小应该与块相同。因为如果跨越了两个数据块,节点一般不会同时存储这两个块,因而会造成网络传输,较低效率。
  5. 数据本地化优化。(1)、避免了调用同一个机架中空闲机器运行该map任务,(2)、其它机架来处理(小概率)。浪费集群宽带资源。
(2) shuffle流程

Map端:分区排序规约

  1. 每个map有一个环形内存缓冲区,用于存储任务的输出。默认大小100MB(io.sort.mb属性),一旦达到阀值0.8(io.sort.spill.percent),一个后台线程把内容写到(spill)磁盘的指定目录(mapred.local.dir)下的新建的一个溢出写文件。
  2. 写磁盘前,后台线程会根据将要传入的reducer将数据分区partition,排序sort。一个个小spill的写,写满了就再新建一个溢出文件(spill file)。在每个分区中,后台线程会按键进行内排序,再写入磁盘spill。如果有combiner,运行combine使得map的输出数据更加紧凑,减少传递到磁盘和传递给reduce的数据。
  3. 等最后记录写完,合并全部溢出写文件为一个已分区且已排序的文件。combiner也会在写到磁盘之前再次执行。

    map总结:map输出-->环形缓冲区-->分区,排序,规约(如果有)-->小spill文件-->合并到磁盘。

Reduce端:复制,合并排序

  1. reduce任务的复制阶段。Reducer通过Http方式得到输出文件的分区。(通过appMaster告知)。少量的复制线程并行获取map的输出,每个map完成时间不同,只要一个map完成,reduce就开始复制输出。
  2. NodeManager为运行Reduce任务。复制阶段把Map输出(整体溢出文件)复制到Reducer的内存或磁盘(由map输出大小决定)。
  3. 排序阶段。合并map输出在Reducer的内存或磁盘中(关于为什么内存和磁盘都有,日后总结)。然后也是基于key值得排序,将局部有序的数据合并成全局有序的数据。然后执行reduce方法。

    reduce总结:复制map输出,合并排序

总结,shuffle流程的过程其实就是,分组+排序(map和reduce都有排序)的过程,还有在map和reduce端的缓存机制。

在map端的排序和reduce端的排序是分别有序和整体排序的关系。

想要优化整个过程,可以增大内存,减少溢出文件的IO和提高溢出阀值等

(3) MRAppMaster的任务监控调度机制,带shuffle机制的job流程图



和之前Yarn框架管理,组成了整个MapReduce资源的分配和任务调度的过程。

3. Partitioner分区和ReduceTasks数量

Map的结果,会通过partition函数分发到Reducer上。它的作用就是根据key或value及reduce的数量来决定当前的这对输出数据最终应该交由哪个reduce task处理。

默认的HashPartitioner

  1. public class HashPartitioner<K, V> extends Partitioner<K, V> {
  2. public int getPartition(K key, V value,
  3. int numReduceTasks) {
  4. return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
  5. }
  6. }

即:key运算结果相同的被分到同一组,哪个key到哪个Reducer的分配过程,是由Partitioner规定的。

输入是Map的结果对<key, value>和Reducer的数目,输出则是分配的Reducer(整数编号)。就是指定Mappr输出的键值对到哪一个reducer上去。

总结:分区Partitioner主要作用在于以下两点

(1)根据业务需要,产生多个输出文件;

(2)多个reduce任务并发运行,提高整体job的运行效率

  1. job.setPartitionerClass(AreaPartitioner.class);
  2. job.setNumReduceTasks(3);

Ps:当分组有6个。当ReduceTask任务有10个时,只有6个输出文件有数据,其它的为空文件;当Task任务时5个时,报错,因为有的分组找不到reduce函数。

4. 自定义分区、排序、分组的总结

在第3节已经讲述了分区(partition)的概念,在分区过后,每个分区里面的数据按照key进行排序、分组。排序会在map和reduce过程中多次调用。在reduce端排序之后,具有相同Key值的记录是属于同一个分组的传入reduce()方法。

下面以二次排序SecondSort为例讲解:

job.setSortComparatorClass()

job.setGroupingComparatorClass()

1.自定义key

创建的自定义key:NewPairKey需要实现的接口:WritableComparable,可序列的并且可比较的

  1. static class NewPairKey implements WritableComparable<NewPairKey> {
  2. int first = 0;
  3. int second = 0;
  4. //空构造器和含参构造器
  5. //序列化,将IntPair转化成使用流传送的二进制
  6. public void write(DataOutput out) throws IOException {}
  7. //反序列化,从流中的二进制转换成IntPai
  8. public void readFields(DataInput in) throws IOException {}
  9. // NewPairKey的比较逻辑
  10. public int compareTo(NewPairKey o) {}
  11. // 新建的类应该重写的方法
  12. public String toString() {}
  13. public boolean equals(Object obj) {}
  14. public int hashCode() {}
  15. }
2.排序的比较逻辑

Key排序的规则:

  1. 如果设置了job的setSortComparatorClass(KeyComparator.class),作为key比较函数类

    1. public static class KeyComparator extends WritableComparator{}
  2. 如果没有设置,则系统会自动使用自定义类NewPairKey中实现的compareTo()方法作为key比较的。上面的方法不是必要的。但是都是构造了新的比较逻辑。

    1. // 当NewPairKey进行排序时,会调用该方法,第一列升序排列,第一列不同时,第二列升序排列。
    2. @Override
    3. public int compareTo(NewPairKey o) {
    4. int minus = this.first - o.first;
    5. if (minus != 0) {
    6. return minus;
    7. }
    8. return this.second - o.second;
    9. }

Ps:上面的compareTo(NewPairKey o)参数类型是NewPairKey,需要将字节流反序列化成NewPairKey对象再比较,造成了新建对象的额外开销。

RawComparator接口允许其实现直接比较数据流中的记录,无需先把数据流反序列化为对象,加速比较!。

如下面IntWritable默认是实现源码。

  1. public class IntWritable implements WritableComparable<IntWritable> {
  2. private int value;
  3. public IntWritable() {}
  4. public IntWritable(int value) {
  5. set(value);
  6. }
  7. /** Set the value of this IntWritable. */
  8. public void set(int value) {
  9. this.value = value;
  10. }
  11. /** Return the value of this IntWritable. */
  12. public int get() {
  13. return value;
  14. }
  15. @Override
  16. public void readFields(DataInput in) throws IOException {
  17. value = in.readInt();
  18. }
  19. @Override
  20. public void write(DataOutput out) throws IOException {
  21. out.writeInt(value);
  22. }
  23. /** Returns true iff <code>o</code> is a IntWritable with the same value. */
  24. @Override
  25. public boolean equals(Object o) {
  26. if (!(o instanceof IntWritable))
  27. return false;
  28. IntWritable other = (IntWritable) o;
  29. return this.value == other.value;
  30. }
  31. @Override
  32. public int hashCode() {
  33. return value;
  34. }
  35. /** Compares two IntWritables. */
  36. @Override
  37. public int compareTo(IntWritable o) {
  38. int thisValue = this.value;
  39. int thatValue = o.value;
  40. return (thisValue < thatValue ? -1 : (thisValue == thatValue ? 0 : 1));
  41. }
  42. @Override
  43. public String toString() {
  44. return Integer.toString(value);
  45. }
  46. /** A Comparator optimized for IntWritable. */
  47. public static class Comparator extends WritableComparator {
  48. public Comparator() {
  49. super(IntWritable.class);
  50. }
  51. @Override
  52. public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
  53. int thisValue = readInt(b1, s1);
  54. int thatValue = readInt(b2, s2);
  55. return (thisValue < thatValue ? -1 : (thisValue == thatValue ? 0
  56. : 1));
  57. }
  58. }
  59. static { // register this comparator
  60. WritableComparator.define(IntWritable.class, new Comparator());
  61. }
  62. }

从IntWritable 的例子中,可以看到:

1、把WritableComparator当IntWritable的内部类,写到里面

2、如何注册一个自定义的WritableComparator。

此处注意一下,关注static { WritableComparator.define(IntWritable.class,new Comparator())} 的位置。

参看《http://blog.itpub.net/30066956/viewspace-2112283/》

3.分组函数类

WritableComparator是RawComparator接口的实现。功能:1.提供了原始compare()方法的一个默认实现;2.和充当RawComparator实例的工厂(《权威指南P105》)。和WritableComparable区别详情见《http://www.cnblogs.com/robert-blue/p/4159434.html》

job.setGroupingComparatorClass(MyGroupingComparator.class).

  1. static class MyGroupingComparator implements RawComparator<NewPairKey> {
  2. @Override
  3. public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
  4. return WritableComparator.compareBytes(b1, s1, Integer.SIZE / 8,
  5. b2, s2, Integer.SIZE / 8);
  6. }
  7. @Override
  8. public int compare(NewPairKey arg0, NewPairKey arg1) {
  9. return arg0.first - arg1.first;
  10. }
  11. }

总结

分组:

  1. 实际上,没有使用自定义分组也能实现二次排序逻辑。
  2. 使用了自定义分组后,分组的逻辑变成不再以整个NewPairKey作为比较,而是以fisrt为标的,把一些相同键的键值进行合并组,测试可知:Reduce input groups=16变成了4。
  3. compare方法需要知道比较的是Int型数据,就是4个字节的比较,Long型数据就是8个字节的比较。

分区和分组的区别:

  1. 分区是对输出结果文件进行分类拆分文件以便更好查看,比如一个输出文件包含所有状态的http请求,那么为了方便查看通过分区把请求状态分成几个结果文件。
  2. 分组就是把一些相同键的键值对进行进行合并组;分区之后数据全部还是照样输出到reduce端,而分组的话就有所减少了。当然这2个步骤也是不同的阶段执行。分区是在map端执行,环形内存缓冲区输出到溢出文件的时候就是按照分区输出;分组是在reduce端。

Group Comparator的执行过程:

  1. 在reduce阶段,reducer接收到所有映射到这个reducer的map输出后,调用key比较函数类对所有数据排序得到全局有序数据。然后开始构造一个key对应的value迭代器。这时就要用到分组,使用job.setGroupingComparatorClass设置的分组函数类。只要这个比较器比较的两个key相同,他们就属于同一个组,它们的value放在一个value迭代器。

需要Group Comparator吗?

  1. 问:mapper产生的中间结果经过shuffle和sort后,每个key整合成一个记录,每次reduce方法调用处理一个记录,但是group的目的是让一次reduce调用处理多条记录,为什么?
  2. 答:如果不用分组,那么同一组的记录就要在多次reduce方法中独立处理,那么有些状态数据就要传递了,就会增加复杂度,在一次调用中处理的话,这些状态只要用方法内的变量就可以的。

关于WritableComparable、RawComparator和WritableComparator联系与区别下回再总结了。

参考《Hadoop权威指南》

http://www.cnblogs.com/robert-blue/p/4159434.html》

http://blog.itpub.net/30066956/viewspace-2112283/》

初接触,记下学习笔记,还有很多问题,望指导,谢谢。

Hadoop_MapReduce流程的更多相关文章

  1. Linux下服务器端开发流程及相关工具介绍(C++)

    去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...

  2. 基于netty http协议栈的轻量级流程控制组件的实现

    今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...

  3. 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

    在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...

  4. nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...

  5. 8、Struts2 运行流程分析

    1.流程分析: 请求发送给 StrutsPrepareAndExecuteFilter StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 ...

  6. Hawk 6. 高级话题:子流程系统

    子流程的定义 当流程设计的越来越复杂,越来越长时,就难以进行管理了.因此,采用模块化的设计才会更加合理.本节我们介绍子流程的原理和使用. 所谓子流程,就是能先构造出一个流程,然后被其他流程调用.被调用 ...

  7. 流程开发Activiti 与SpringMVC整合实例

    流程(Activiti) 流程是完成一系列有序动作的概述.每一个节点动作的结果将对后面的具体操作步骤产生影响.信息化系统中流程的功能完全等同于纸上办公的层级审批,尤其在oa系统中各类电子流提现较为明显 ...

  8. zookeeper源码分析之五服务端(集群leader)处理请求流程

    leader的实现类为LeaderZooKeeperServer,它间接继承自标准ZookeeperServer.它规定了请求到达leader时需要经历的路径: PrepRequestProcesso ...

  9. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

随机推荐

  1. [deviceone开发]-QQ分享、微信分享和新浪微博分享

    一.简介 该demo主要实现QQ分享.微信分享和新浪微博分享.(调试包请到论坛扫描对应二维码下载) 二.效果图 三.相关讨论 http://bbs.deviceone.net/forum.php?mo ...

  2. Asp.net EasyUI中的combogrid实现分页功能

    在jquery.easyUI.js 要实现分页,必须在后台接收参数时声明两个变量:page(当前第几页),rows(每页显示多少条信息),否者easyUI前台传递不了分页参数. 这两个属性的名称在ea ...

  3. 制作具有SSH、MySQL功能的Chroot

    由于工作需求,需要在Linux上建立SSH.MySQL两个用户. 使这两个账户连接到跳板机后仅能执行有限的命令(SSH用户只能执行SSH命令,MySQL用户只能执行MySQL命令). MySQL账户C ...

  4. PhpStorm下Laravel代码智能提示

    phpstorm&Laravel PHPstorm是我见过的最好的PHP的IDE,前年用的时候就毫不犹豫的抛弃了zend studio :) ,Laravel是我用过最好的框架,除了做手游后台 ...

  5. 需要记住的几个ASCII码

    --------- A--------- a---------

  6. android加固系列—1.如何检验so文件是否加壳成功

    程序对so文件加壳后,如何验证是否加壳成功呢,首先除了能在应用中正常运行外,还要用IDA来检测: 绿色乱码表示rr这个函数成功加密: 工具下载,可支持动态调试版:

  7. iOS文件解压&&数据加密

    一文件压缩.这里我们需要一个第三方SSZipArchive(需要添加libz.td) #import "ViewController.h" #import "SSZipA ...

  8. Android开发拾遗(一)用Wi-Fi连接adb

    可以用Wi-Fi通过标准的TCP/IP连接来连接Android设备. 这在开发监听USB相关事件的应用程序时会特别有用,原因是USB连接会起到干扰作用,比如USB的连接/断开事件. 首先要像通常一样用 ...

  9. 开发笔记之NSTable 排序

    (1)第一步设置一下button IBOutlet NSButton * nameOrderBT; IBOutlet NSButton * sizeOrderBT; (2)切换设置切换相遇函数 -(I ...

  10. PMBOK学习笔记二-项目管理过程

    项目管理过程可归纳为五类,即五大项目管理过程组 启动过程组.定义一个新项目或现有项目的一个新阶段,授权开始该项目或阶段的一组过程..规划过程组.明确项目范围,优化目标,为实现目标制定行动方案的一组过程 ...