一、先看简单理解

对于hadoop的map端配置项"mapreduce.task.io.sort.mb"和"mapreduce.map.sort.spill.percent"应该都比较熟悉了,如图解释(http://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html):

翻译成汉语的解释也有不少,随便粘一个"mapreduce.task.io.sort.mb 任务内部排序缓冲区大小,默认100","mapreduce.map.sort.spill.percent Map阶段溢写文件的阈值(排序缓冲区大小的百分比),默认0.8,也就是80%"。这些内容一点都不难理解,就是map的结果先放入缓冲区(其实先序列化),当缓冲区的数据量达到阈值时(默认100M * 0.8 = 80M),溢出行为会在一个后台线程执行开始spill操作。

二、发现问题

问题的核心在于mapreduce.map.sort.spill.percent这个配置。

前两天浏览官网(http://hadoop.apache.org/docs/current/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html)时在上述两条配置的说明下面还有两条注意事项,其中主要疑问在第一条,如图:

只看第一条就行,我开始对这条英文的理解比较模糊,于是上网找了一些官网的翻译(http://blog.csdn.net/u011812294/article/details/53379338?locationNum=8&fps=1),如图:

虽然我对这段英文理解的比较模糊,但是也能看出这个翻译是明显错误的(只是就事论事,没有批评作者的意思,毕竟官网那么多英文都大体翻译了过来已经很了不起了,谁都有没有理解不是很透彻的地方,而且好多内容我都是看这篇翻译弄懂的),那到底会不会起额外的线程呢,spill到磁盘的数据是只有percent * buffer的数据量呢还是另有门道呢?这算是"疑问一"。于是我就又看了官网的mapred-default.xml文件对这个配置的说明,如图:

这解释前半部分没什么说的,后半部分透露出一个信息,当percent小于0.5的时候spill到磁盘的数据量有可能会大于percent * buffer,这又是为什么呢?这算是"疑问二"。下面就是我通过大量的请教别人和上网查资料之后,对这两个问题的解释。

三、解释两个疑问

对我帮助最大的应该是这篇文章"http://www.tuicool.com/articles/7FNN32",对此作者表示由衷的感谢。

要解释这两个疑问需要用到再稍微深一层的知识,我再这里只对深一层的东西做一个简单的抽象,能解决问题就行,例如实际有3个环形缓冲区,我只抽象成一个。

简单来说,map的输出到一个缓存区(中间还有个序列化过程不讨论),这个环形缓冲区有三个索引(或者指针),分别叫kvStart、kvEnd、kvIndex。map结果每进来一个keValue对,kvIndex更新一次,也就是始终标识下一个可用的地址,此时还没达到阈值,没有开始spill,kvStart=kvEnd,指向标识当spill开始时的起始位置,然后,阈值到了,后台开启一个spill线程,此时,kvStart暂时不变,而对kvEnd进行重新赋值,kvEnd=kvIndex,然后kvIndex不受影响继续随着map结果的写入而不断更新直到缓冲区满了为止。此时,spill要处理的数据其实已经确定了,就是kvStart到(kvEnd-1)这区间的数据进行溢写,此过程kvEnd正常情况不变,而每溢写成功一条数据kvStart好像是更新一次,直到最后spill成功时kvStart=kvEnd,为下一次的spill做准备。

上一段描述的是一般情况下的spill过程抽象,但是还是不能解释我提的两个疑问,其实从英文解释可以看的出我的两个疑问是些另外的情况,而这另外的情况指的就是percent小于0.5的可能会发生的情况。下面举例子说明这种情况。

例如mapreduce.map.sort.spill.percent=0.33,当缓冲区第一次达到阈值时,启动一个后台spill线程开始正常的溢写操作,由于缓冲区没满,map结果继续写入缓冲区(这一过程称为Collect),当又一个0.33*buffer被写入之后,便再次触发溢写过程,但是此时不会另外启动一个spill线程,不过呢kvEnd会被重新赋值,kvEnd被重新赋值之后呢,kvStart要到达新的kvEnd时才能结束,这样两次触发一共要处理的数据总量就是2 * 0.33 * buffer=0.66 * buffer了(当然也有可能出现3次出发,结果0.99 * buffer的情况),但是多次触发很明显只能是percent<0.5的情况,因为当percent>0.5时剩余buffer即使满了也达不到0.5,不能触发。

这样就很好的解释了这段英文"Note that collection will not block if this threshold is exceeded while a spill is already in progress, so spills may be larger than this threshold when it is set to less than .5"

还有一句英文"and the remainder of the buffer is filled while the spill runs, the next spill will include all the collected records, or 0.66 of the buffer, and will not generate additional spills",我感觉这还真有点灵活翻译理解的味道,关键字在于对"or"的理解,我认为翻译成"或者"并不是很恰当,而应该翻译成"也就是",换句话说,"0.66"就是对前面"all the collected records"用一种说法做的说明,而不是指的两种情况,当然"0.66"更不是指的percent=0.66了。至此,两个疑问都解释清楚了,当然底层模型我抽象的比较简单,要了解真正的过程看对我帮助最大的那篇文章,解释的非常详细,不过内容也十分多,需要自己找。

四、对spill过程中的锁做一个简单的摘抄,也来自文章"http://www.tuicool.com/articles/7FNN32"

两种信号:spillDone和spillReady,它们的逻辑如下:

1)对于写线程来说,如果写满了,就调用spillDone.await等待spillDone信号;否则不断往缓冲区里面写,到了一定程度,就发送spillReady.signal这个信号给读线程,发完这个信号后如果缓冲区没满,就释放锁继续写(这段代码无需锁),如果满了,就等待spillDone信号;

2)对于读线程来说,在平时调用spillReady.await等待spillReady这个信号,当读取之后(此时写线程要么释放锁了,要么调用spillDone.await在等待了,读线程肯定可以获得锁),则把锁释放掉,开始Spill(这段代码无需锁),完了读线程再次获取锁,修改相应参数,发送信号spillDone给写线程,表明Spill完毕。

要看更详细信息还是看这篇文章"http://www.tuicool.com/articles/7FNN32"。

我对Map端spill的理解的更多相关文章

  1. Hadoop on Mac with IntelliJ IDEA - 10 陆喜恒. Hadoop实战(第2版)6.4.1(Shuffle和排序)Map端 内容整理

    下午对着源码看陆喜恒. Hadoop实战(第2版)6.4.1  (Shuffle和排序)Map端,发现与Hadoop 1.2.1的源码有些出入.下面作个简单的记录,方便起见,引用自书本的语句都用斜体表 ...

  2. Hadoop2.4.1 MapReduce通过Map端shuffle(Combiner)完成数据去重

    package com.bank.service; import java.io.IOException; import org.apache.hadoop.conf.Configuration;im ...

  3. 项目中Map端内存占用的分析

      最近在项目中开展重构活动,对Map端内存尽量要省一些,当前的系统中Map端内存最高占用大概3G左右(设置成2G时会导致Java Heap OOM).虽然个人觉得占用不算多,但是显然这样的结果想要试 ...

  4. hadoop的压缩解压缩,reduce端join,map端join

    hadoop的压缩解压缩 hadoop对于常见的几种压缩算法对于我们的mapreduce都是内置支持,不需要我们关心.经过map之后,数据会产生输出经过shuffle,这个时候的shuffle过程特别 ...

  5. MapReduce在Map端的Combiner和在Reduce端的Partitioner

    1.Map端的Combiner. 通过单词计数WordCountApp.java的例子,如何在Map端设置Combiner... 只附录部分代码: /** * 以文本 * hello you * he ...

  6. Hive Map 端OOM 异常

    怪异现象:数据量不大,且不是Reduce端OOM,是Map端OOM Map Task运行的时候数据流中包含了非法字符例如:EOF.NOP等东西,导致BufferedReader读取和StreamDec ...

  7. Hadoop基础-Map端链式编程之MapReduce统计TopN示例

    Hadoop基础-Map端链式编程之MapReduce统计TopN示例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.项目需求 对“temp.txt”中的数据进行分析,统计出各 ...

  8. hadoop map端的超时参数

    目前集群上某台机器卡住导致出现大量的Map端任务FAIL,当定位到具体的机器上时,无法ssh或进去后terminal中无响应,退出的相关信息如下: [hadoop@xxx ~]$ Received d ...

  9. 项目中Map端数据处理不均匀性分析

    Map任务的不均匀性 最近发现Map端数据越来越不均匀,而处理输入的数据,写到本地磁盘的数据量都差不多,我们随便拿出来两个attempt任务(当前map数量为64个),33和45,33的counter ...

随机推荐

  1. Ubuntu系统中初次下载Android源码的一点经验

    这阵子突然心血来潮,想看看android的源代码,所以这一两天晚上都在折腾下载这个东西. (其实在GitHub上可以在线看的,不过不太喜欢在线看,URL附上 https://github.com/an ...

  2. 使用SmsManager服务群发短信

    SmsManager是Android提供的一个非常常见的服务,SmsManager提供了一系列sendXxxMessage()方法用于发送短信,不过短信通常都是普通文本,调用sendTextMessa ...

  3. Android打开系统设置

    今天在做项目过程中,遇到一个问题:用户体验某个功能时需要查看用户是否已经打开了GPS定位服务,若没有则要求进入定位服务设置界面. 下面就直接贴出代码 以下代码是放在了Button的监听事件里,只贴出重 ...

  4. redis 学习笔记——redis集群

    redis-cluster 简介 redis-cluster是一个分布式.容错的redis实现,redis-cluster通过将各个单独的redis实例通过特定的协议连接到一起实现了分布式.集群化的目 ...

  5. Java 编译打包命令

    背景 编译 打包 解压 运行 参考 背景 我们有的时候总是要使用将自己写的工程编译成 class 文件,同时打包成 jar,虽然有各种工具可以帮助我们,但是毕竟掌握使用 java 本来的命令去做这些更 ...

  6. MySQL分表

    一.概念 1.为什么要分表和分区?日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表.这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询 ...

  7. idea导入web项目的部署

    前几天 参考 http://zyjustin9.iteye.com/blog/2172712 这篇文章的部署,一直没有问题,今天又部署了一个项目,按照这个步骤,死活却不能部署成功.最后发现,原来是在部 ...

  8. WebService一、数据交互

    调用webservice总结:  1.加入第三方的jar包 Ksoap2-android-XXX    2.访问响应的webservice的网站,查看响应的信息,得到nameSpace,methodN ...

  9. 如何得到AdoConnection.execute(sqlstr)执行的返回结果

    如何得到AdoConnection.execute(sqlstr)执行的返回结果? 1: TAdoConnection.execute有procedure.function的两种定义,function ...

  10. soa服务治理-dubbo

    dubbo官网:http://dubbo.io/Home-zh.htm 学习点: 1.  日志的配置