热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

io.sort.spill.percent调整

引言MapReduce作出保证:进入每个Reducer的数据行都是有序的(根据数据行的键值进行排序)。MapReduce将Mapper的输出进行排序并传递给Reducer作为输入的
引言 MapReduce作出保证:进入每个Reducer的数据行都是有序的(根据数据行的键值进行排序)。MapReduce将Mapper的输出进行排序并传递给Reducer作为输入的过程称为Shuffle。在很多场景下,Shuffle是整个MapReduce过程的核心,也是“奇迹”发生的地方,如下图所示: 理解Shuffle的执行过程对我们优化MapReduce任务带来帮助。这里以Hadoop 0.20.2代码为基础进行介绍,同时也会涉及到如何扩展MapReduce组件,从而影响Shuffer的行为。 Map Task Map Task产生输出的时候,并不是直接将数据写到本地磁盘,这个过程涉及到两个部分:写缓冲区、预排序。 (1)写缓冲区 每一个Map Task都拥有一个“环形缓冲区”作为Mapper输出的写缓冲区。写缓冲区大小默认为100MB(通过属性io.sort.mb调整),当写缓冲区的数据量达到一定的容量限额时(默认为80%,通过属性io.sort.spill.percent调整),后台线程开始将写缓冲区的数据溢写到本地磁盘。在数据溢写的过程中,只要写缓冲区没有被写满,Mappper依然可以将数据写出到缓冲区中;否则Mapper的执行过程将被阻塞,直到溢写结束。 溢写以循环的方式进行(即写缓冲区的数据量大致限额时就会执行溢写),可以通过属性mapred.local.dir指定写出的目录。 (2)预排序 溢写线程将数据最终写出到本地磁盘之前,首先根据Reducer的数目对这部分数据进行分区(即每一个分区中的数据会被传送至同一个Reducer进行处理,分区数目与Reducer数据保持一致),然后对每一个分区中的数据根据键值进行排序(预排序),如果MapReducer开启Combiner,则对该分区中排序后的数据执行Combine过程。Combine过程的执行“紧凑”了Mapper的输出结果,因此写入本地磁盘的数据量和传送给Reducer的数据量都会被减少,通常情况下能很大程序的提高MapReducer任务的效率。 每一次写缓冲达到溢写临界值时,都会形成一个新的溢写文件,因此当Map Task输出最后一个数据时,本地磁盘上会有多个溢写文件存在。在整个Map Task完成之前,这些溢写文件会被合并为一个分区且排序后的文件。合并可能分为多次,属性io.sort.factor控制一次最多合并多少个文件。 如果溢写文件个数超过3(通过属性min.num.spills.for.combine设置),会对合并且分区排序后的结果执行Combine过程(如果MapReduce有设置Combiner),而且combine过程在不影响最终结果的前提下可能会被执行多次;否则不会执行Combine过程(相对而言,Combine开销过大)。 注意:Map Task执行过程中,Combine可能出现在两个地方:写缓冲区溢写过程中、溢写文件合并过程中。 通过对Map Task的输出结果进行压缩是一个好主意,可以加快写入磁盘的速度、节省磁盘空间以及减少需要传递给Reducer的数据量。默认情况下,压缩是不被开启的,可以通过属性mapred.compress.map.output、mapred.map.output.compression.codec进行相应设置。 以上两步主要对应着上图中"partition, sort, and spill to disk",但代码与实际执行过程略有不同。在Hadoop 0.20.2版本中,写缓冲区由org.apache.hadoop.mapred.MapTask.MapOutputBuffer实现,写缓冲区代码如下: 1 2 3 4 5 6 @Override public void write(K key, V value) throws IOException,     InterruptedException {   collector.collect(key, value,   partitioner.getPartition(key, value, partitions)); } 可以看出,在将Mapper的一条输出结果(由key、value表示)写出到写缓冲区之前,已经提前计算好相应的分区信息,即分区的过程在数据写入写缓冲区之前就已经完成,溢写过程实际是写缓冲区数据排序的过程(先按分区排序,如果分区相同时,再按键值排序)。 这里涉及到MapReduce的两个组件:Comparator、Partitioner。 (1)Comparator Comparator不会影响对分区排序的过程,它影响的是对键值的排序过程,代码如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int compare(int i, int j) { final int ii = kvoffsets[i % kvoffsets.length]; final int ij = kvoffsets[j % kvoffsets.length]; // sort by partition if (kvindices[ii + PARTITION] != kvindices[ij + PARTITION]) { return kvindices[ii + PARTITION] - kvindices[ij + PARTITION]; } // sort by key return comparator.compare(kvbuffer, kvindices[ii + KEYSTART], kvindices[ii + VALSTART] - kvindices[ii + KEYSTART], kvbuffer, kvindices[ij + KEYSTART], kvindices[ij + VALSTART] - kvindices[ij + KEYSTART]); } comparator的实例化是通过job.getOutputKeyComparator()完成的,代码如下: public RawComparator getOutputKeyComparator() { Class theClass = getClass( "mapred.output.key.comparator.class", null, RawComparator.class); if (theClass != null) { return ReflectionUtils.newInstance(theClass, this); } return WritableComparator.get(getMapOutputKeyClass().asSubclass( WritableComparable.class)); } 由上述代码可以看出,comparator实例有两种提供方式,亦即我们可以自定义扩展组件Comparator的方式: a. 继承接口RawComparator,并通过属性mapred.output.key.comparator.class进行配置; b. 为Map Output Key(Map Output Key必须实现WritableComparable接口)设置相应的WritableComparator,并通过WritableComparator的静态方法define进行注册。 RawComparator接口代码如下: public interfacewww.76seo.com RawComparator extends Comparator { public int compare(byte[] b1, int s1, bbs.76seo.comint l1, byte[] b2, int s2, int l2); } RawComparator有一个实现类WritableComparator,核心代码如下: public class WritableComparator implements RawComparator { private static HashMap comparators = new HashMap(); public static synchronized WritableComparator get( Class c) { WritableComparator comparator = comparators.get(c); if (comparator == null) { comparator = new WritableComparator(c, true); } return comparator; } public static synchronized void define(Class c, WritableComparator comparator) { comparators.put(c, comparator); } public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) { try { // parse key1 buffer.reset(b1, s1, l1); key1.readFields(buffer); // parse key2 buffer.reset(b2, s2, l2); key2.readFields(buffer); } catch (IOException e) { throw new RuntimeException(e); } // compare them return compare(key1, key2); } public int compare(WritableComparable a, WritableComparable b) { return a.compareTo(b); } public int compare(Object a, Object b) { return compare((WritableComparable) a, (WritableComparable) b); } } 分区内排序的过程就是通过RawComparator compare方法完成的,而且WritableComparator为compare方法提供了默认实现,即首先将字节数据反序列化为相应的WritableComparable实例,然后再进行比较(a.compareTo(b)),由于存在反序列化的过程,带来一定的性能开销。 (2)Partitioner 组件Partitioner一方面影响MapReduce任务的正确性(某些场景下需要保证具有相同规则的MapOutKey进入同一个Reducer进行处理),另一方面解决数据传输过程(Mapper输出传送至Reducer)中“数据倾斜”的问题(某些场景下需要均衡各个Reducer处理的数据量)。 组件Partitioner通过抽象类org.apache.hadoop.mapreduce.Partitioner提供,代码如下: public abstract class Partitioner { public abstract int getPartition(KEY key, VALUE value, int numPartitions); } 并且需要通过属性mapreduce.partitioner.class进行配置。 一般情况下,具体实现时通过key(可能会涉及到value)计算Hash值,并对numPartitions(分区数目、Reducer数目)取余即可。 Reduce Task 每一个Reduce Task仅仅运行属于它的那个分区的数据,而这些数据位于集群中运行Map Tasks的节点的本地磁盘上(Map的输出结果位于运行该Map Task的节点的本地磁盘上,结果包含着多个分区的数据)。这些Map Tasks完成于不同的时间,因此Reduce Task在某一个Map Task运行完成后便立即开始拷贝它的输出结果(通过JobTracker与TaskTracker的交互获取通知),Reduce Task的这个阶段被称为“Copy Phase”。Reduce Task拥有少量的线程用于并行地获取Map Tasks的输出结果,默认线程数为5,可以通过属性mapred.reduce.parallel.copies进行设置。 如果Map Task的输出结果足够小,它会被拷贝至Reduce Task的缓冲区中;否则拷贝至磁盘。当缓冲区中的数据达到一定量(由属性mapred.job.shuffle.merge.percent、mapred.inmem.merge.threshold),这些数据将被合并且溢写到磁盘。如果Combine过程被指定,它将在合并过程被执行,用来减少需要写出到磁盘的数据量。 随着拷贝文件中磁盘上的不断积累,一个后台线程会将它们合并为更大地、有序的文件,用来节省后期的合并时间。如果Map Tasks的输出结合使用了压缩机制,则在合并的过程中需要对数据进行解压处理。 当Reduce Task的所有Map Tasks输出结果均完成拷贝,Reduce Task进入“Sort Phase”(更为合适地应该被称为“Merge Phase”,排序在Map阶段已经被执行),该阶段在保持原有顺序的情况下进行合并。这种合并是以循环方式进行的,循环次数与合并因子(io.sort.factor)有关。 Sort Phase通常不会合并为一个有序的文件,也就是“Merge Phase”的最后一次合并将被省略,省去一次磁盘操作,直接将数据“合并输入”至Reduce相应方法(这次合并的数据可以结合内存、磁盘两部分进行操作),即“Reduce Phase”。 在Reduce Phase的过程中,它处理的是所有Map Tasks输出结果中某一个分区中的所有数据,这些数据整体表现为一个根据键有序的输入,对于每一个键都会相应地调用一次Reduce Function(同一个键对应的值可能有多个,这些值将作为Reduce Function的参数),如下: protected void reduce(KEYIN key, Iterable values, Context context)     throws IOException, InterruptedException { ...... } 通常Reduce Function的结果被直接写出至HDFS。 这里需要重点理解一下“同一个键”,上面的描述实际隐藏了一个被称为“Group”的阶段,这个阶段决定着哪些键值对属于同一个键。如果没有特殊设置,只有在Map Task输出时那些键完全一样的数据属于同一个键,但这是可以被改变的,如下所示: (a,b,c : 1) (a,b,c : 2) (a,c,e : 3) (b,c,d : 4) ...... 如果没有特殊设置,(a,b,c : 1)、(a,b,c : 2)会一并Reduce Function,而(a,c,e : 3)、(b,c,d : 4)会分别进入Reduce Function。 如果我们的计算需求要求我们统计键的第一个字母相同的所有值的和,这时就要求我们将(a,b,c : 1)、(a,b,c : 2)、(a,c,e : 3)合并进入Reduce Function,而且它们的键值为a,b,c,即第一个键值对的键值。 Group阶段决定哪些键值对属于同一个键的计算过程实际也是由一个RawComparator完成的,由属性mapred.output.value.groupfn.class决定,其实就是在键值对不断输入的过程中检查下一个键是否与当前键一致,从而决定哪些键值对属于同一个键。 一般情况下,Combiner与Reducer实际上是同一个类,亦即Combiner也存在Group阶段,但是要注意,Combiner(可能发生于三个地方)与Reducer在Group阶段使用的RawComparator是不一样的,Combiner使用的RawComparator由mapred.output.key.comparator.class指定,而Reducer使用的RawComparator由mapred.output.value.groupfn.class指定。 结语 通常MapReduce被人们粗略地认为只有两个阶段,即Map阶段、Reduce阶段,通过上面的介绍我们可以了解到在Map与Reduce之间存在着很复杂的Shuffle操作,涉及到数据的partition、sort、[combine]、spill、[comress]、[merge]、copy、[combine]、merge、group,而这些操作不但决定着程序逻辑的正确性,也决定着MapReduce的运行效率。

io.sort.spill.percent调整


推荐阅读
  • 深入解析Java虚拟机的内存分区与管理机制
    Java虚拟机的内存分区与管理机制复杂且精细。其中,某些内存区域在虚拟机启动时即创建并持续存在,而另一些则随用户线程的生命周期动态创建和销毁。例如,每个线程都拥有一个独立的程序计数器,确保线程切换后能够准确恢复到之前的执行位置。这种设计不仅提高了多线程环境下的执行效率,还增强了系统的稳定性和可靠性。 ... [详细]
  • Unity3D 中 AsyncOperation 实现异步场景加载及进度显示优化技巧
    在Unity3D中,通过使用`AsyncOperation`可以实现高效的异步场景加载,并结合进度条显示来提升用户体验。本文详细介绍了如何利用`AsyncOperation`进行异步加载,并提供了优化技巧,包括进度条的动态更新和加载过程中的性能优化方法。此外,还探讨了如何处理加载过程中可能出现的异常情况,确保加载过程的稳定性和可靠性。 ... [详细]
  • Python 伦理黑客技术:深入探讨后门攻击(第三部分)
    在《Python 伦理黑客技术:深入探讨后门攻击(第三部分)》中,作者详细分析了后门攻击中的Socket问题。由于TCP协议基于流,难以确定消息批次的结束点,这给后门攻击的实现带来了挑战。为了解决这一问题,文章提出了一系列有效的技术方案,包括使用特定的分隔符和长度前缀,以确保数据包的准确传输和解析。这些方法不仅提高了攻击的隐蔽性和可靠性,还为安全研究人员提供了宝贵的参考。 ... [详细]
  • POJ 2482 星空中的星星:利用线段树与扫描线算法解决
    在《POJ 2482 星空中的星星》问题中,通过运用线段树和扫描线算法,可以高效地解决星星在窗口内的计数问题。该方法不仅能够快速处理大规模数据,还能确保时间复杂度的最优性,适用于各种复杂的星空模拟场景。 ... [详细]
  • 在 Mac 上查看隐藏文件和文件夹的详细指南。通过终端命令,您可以轻松地显示或隐藏这些文件。具体步骤如下:输入 `defaults write com.apple.finder AppleShowAllFiles -bool true` 以显示所有隐藏文件,或使用 `defaults write com.apple.finder AppleShowAllFiles -bool false` 以重新隐藏它们。此方法适用于各种版本的 macOS,帮助用户更好地管理和访问系统文件。 ... [详细]
  • 本文详细解析了逻辑运算符“与”(&&)和“或”(||)在编程中的应用。通过具体示例,如 `[dehua@teacher~]$[$(id -u) -eq 0] && echo "You are root" || echo "You must be root"`,展示了如何利用这些运算符进行条件判断和命令执行。此外,文章还探讨了这些运算符在不同编程语言中的实现和最佳实践,帮助读者更好地理解和运用逻辑运算符。 ... [详细]
  • 在 Android 开发中,`android:exported` 属性用于控制组件(如 Activity、Service、BroadcastReceiver 和 ContentProvider)是否可以被其他应用组件访问或与其交互。若将此属性设为 `true`,则允许外部应用调用或与之交互;反之,若设为 `false`,则仅限于同一应用内的组件进行访问。这一属性对于确保应用的安全性和隐私保护至关重要。 ... [详细]
  • 在Conda环境中高效配置并安装PyTorch和TensorFlow GPU版的方法如下:首先,创建一个新的Conda环境以避免与基础环境发生冲突,例如使用 `conda create -n pytorch_gpu python=3.7` 命令。接着,激活该环境,确保所有依赖项都正确安装。此外,建议在安装过程中指定CUDA版本,以确保与GPU兼容性。通过这些步骤,可以确保PyTorch和TensorFlow GPU版的顺利安装和运行。 ... [详细]
  • 在Eclipse中提升开发效率,推荐使用Google V8插件以增强Node.js的调试体验。安装方法有两种:一是通过Eclipse Marketplace搜索并安装;二是通过“Help”菜单中的“Install New Software”,在名称栏输入“googleV8”。此插件能够显著改善调试过程中的性能和响应速度,提高开发者的生产力。 ... [详细]
  • 题目 E. DeadLee:思维导图与拓扑结构的深度解析问题描述:给定 n 种食物,每种食物的数量由 wi 表示。同时,有 m 位朋友,每位朋友喜欢两种特定的食物 x 和 y。目标是通过合理分配食物,使尽可能多的朋友感到满意。本文将通过思维导图和拓扑排序的方法,对这一问题进行深入分析和求解。 ... [详细]
  • 深入解析Linux内核中的进程上下文切换机制
    在现代操作系统中,进程作为核心概念之一,负责管理和分配系统资源,如CPU和内存。深入了解Linux内核中的进程上下文切换机制,需要首先明确进程与程序的区别。进程是一个动态的执行流,而程序则是静态的数据和指令集合。进程上下文切换涉及保存当前进程的状态信息,并加载下一个进程的状态,以实现多任务处理。这一过程不仅影响系统的性能,还关系到资源的有效利用。通过分析Linux内核中的具体实现,可以更好地理解其背后的原理和技术细节。 ... [详细]
  • 如何在PDF文档中添加新的文本内容?
    在处理PDF文件时,有时需要向其中添加新的文本内容。这是否可以直接实现呢?有哪些简便且免费的方法可供选择?使用极速PDF阅读器打开文档后,可以通过点击左上角的“注释”按钮切换到注释模式,并选择相应的工具进行编辑。此外,还可以利用其他功能丰富的PDF编辑软件,如Adobe Acrobat DC或Foxit PhantomPDF,它们提供了更多高级的编辑选项,能够满足更复杂的需求。 ... [详细]
  • 题目要求解决一个有趣的编程挑战,即计算由四个自然数 \( p, q, r, s \) 组成的分数序列的和。具体来说,需要编写一个 C# 程序来处理这些自然数,并通过特定的数学运算得出最终结果。该任务不仅考验编程技能,还涉及对数学公式的理解和应用。 ... [详细]
  • 在 Axublog 1.1.0 版本的 `c_login.php` 文件中发现了一个严重的 SQL 注入漏洞。该漏洞允许攻击者通过操纵登录请求中的参数,注入恶意 SQL 代码,从而可能获取敏感信息或对数据库进行未授权操作。建议用户尽快更新到最新版本并采取相应的安全措施以防止潜在的风险。 ... [详细]
  • Nginx 反向代理配置与应用指南
    本文详细介绍了 Nginx 反向代理的配置与应用方法。首先,用户可以从官方下载页面(http://nginx.org/en/download.html)获取最新稳定版 Nginx,推荐使用 1.14.2 版本。下载并解压后,通过双击 `nginx.exe` 文件启动 Nginx 服务。文章进一步探讨了反向代理的基本原理及其在实际应用场景中的配置技巧,包括负载均衡、缓存管理和安全设置等,为用户提供了一套全面的实践指南。 ... [详细]
author-avatar
菜鸟一号
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有