Hadoop-MapReduce-优化指南-全-

Hadoop MapReduce 优化指南(全)

原文:Optimizing Hadoop for MapReduce

协议:CC BY-NC-SA 4.0

零、前言

MapReduce 是一种重要的并行处理模型,适用于大规模的数据密集型应用,如数据挖掘和 Web 索引。 Hadoop 是 MapReduce 的开源实现,广泛应用于支持需要低响应时间的集群计算作业。

大多数 MapReduce 程序都是为数据分析编写的,通常需要很长时间才能完成。 许多公司正在采用 Hadoop 进行高级数据分析,而不是需要时间完成保证的大型数据集。 效率,特别是 MapReduce 的 I/O 成本,仍然需要解决才能成功。 经验表明,错误配置的 Hadoop 集群会显著降低 MapReduce 作业的性能。

在这本书中,我们将讨论 MapReduce 优化问题,如何找出不足之处,以及如何使用 Hadoop 集群的所有资源来优化处理输入数据。 本书首先介绍了 MapReduce,以了解其内部工作原理,并讨论了影响其性能的因素。 然后,继续调查 Hadoop 指标和性能工具,并确定资源弱点,如 CPU 争用、内存使用、海量 I/O 存储和网络流量。

本书将以实际经验为基础,循序渐进地教您如何消除作业瓶颈,并在生产环境中全面优化 MapReduce 作业。 此外,您还将学习计算用于处理数据的适当数量的集群节点,根据硬件资源定义适当数量的映射器和减少器任务,以及如何使用压缩技术和组合器优化映射器和减少器任务的性能。

最后,您将学习调优 Hadoop 集群的最佳实践和建议,并了解 MapReduce 模板类是什么样子。

这本书涵盖了哪些内容

第 1 章了解 Hadoop MapReduce,解释 MapReduce 的内部工作方式以及影响 MapReduce 性能的因素。

第 2 章Hadoop 参数概述介绍了 Hadoop 配置文件和 MapReduce 性能相关参数。 它还解释了 Hadoop 指标和几个可用于监视 Hadoop MapReduce 活动的性能监视工具。

第 3 章检测系统瓶颈探索了 Hadoop MapReduce 性能调优周期,并解释了如何创建性能基准。 然后,您将学习基于 Hadoop 计数器识别资源瓶颈和弱点。

第 4 章识别资源弱点解释了如何检查 Hadoop 群集的运行状况以及识别 CPU 和内存使用情况、海量 I/O 存储和网络流量。 此外,您还将学习在配置 Hadoop 集群时如何正确扩展。

第 5 章增强映射和归约任务向您展示了如何增强映射和归约任务执行。 您将了解块大小的影响、如何减少溢出记录、确定映射和降低吞吐量,以及调优 MapReduce 配置参数。

第 6 章优化 MapReduce 任务解释了何时需要使用组合器和压缩技术来优化映射和归约任务,并介绍了几种优化应用代码的技术。

第 7 章最佳实践和建议介绍了各种硬件和软件检查表、建议和调优属性,以便以最佳方式使用 Hadoop 集群。

这本书你需要什么

Apache Hadoop 框架(Linux),可以访问在 http://hadoop.apache.org/操作系统上运行 Hadoop 的计算机。

这本书是给谁看的

如果您是一位经验丰富的 MapReduce 用户或开发人员,这本书将非常适合您。 如果你是 MapReduce 的初学者或用户,想要尝试新事物并学习优化应用的技术,这本书也是一本非常有用的指南。 创建 MapReduce 应用的知识不是必需的,但将帮助您更快地掌握某些概念,并更熟悉 MapReduce 类模板代码片段。

公约

在这本书中,你会发现许多区分不同信息的文本样式。 以下是这些风格的一些示例,并解释了它们的含义。

文本、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 句柄中的代码字如下所示:“我们可以通过使用include指令包括其他上下文。”

代码块设置如下:

[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)

当我们希望您注意代码块的特定部分时,相关行或项将以粗体显示:

[default]
exten => s,1,Dial(Zap/1|30)
exten => s,2,Voicemail(u100)
exten => s,102,Voicemail(b100)
exten => i,1,Voicemail(s0)

任何命令行输入或输出都如下所示:

# cp /usr/src/asterisk-addons/configs/cdr_mysql.conf.sample
 /etc/asterisk/cdr_mysql.conf

新术语重要单词以粗体显示。 您在屏幕上看到的单词(例如,菜单或对话框中的单词)会出现在文本中,如下所示:“单击下一步按钮将转到下一个屏幕。”

备注

警告或重要说明会出现在这样的框中。

提示

提示和技巧如下所示。

读者反馈

欢迎读者的反馈。 让我们知道你对这本书的看法-你喜欢什么或不喜欢什么。 读者反馈对于我们开发真正能让您获得最大收益的图书非常重要。

要向我们发送一般反馈,只需发送电子邮件至<[feedback@packtpub.com](mailto:feedback@packtpub.com)>,并通过消息主题提及书名。

如果有一个您擅长的主题,并且您有兴趣撰写或投稿一本书,请参阅我们位于www.Packtpub.com/Authors上的作者指南。

客户支持

现在您已经成为 Packt 图书的拥有者,我们有很多东西可以帮助您从购买中获得最大价值。

勘误表

虽然我们已经竭尽全力确保内容的准确性,但错误还是会发生。 如果您在我们的一本书中发现错误--可能是文本或代码中的错误--如果您能向我们报告,我们将不胜感激。 通过这样做,您可以将其他读者从挫折中解救出来,并帮助我们改进本书的后续版本。 如果您发现任何勘误表,请访问http://www.packtpub.com/submit-errata,选择您的图书,单击勘误表提交链接,然后输入勘误表的详细信息。 一旦您的勘误表被核实,您提交的勘误表将被接受,勘误表将被上传到我们的网站上,或添加到该标题勘误表部分下的任何现有勘误表列表中。 通过从http://www.packtpub.com/support中选择您的书目,可以查看任何现有勘误表。

盗版

在互联网上盗版版权材料是所有媒体持续存在的问题。 在 Packt,我们非常重视版权和许可证的保护。 如果您在互联网上发现任何形式的非法复制我们的作品,请立即提供我们的位置、地址或网站名称,以便我们采取补救措施。

请拨打<[copyright@packtpub.com](mailto:copyright@packtpub.com)>与我们联系,并提供疑似盗版材料的链接。

我们感谢您在保护我们的作者方面的帮助,以及我们为您提供有价值内容的能力。

问题

如果您对本书的任何方面有任何问题,可以拨打<[questions@packtpub.com](mailto:questions@packtpub.com)>与我们联系,我们将尽最大努力解决。

一、了解 Hadoop MapReduce

MapReduce,一种流行的数据密集型分布式计算模型,正在成为 Web 索引、数据挖掘、和科学模拟等大规模数据并行应用的重要编程模型。

Hadoop 是 Google 的 MapReduce 编程模型最流行的开源 Java 实现。 它已经被许多公司用于大规模数据分析任务,并且经常用于响应时间短的工作。

在深入研究 MapReduce 编程和 Hadoop 性能调优之前,我们将回顾 MapReduce 模型的基础知识,并了解影响 Hadoop 性能的因素。

在本章中,我们将介绍以下内容:

  • MapReduce 模型
  • Hadoop MapReduce 概述
  • MapReduce 的内部工作原理
  • 影响 MapReduce 性能的因素

MapReduce 模型

MapReduce 是一个编程模型,设计用于通过大型商用硬件集群处理非结构化数据并生成大型数据集。 它能够在集群中的数千个计算节点上处理数 TB 的数据、处理故障、复制任务和聚合结果。

MapReduce 模型很容易理解。 它是由谷歌研究院(http://research.google.com/archive/mapreduce.html)的工程师在 21 世纪初设计的。 它由两个函数组成,一个是map函数,另一个是可以在多台机器上并行执行的reduce函数。

要使用 MapReduce,程序员需要编写一个用户定义的map函数和一个用户定义的reduce函数来表达他们想要的计算。 map函数读取键/值对,应用特定于用户的代码,并生成称为中间结果的结果。 然后,这些中间结果由输出最终结果的reduce特定于用户的代码聚合。

MapReduce 应用的输入按照将产生键/值对的输入规范组织在记录中,每个键/值对都是<k1, v1>对。

因此,MapReduce 过程由两个主要阶段组成:

  • map():用户定义的map函数逐个应用于所有输入记录,并为每个记录输出一个包含零个或多个中间键/值对的列表,即<k2, v2>条记录。 然后收集并重新组织所有<k2, v2>条记录,以便将具有相同关键字(k2)的记录放在一起成为<k2, list(v2)>条记录。

  • reduce(): The user-defined reduce function is called once for each distinct key in the map output, <k2, list(v2)> records, and for each record the reduce function outputs zero or more <k2, v3> pairs. All <k2, v3> pairs together coalesce into the final result.

    提示

    mapreduce函数的签名如下:

    • map(<k1, v1>)``list(<k2, v2>)
    • reduce(<k2, list(v2)>)``<k2, v3>

MapReduce 编程模型设计为独立于存储系统。 MapReduce 通过读取器从底层存储系统读取键/值对。 读取器从存储系统检索每条记录,并将记录包装成键/值对以供进一步处理。 用户可以通过实施相应的读卡器来添加对新存储系统的支持。 这种与存储无关的设计被认为对异构系统有利,因为它使 MapReduce 能够分析存储在不同存储系统中的数据。

为了理解 MapReduce 编程模型,让我们假设您想要计算给定输入文件中每个单词的出现次数。 转换为 MapReduce 作业后,字数统计作业由以下步骤定义:

  1. 输入数据被分成多个记录。
  2. 映射函数处理这些记录,并为每个单词生成键/值对。
  3. map函数输出的所有键/值对被合并在一起,按键分组并排序。
  4. 中间结果被传输到reduce函数,该函数将产生最终输出。

此 MapReduce 应用的总体步骤如下图所示:

The MapReduce model

在聚合键/值对时,可以观察到大量的 I/O 和网络流量 I/O。 为了减少 MAP 和 Reduce 步骤之间所需的网络通信量,程序员可以选择通过提供组合器函数来执行映射端预聚合。 组合器函数类似于reduce函数,不同之处在于它们不会被传递给给定键的所有值;相反,组合器函数发出一个输出值,该输出值汇总传递给它的输入值。

Hadoop MapReduce 概述

Hadoop 是 Google 提出的 MapReduce 编程模型最流行的开源 Java 实现。 MapReduce 还有许多其他实现(如 Sphere、Starfish、Riak 等),它们实现了 Google 文档中描述的所有功能,或者只实现了这些功能的一部分。

Hadoop 由分布式数据存储引擎和 MapReduce 执行引擎组成。 它已经成功地用于处理使用大量节点的大量数据集上的高度可分布性问题。 这些节点共同形成一个Hadoop 集群,该集群由一个称为JobTracker的主节点和多个工作(或从)节点组成;每个工作节点称为TaskTracker。 在这个框架中,用户程序被称为作业,分为两个步骤:映射和还原。

与 MapReduce 编程模型中一样,在使用 Hadoop MapReduce 实现时,用户只需定义 map 和 Reduce 函数。 Hadoop MapReduce 系统自动并行执行这些功能,并确保容错。

提示

要了解更多关于 Hadoop MapReduce 实现的信息,您可以浏览 Hadoop 的官方网站http://hadoop.apache.org/

基本上,Hadoop MapReduce 框架利用分布式文件系统来读写其数据。 这个分布式文件系统称为Hadoop 分布式文件系统(HDFS),它是Google 文件系统(GFS)的开源版本。 因此,Hadoop MapReduce 作业的 I/O 性能强烈依赖于 HDFS。

HDFS 由称为NameNode的主节点和称为DataNodes的从节点组成。 在 HDFS 中,个数据被分成个固定大小的块(块),并分布在集群中的所有 DataNode 上。 每个数据块通常使用两个副本进行复制:一个放在同一机架内,另一个放在机架外。 NameNode 跟踪哪些 DataNode 保存哪些块的副本。

Hadoop MapReduce 内部

MapReduce 编程模型可用于通过一个或多个步骤处理许多大型数据问题。 此外,它可以有效地实现以支持使用大量机器处理大量数据的问题。 在大数据环境中,处理的数据量可能非常大,以至于无法将数据存储在一台机器上。

在典型的 Hadoop MapReduce 框架中,数据被分成块并分布在集群中的许多节点上,MapReduce 框架通过将计算传送到数据而不是将数据移动到处理数据的位置来利用数据的局部性。 MapReduce 应用的大多数输入数据块都位于本地节点上,因此可以非常快速地加载它们,并且可以在多个节点上并行读取多个块。 因此,MapReduce 可以获得非常高的聚合 I/O 带宽和数据处理速率。

要启动 MapReduce 作业,Hadoop 需要创建 MapReduce 应用的一个实例,并将该作业提交给 JobTracker。 然后,作业被划分为映射任务(也称为映射器)和 Reduce 任务(也称为 Reducer)。

当 Hadoop 启动 MapReduce 作业时,它会将输入数据集分割成均匀大小的数据块,并使用心跳协议来分配任务。 然后,每个数据块被调度到一个 TaskTracker 节点,并由映射任务处理。

每个任务都在 Worker 节点中的一个可用插槽上执行,该节点配置了固定数量的 MAP 插槽和另一个固定数量的 Reduce 插槽。 如果所有可用插槽都已被占用,则挂起的任务必须等待,直到一些插槽被释放。

TaskTracker 节点定期将其状态发送到 JobTracker。 当 TaskTracker 节点空闲时,JobTracker 节点会为其分配新任务。 JobTracker 节点在传播数据块时会考虑数据局部性。 它总是尝试将本地数据块分配给 TaskTracker 节点。 如果尝试失败,JobTracker 节点将改为向 TaskTracker 节点分配一个机架本地或随机数据块。

当所有映射函数完成执行时,运行时系统将对所有中间对进行分组,并启动一组 Reduce 任务以生成最终结果。 它将执行从改组阶段移到缩减阶段。 在最后的 Reduce 阶段,调用reduce函数来处理中间数据并写入最终输出。

用户经常使用不同粒度的术语来指定 Hadoop 映射和归约任务、子任务、阶段和子阶段。 映射任务由两个子任务组成:映射和合并,而 Reduce 任务只包含一个任务。 但是,混洗和排序首先发生,并由系统完成。 每个子任务又被划分为许多子阶段,如读-映射、溢出、合并、复制-映射和减少-写入。

影响 MapReduce 性能的因素

使用 MapReduce 处理输入数据的时间可能受许多因素的影响。 其中一个因素是您在实现mapreduce函数时使用的算法。 其他外部因素也可能影响 MapReduce 的性能。 根据我们的经验和观察,以下是可能影响 MapReduce 性能的主要因素:

  • 硬件(或资源),如 CPU 时钟、磁盘 I/O、网络带宽和内存大小。
  • 底层存储系统。
  • 输入数据、置乱数据和输出数据的数据大小,它们与作业的运行时间密切相关。
  • 作业算法(或程序),例如映射、约简、分区、合并和压缩。 有些算法可能很难在 MapReduce 中概念化,或者在 MapReduce 中表达起来效率不高。

在运行映射任务时,混洗子任务的中间输出存储在内存缓冲区中,以减少磁盘 I/O。但是,由于此输出的大小可能超过内存缓冲区的大小,并且可能会发生这种溢出,因此需要溢出子阶段将数据刷新到本地文件系统中。 此子阶段可能会影响 MapReduce 的性能,通常使用多线程来实现,以最大限度地提高磁盘 I/O 的利用率并缩短作业的运行时间。

MapReduce 编程模型使用户能够使用自己的mapreduce函数指定数据转换逻辑。 该模型没有指定如何对映射函数生成的中间对进行分组,以供 Reduce 函数处理。 因此,采用合并-排序算法作为默认分组算法。 然而,合并排序算法并不总是最有效的算法,特别是对于聚集和相等连接这样的分析任务,这些任务并不关心中间键的顺序。

备注

在 MapReduce 编程模型中,分组/分区是一项串行任务! 这意味着框架需要等待所有映射任务完成,然后才能运行任何 Reduce 任务。

要了解有关合并排序算法的更多信息,请参阅 URLhttp://en.wikipedia.org/wiki/Merge_sort

MapReduce 性能基于mapreduce的运行时。 这是因为在典型环境中,群集中的节点数或节点中的插槽数等参数是不可修改的。

其他可能影响 MapReduce 性能的因素包括:

  • I/O 模式:这是从存储系统检索数据的方式。 从底层存储系统读取数据有两种模式:

    • 直接 I/O:这用于通过硬件控制器直接从本地磁盘缓存读取到内存;因此,不需要进程间通信开销。

    • Streaming I/O: This allows you to read data from another running process (typically the storage system process) through certain inter-process communication schemes such as TCP/IP and JDBC.

      提示

      要提高性能,使用直接 I/O 可能比流式 I/O 更高效。

  • Input data parsing: This is the conversion process from raw data into the key/value pairs when data is retrieved from the storage system. The data parsing process aims to decode raw data from its native format and transform it into data objects that can be processed by a programming language such as Java.

    可以将输入数据解码为(Java 或其他)对象,以便在创建实例后可以更改内容,通常是在使用对对象实例(这些对象称为可变对象)或对内容在创建后无法更改的对象(称为不可变对象)的引用时。 在有一百万条记录的情况下,不可变解码过程比可变解码过程慢得多,因为它可能会产生大量的不可变对象。 因此,这可能会导致系统性能不佳。

  • 输入数据存储:当 MapReduce 检索要处理的数据时,底层存储系统必须确保高速访问和数据可用性(如 HDFS 和 HBase)。 如果您选择使用存储文件系统,而不是建议与 MapReduce 一起使用的存储文件系统,则对输入数据的访问可能会潜在地影响 MapReduce 的性能。

在使用 Hadoop 框架时,许多因素可能会影响整体系统性能和作业的运行时间。 这些因素可能是 Hadoop MapReduce 引擎的一部分,也可能在其外部。

Hadoop 配置参数通常指示可以并发运行的任务数量,并确定作业的运行时间,因为在设置 Hadoop 群集和作业开始执行之后,其他因素不可修改。 错误配置的 Hadoop 框架可能会未充分利用群集资源,从而影响 MapReduce 作业性能。 这是由于控制 Hadoop 框架行为的大量配置参数造成的。

Hadoop 作业通常由实现不同算法的多个子模块组成,这些子模块中的一些是串行连接的,而另一些是并行连接的。 Hadoop 框架的错误配置可能会影响所有内部任务如何协同完成任务。 所有这些参数的设置(将在 C章节 2Hadoop 参数概述中介绍)的影响取决于mapreduce函数的代码、集群资源,当然还有输入数据。

MapReduce 作业性能还可能受到 Hadoop 集群中节点数量以及所有节点可用于运行映射和归约任务的资源的影响。 每个节点容量决定了节点可以执行的映射器和减少器任务的数量。 因此,如果节点资源未得到充分利用或过度利用,将直接影响 MapReduce 任务的性能。

摘要

在本章中,我们学习了 MapReduce 编程模型,并回顾了它的内部工作原理。 然后,我们重点介绍了 Hadoop MapReduce,并了解了它的主要组件。 我们还讨论了可能影响 Hadoop MapReduce 性能的内部和外部因素。

在下一章中,我们将研究 Hadoop 的可调参数,并了解 Hadoop 指标和性能工具。

二、Hadoop 参数概述

运行 Hadoop 作业后,了解集群资源是否得到充分利用非常重要。 幸运的是,Hadoop 框架提供了几个参数,使您能够调优作业并指定它将如何在集群上运行。

性能调优涉及四个主要组件:CPU 利用率、内存占用、磁盘 I/O 和网络流量。 本章描述了与这些组件最相关的参数,并介绍了优化 Hadoop 执行和定义一些配置参数的技术。

拥有一个高效的监视工具非常重要,而且必不可少,它可以在问题正在发展或发生时发出警报,从而直观地指示 Hadoop 群集的运行情况。 本章重点介绍使用配置参数进行 Hadoop 性能调优,并介绍几种用于监视 Hadoop 服务的工具。

在本章中,我们将介绍以下主题:

  • Hadoop 可调参数的研究
  • 与 CPU 利用率、内存、磁盘 I/O 和网络相关的 Hadoop 配置参数
  • Hadoop 指标
  • Hadoop 监控工具

调查 Hadoop 参数

正如在第 1 章了解 MapReduce中所讨论的,有很多因素可能会影响 Hadoop MapReduce 的性能。 通常,依赖于工作负载的 Hadoop 性能优化工作必须集中在三个主要类别上:系统硬件、系统软件和 Hadoop 基础架构组件的配置和调优/优化。

需要指出的是,Hadoop 被归类为高度可扩展的解决方案,但不一定是高性能集群解决方案。 管理员可以使用各种配置选项配置和调优 Hadoop 群集。 性能配置参数主要关注 CPU 利用率、内存占用、磁盘 I/O 和网络流量。 除了 Hadoop 的主要性能参数外,机架间带宽等其他系统参数可能会影响集群的整体性能。

Hadoop 可以根据用户需求进行配置和定制;可以修改安装后自动生成的配置文件,以满足应用和集群的特定需求。

配置文件分为两类:只读默认配置和站点特定配置:

  • 第一类别包括core-default.xmlhdfs-default.xmlmapred-default.xml
  • 第二个类别包括core-site.xmlhdfs-site.xmlmapred-site.xml

Hadoop 配置文件中有大量的变量,您可以定义或覆盖它们来控制 Hadoop 配置。 这些变量可以在core-site.xmlhdfs-site.xmlmapred-site.xml中定义。 一些变量用于指定系统上的文件路径,而其他变量则以不同的粒度调整 Hadoop 框架的不同方面。 每个配置文件都有以 XML 格式表示的名称-值对,它们定义了 Hadoop 的不同方面的工作方式。

从系统的硬件角度来看,在性能、可伸缩性和成本方面平衡适当的硬件组件至关重要。 从软件的角度来看,操作系统、JVM(Java 虚拟机)、特定 Hadoop 版本以及运行 Hadoop 安装程序所需的其他软件组件的选择确实会对环境的性能和稳定性产生深远影响。 任何 Hadoop 项目的设计、设置、配置和调优阶段对于充分受益于分布式 Hadoop 硬件和软件解决方案堆栈至关重要。

mapred-site.xml 配置文件

可以认为mapred-site.xml文件是提高 Hadoop MapReduce 性能的主键。 此配置文件包含 CPU、内存、磁盘 I/O 和网络相关参数。

CPU 相关参数

以下两个参数与 CPU 利用率最相关。 通过覆盖这些变量,您可以指定 TaskTracker 节点将同时运行的最大 MAP/Reduce 任务数。 默认情况下,这两个参数的值都是2

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| mapred.tasktracker.map.tasks.maximum | 用于设置 TaskTracker 将同时运行的映射任务的最大数量[T0 | 2 |
| mapred.tasktracker.reduce.tasks.maximum | 用于设置 TaskTracker 将同时运行的最大 Reduce 任务数[T0 | 2 |

从理论上讲,增加这些默认值会增加 CPU 利用率,从而提高性能,但这个必须根据您的集群资源(如 CPU(有/没有超线程因子)和可用内存)灵活地执行。 否则,您可能会降低整个 MapReduce 作业和群集性能的级别。

用户提出的一个典型问题是,应该设置多少个映射器/减少器才能获得最佳性能? 要设置此值,应考虑 TaskTracker 对 CPU、内存和磁盘 I/O 等资源的利用率。此外,还应考虑正在运行的作业是否占用大量 CPU 资源。

为了说明这些参数设置,假设您有 10 个 Hadoop 集群节点,每个节点都有一个四核 CPU。 那么,守护进程的总数应该不超过10(节点)x 4(CPU 核心)=40。 考虑到应该为 DataNode 守护进程保留一个 CPU 核心,为 TaskTracker 保留另一个 CPU 核心,那么只剩下 38 个 CPU 核心用于 map/duce 任务。

您不需要将映射器或减少器计数设置为相同的值,因为它取决于每个任务的 CPU 利用率以及每个映射器和减少器如何完成作业以获得最终结果。 您可以将 50%的 CPU 资源分配给映射器,将 50%的 CPU 资源分配给减速器,将三分之二的 CPU 资源分配给映射器,将三分之一的 CPU 资源分配给减速器,或者使用任何其他允许您使集群以最佳方式运行的分配方案。

将计数值设置为-1意味着 Hadoop 将使用管理员为映射器和减少器任务分配的所有内存。 将此值设置为大于物理 CPU 核心计数将导致密集的 CPU 上下文切换(请参阅第 4 章识别资源弱点),这可能会显著降低群集的性能。

磁盘 I/O 相关参数

为了优化磁盘 I/O 操作,您可以决定使用默认关闭的数据压缩。 可以通过更改控制压缩的参数的默认值来启用压缩。 变量mapred.compress.map.output启用映射输出压缩,变量mapred.output.compress启用作业输出压缩,变量mapred.map.output.compression.codec用于定义压缩编解码器。

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| mapred.compress.map.output | 这是一个布尔值(TRUE 或 FALSE),默认情况下设置为false。 如果设置为true,映射任务的输出将在通过网络发送之前使用SequenceFile编解码器压缩进行压缩。 | false |
| mapred.output.compress | 这是一个布尔值;将其设置为true将启用作业输出压缩。 | false |
| mapred.map.output.compression.codec | 该值用于确定将用于压缩 MAP 输出的压缩编解码器(编码器/解码器)。 | org.apache.hadoop.io.compress.DefaultCodec |

为映射任务启用压缩输出将减少要写入存储的中间数据量。 因此,这将加快混洗和写入阶段的磁盘写入操作,并减少数据传输的总时间。 在压缩/解压缩过程中,使用压缩加速磁盘写入操作会带来额外的 CPU 开销。

实际经验表明,只有当输入数据较大且易于拆分(如文本文件)时,才应启用压缩。 否则,启用压缩可能会降低群集的性能。

为了实现磁盘 I/O 均衡,大幅提升 I/O 性能,您可以使用写入多个位置的功能在每个节点的所有磁盘上写入数据。 使用多个物理设备产生的性能比 RAID 0 条带化大约高 50%。

以下两个参数确定在 Hadoop 中存储数据的位置。 您可以使用mapred.local.dir变量指定存储中间映射输出数据的位置,使用dfs.data.dir变量指定 HDFS 数据的存储位置。

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| mapred.local.dir | 它用于指定需要存储映射的中间文件的本地目录。 您可以为此参数定义多个目录,这些目录应该位于不同的物理设备上。 如果提供了多个目录,数据将分布在这些目录中。 不存在的目录将被忽略。 | ${hadoop.tmp.dir}/mapred/local |
| dfs.data.dir (hdfs-site.xml) | 这用于指定 DataNode 应在本地文件系统上存储其块的目录。 如果您提供逗号分隔的目录列表,则数据块将存储在所有目录中。 如果此参数丢失,则整个 HDFS 数据集都会丢失。 | ${hadoop.tmp.dir}/dfs/data |

内存相关参数

内存资源是一种非常重要的资源,需要合理分配,以避免交换并允许 Hadoop 作业以最佳方式运行。 您可以使用与内存相关的参数来设置要为 MapReduce 作业保留的物理内存量。 下表显示了最常见的内存相关参数:

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| mapred.child.java.opts | 这控制每个 JVM 任务可用的内存量。 默认值将保留 200 MB 内存以运行 MapReduce 作业。 | -Xmx200m |
| Mapred.child.ulimit | 此参数用于控制将分配给 MapReduce 作业的虚拟内存限制。 |   |

未指定默认值Mapred.child.ulimit。 如果选择为此参数指定值,则该值应大于或至少等于mapred.child.java.opts–Xmx值。 否则,Java 虚拟机可能无法启动。 要正确设置此值,应将其设置为大于2mapred.Child.java.opts*的值。

合并和排序是可以使用与内存相关的参数进行优化的另一个方面。 您可以设置三个主要参数来优化 MapReduce 的合并和排序性能。

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| io.sort.mb | 它指定对数据流进行排序时要使用的缓冲区空间量(以 MB 为单位)。 配置错误时,此参数通常会导致小内存计算机上的作业内存不足。 | 100 |
| io.sort.factor | 这决定了在对文件进行排序时一次要合并的映射输出分区的个。 | 10 |
| mapred.job.reduce.input.buffer.percent | 这决定了在 Reduce 阶段保留映射输出的内存相对于最大堆大小的百分比。 Reduce 任务可以在混洗结束时开始,并且为任何剩余的映射输出分配的内存必须小于此阈值。 | 0.0 |

如果增加io.sort.mbio.sort.factor的值,它将为排序和合并操作分配更多内存。 这最大限度地减少了对磁盘的溢出,从而减少了映射器和减速器任务的 I/O 时间。 另一方面,增加这些值会增加每个映射任务所需的内存,并且当分配给每个任务的内存不够大时,可能会增加垃圾收集器活动。

我们的经验表明,如果有大量溢出到磁盘,并且排序/混洗阶段的 I/O 次数很高,则可能需要增加io.sort.factor的值。 此外,如果您的映射输出很大,且映射端 I/O 频繁,则应该尝试增加io.sort.mb的值。 为了避免“任务内存不足”错误,您应该将io.sort.mb设置为大于0.25mapred.Child.java.opts且小于0.5mapred.Child.java.opts的值。

您可以增加mapred.job.reduce.input.buffer.percent的值以在内存中获得更多缓冲区,这将在 Reduce 阶段减少本地磁盘 I/O 时间,但如前所述,如果分配了更多内存,则可能会增加垃圾收集器释放未使用内存的活动。 因此,当映射输出较大且本地磁盘 I/O 在 Reduce 排序阶段频繁时,应尝试分配更多内存。

网络相关参数

Hadoop 有一个名为机架感知的概念。 管理员可以定义群集中每个 DataNode 的机架。 让 Hadoop 机架感知至关重要,因为机架感知可防止数据丢失,而良好的机架感知配置可提高网络性能。 下表中定义的变量可帮助 Hadoop 安装机架识别:

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| mapred.reduce.parallel.copies | 它指定在混洗阶段用于获取映射输出的并行传输的个数。 | 5 |
| topology.script.file.name(core-site.xml) | 这指定要调用的脚本名,以便将 DNS 名称解析为网络拓扑名称(例如,将host.servers.company作为参数传递,并返回/rack1作为输出)。 |   |

增加mapred.reduce.parallel.copies可能会增加网络流量并加快复制映射输出的过程,但代价是 CPU 使用率更高。 我们建议仅当您的映射器任务产生非常大的输出时才增加此值。

Hadoop 机架感知是使用core-site.xml文件中的topology.script.file.name参数配置的。 此参数应指向确定机架-主机映射的用户定义脚本(Rack1:DataNode1、DataNode2…。 机架 2:数据节点 6、数据节点 7…)。 。 如果未配置topology.script.file.name,则会为放置在同一机架上的任何节点的 IP 地址传递/default-rack

备注

您可以在http://hadoop.apache.org/docs/r1.2.1/cluster_setup.html#Hadoop+Rack+Awareness了解有关 Hadoop 机架感知设置和配置的更多信息

hdfs-site.xml 配置文件

hdfs-site.xml配置文件包含许多与 HDFS 存储系统相关的参数,您可以覆盖这些参数,以便自定义 Hadoop 安装和/或调整它。 文件系统块大小的值是hdfs-site.xml中最常调优的 HDFS 参数。 它控制每个映射任务将处理的输入拆分的大小。 输入分割的大小可以通过三个主要变量指定:dfs.block.size(在hdfs-site.xml中)、mapred.min.split.sizemapred.max.split.size(都在mapred-site.xml中)。

默认情况下,dfs.block.size设置为 67108864字节(64 MB)。 增加此值将创建更大的输入拆分,从而减少将存储在每个 DataNode 上的块数。

映射任务的总数取决于输入数据大小和总输入分割大小。 虽然贴图输出大小与 HDFS 块大小成正比,但如果与溢出相关的属性没有相应调整,则较大的块大小可能会导致额外的映射端溢出。

通常,为了最大化吞吐量,您应该根据输入数据调整块大小。 对于非常大的输入文件,最好使用非常大的块(128MB 甚至 256MB),而对于较小的文件,最好使用较小的块大小。 请注意,通过更改dfs.block.size参数,在写入文件时,可以在同一文件系统上拥有不同块大小的文件(请参阅第 5 章增强**映射和归约任务,以了解块大小的影响)。

下表显示了可在hdfs-site.xml配置文件中设置的主要参数:

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| dfs.access.time.precision | 这是维护的访问时间的精度(以毫秒为单位)。 如果此值为 0,则不维护任何访问时间。 默认值为 1 小时。 要禁用 HDFS 的访问时间,请将其设置为0(零),这可能会提高繁忙群集的性能,这些群集由于 NameNode 上的日志写入速度较慢而经常出现瓶颈。 | 3600000 |
| dfs.balance.bandwidthPerSec | 这指定每个 DataNode 可用于在 Hadoop 群集的 DataNode 之间重新平衡块存储的最大带宽量。 该值以每秒字节数表示。 | 1048576 |
| dfs.block.size | 这是新文件的默认块大小。 此参数应根据您的群集和数据作业进行调整。 | 67108864 |
| dfs.data.dir | 这决定了数据节点应该在本地文件系统上存储其块的位置。 如果可以提供逗号分隔的目录列表,则数据将存储在所有命名目录中。 如果此数据丢失,则整个 HDFS 数据集也会丢失。 | ${hadoop.tmp.dir}/dfs/data |
| dfs.datanode.du.reserved | 这是在每个用于块存储的位置中必须保留的空闲空间量。 | 0.0 |
| dfs.datanode.handler.count | 这决定了处理块请求的服务器线程的数量。 如果您增加此值,则可能会增加 DataNode 吞吐量,特别是当它将数据块存储在多个单独的物理设备上时。 | 3 |
| dfs.max.objects | 此变量确定允许的对象(文件、目录和块)的最大数量。 默认情况下,此变量设置为零,表示对对象数量没有限制。 | 0 |
| dfs.name.dir | 此变量支持用于数据块存储的个目录路径或逗号分隔的目录列表。 Hadoop 将使用轮询算法处理该列表以存储新数据块。 要获得更好的性能,应将这些位置设置为指向不同的物理设备。 | ${hadoop.tmp.dir}/dfs/name |
| dfs.name.edits.dir | 这决定了事务(编辑)文件应存储在 NameNode 上的何处。 理想情况下,为了实现冗余,您可以定义一个逗号分隔的目录列表,以复制所有目录中的事务文件。 默认值与dfs.name.dir相同。 | ${dfs.name.dir} |
| dfs.namenode.handler.count | 它指定 NameNode 的服务器线程数。 如果您有一个大型且繁忙的群集,则应增加此值。 | 10 |
| dfs.replication | 这决定了群集上存储的每个数据块的默认数据块复制编号。 此数字可以在创建文件时指定。 定义一个较大的值允许更多的 DataNode 在块不可用之前出现故障;但是,这将增加存储数据所需的网络 I/O 量和磁盘空间要求。 这还可能增加映射任务将具有输入分割的本地副本的概率。 | 3 |
| dfs.replication.considerLoad | 这是一个布尔值,用于在挑选复制位置时决定 DataNode 负载。 | true |

更改块大小会影响很多事情。 在大多数情况下,拆分数据块大小较大的文件会产生较少的数据块。 因此,这将减少 NameNode 的元数据,这对于非常大的文件非常重要。 此外,客户端可以在不与 NameNode 交互的情况下读取/写入更多数据。

块越少,存储文件所需的节点就越少。 这可能会降低并行访问的总吞吐量,并使数据本地任务的调度更加困难。

降低并行吞吐量还意味着您可能无法获得最大并行度,这可能会降低开销,并且您的集群可能未得到充分利用。 这增加了任务分散的机会,如果任务失败,则需要重做更多工作。

此外,块越少,每个任务要处理的数据就越多,这可能会导致额外的读/写操作。

core-site.xml 配置文件

core-site.xml文件是主要的 Hadoop 配置文件之一,包含整个 Hadoop 发行版通用的配置。 它存在于群集中的每台主机上。

基本上,core-site.xml的变量允许您覆盖或定义分布式文件系统名称、临时目录和其他一些与网络配置相关的参数。

|

配置变量

|

描述 / 描写 / 形容 / 类别

|

默认值

|
| --- | --- | --- |
| fs.default.name | 这决定了默认文件系统的名称(URI)。 此应为hdfs://NameNodeHostName:PORT。 | file:/// |
| hadoop.tmp.dir | 这决定了存储临时文件的路径。 | /tmp/hadoop-${user.name} |
| fs.checkpoint.dir | 它确定辅助 NameNode 存储检查点的目录列表。 它将检查点的副本存储在列表中的每个目录中。 | ${hadoop.tmp.dir}/dfs/namesecondary |
| io.file.buffer.size | 这决定了在磁盘文件读/写操作期间缓冲的数据量。 此缓冲区的大小通常是硬件页面大小的倍数(在英特尔 x86 上为 4096)。 | 4096 |

在小型群集的情况下,所有服务器通常由一台交换机连接。 因此,只有两个位置级别:在机器上离机。 当将数据从 HDFS 加载到 DataNode 的本地驱动器时,NameNode 将只计划将一个副本传输到本地 DataNode,并将从群集中随机挑选另外两台机器来存储数据的副本。

对于跨多个机架的较大 Hadoop 群集,确保所有机架上都存在数据副本非常重要。 这样,交换机故障不会因为副本可用而导致数据块不可用。

参数io.file.buffer.size设置 Hadoop 在 I/O 操作期间使用的缓冲区大小。 此参数默认设置为4096字节(4 KB)。 在现代系统上,可以将其增加到65536字节(64KB)或131072字节(128KB)以提高性能。

Hadoop MapReduce 指标

由于 Hadoop 系统的规模和分布式特性,诊断 Hadoop 程序的性能问题和监控 Hadoop 系统本身就很困难。 尽管 Hadoop 系统导出了许多文本度量和日志,但这些信息可能很难解释,也不能被许多应用程序员完全理解。

目前,Hadoop 通过日志和指标 API 来报告关于整个系统性能的粗粒度指标。 不幸的是,它缺少每个作业/每个任务级别的重要指标,例如磁盘和网络 I/O 利用率。 在 Hadoop 系统中运行多个作业的情况下,它也缺乏反映每个任务的集群资源利用率的指标。 这导致集群管理员难以测量其集群利用率并设置 Hadoop 系统的正确配置。

此外,Hadoop 生成的日志可能会过大,这使得手动处理它们变得极其困难,并且很难回答这样一个简单的问题:“为什么特定数量的映射器/减少器不能实现最佳吞吐量?” 以下屏幕截图详细显示了部分 Hadoop 作业历史视图:

Hadoop MapReduce metrics

性能监控工具

监视 Hadoop 群集节点上的基本系统资源(如 CPU 利用率和平均磁盘数据传输率)有助于了解这些硬件资源的总体利用率,并在诊断性能问题时识别任何瓶颈。 监控 Hadoop 集群包括监控集群节点上系统资源的使用情况以及监控关键服务指标。 最常监视的资源是 I/O 带宽、每秒磁盘 I/O 操作数、平均数据传输率、网络延迟以及平均内存和交换空间利用率。

Hadoop 性能监视建议收集性能计数器的数据,以确定各种任务的响应时间是否在可接受的执行时间范围内。 一段时间内 MapReduce 任务和 HDFS 存储容量的平均利用率百分比表明您的群集资源是得到最佳利用还是未得到充分利用。

Hadoop 为监控和调试 Hadoop 服务提供了大量的指标和信息源。 它需要从群集节点关联和收集这些系统和服务指标,以分析 Hadoop 群集的整体状态,并诊断发现的任何问题。

您可以通过使用成熟的开源监控系统(如ChukwaGangliaNagiosAmbari(非详尽列表))将 Hadoop 提供的各种指标和信息源整合为更有意义的特定于服务的摘要、图表和警报,从而增强您的监控体验。

使用 Chukwa 监控 Hadoop

Chukwa(http://incubator.apache.org/chukwa/)是一个开源数据收集系统,用于监视和分析大型分布式系统。 它构建在 Hadoop 之上,包括一个用于监视、分析和查看结果的强大而灵活的工具包。

Chukwa 的许多组件都是可插拔的,便于自定义和增强。 它为处理收集的数据提供了一个标准化的框架,在收集和分析能力方面可以扩展到数千个节点。

使用 Ganglia 监控 Hadoop

Ganglia(http://ganglia.sourceforge.net/)最初是由加州大学伯克利分校(University of California,Berkeley)开发的。 其目的是提供一个健壮且耗费资源的解决方案来监控计算集群的性能。 此群集可以包含数百或数千个节点。 基本上,Ganglia 收集每个受监视节点的高级变量,如 CPU 利用率和空闲磁盘空间。 此外,它还可用于监视出现故障的群集节点。

当前的 Hadoop 版本内置了对 Ganglia(3.0+版本)的支持。 它是一个高度可扩展的群集监控工具,可提供有关单个群集或一组群集或群集中单个计算机的状态的图形化视图信息。

Ganglia 的架构和 Hadoop 上的实现支持集群的联合,监控每个集群中的状态并聚合这些状态。 该体系结构包括一个Ganglia Collector,它运行监视守护进程并收集每个集群的指标。 它还运行一个元守护程序,用于聚合所有群集的指标。 Ganglia Collector 提供了一个 Web 用户界面,可以显示内存使用情况、磁盘使用情况、网络统计信息、正在运行的进程和其他指标的实时动态视图。

使用 Nagios 监控 Hadoop

Nagios(http://www.nagios.org/)是一个流行的开源监控工具系统,在高性能计算(HPC)等环境中大量使用,旨在获取系统资源指标。 您可以使用它监视 Hadoop 集群资源以及应用和操作系统属性的状态,例如 CPU 使用率、磁盘空间和内存使用率。

Nagios 有一个集成的内置通知系统,它侧重于警报,而不是收集和跟踪系统指标(如 Ganglia)。 当前版本的 Nagios 允许您在目标主机上运行代理,并为收集有关 Hadoop 集群状态的指标和信息数据提供了灵活且可定制的框架。

Nagios 可用于解决不同的监控角度:

  • 获取有关您的 Hadoop 基础架构组织的即时信息
  • 在系统故障时发出和接收警报
  • 分析、报告和生成有关群集利用率的图表,并就未来的硬件采购做出决策
  • 检测和预测未来的问题
  • 监视如何耗尽队列,并查找用于运行作业的节点的可用性

备注

Nagios 将被视为健康检查和警报监控工具。

使用 Apache Ambari 监控 Hadoop

ApacheAmbari 项目(Hadoop)简化了 http://incubator.apache.org/ambari/管理和集群监控。 其主要目标是简化多实例环境中 Hadoop 群集的部署和管理。

Ambari 提供了一组直观且易于使用的工具来监控 Hadoop 集群,隐藏了 Hadoop 框架的复杂性。 它为管理员提供 REST 风格的 API,以允许与其他系统集成。 此外,Ambari 依靠 Ganglia 和 Nagios 提供的警报系统功能在需要时发送电子邮件以引起管理员的注意(例如,当节点出现故障时,剩余磁盘空间较低,等等)。 此外,Ambari 支持安装安全的(基于 Kerberos 的)Hadoop 群集,提供基于角色的用户身份验证、授权、审核以及与 LDAP 和 Active Directory 集成以进行用户管理,从而支持 Hadoop 安全性。

提示

如果通过 Apache Ambari 设置 Hadoop 集群,还可以使用它设置 Ganglia 或 Nagios 等监控工具。

摘要

在本章中,我们讨论了 Hadoop MapReduce 性能调优,并了解了应用开发人员和集群管理员如何调优 Hadoop 以提高 MapReduce 作业的性能。

我们了解了与 CPU、磁盘 I/O、内存和网络利用率相关的大多数配置变量,并讨论了这些变量可能如何影响 MapReduce 作业的性能。

然后,我们了解了 Hadoop 指标,并推荐了一些开源监控工具,这些工具增强了 Hadoop 监控体验,对 Hadoop 集群管理员和应用开发人员非常方便。

在下一章中,我们将学习如何根据性能指标识别资源瓶颈,并了解常见的性能调优方法。

三、检测系统瓶颈

如何知道 Hadoop MapReduce 作业是否以最佳方式执行其工作? 在我们的咨询实践中,我们收到的与性能相关的最常见请求之一是找出执行特定作业需要很长时间的原因,并对瓶颈事件进行故障排除。

了解 Hadoop MapReduceHadoop 参数概述中,我们了解了可能影响 Hadoop MapReduce 性能的因素和 Hadoop MapReduce 公共参数的设置。 在本章中,我们将继续我们的旅程,学习如何检测潜在的系统瓶颈。

本章介绍性能优化过程、在任何优化作业之前创建基准的重要性,以及如何使用此基准优化集群。 您还将学习如何识别资源瓶颈以及如何打破这些瓶颈。

在本章中,我们将介绍以下内容:

  • 介绍性能调优过程
  • 创建性能基准
  • Hadoop 集群调优方法
  • 确定系统资源瓶颈

性能调整

性能调优的基本目标是确保给定集群配置中的所有可用资源(CPU、RAM、I/O 和网络)都可用于特定作业,并以平衡的方式使用。

Hadoop MapReduce 资源分为计算、内存、网络带宽和输入/输出存储等类别。 如果这些资源中的任何一个性能不佳,这将影响 Hadoop 的性能,这可能会导致您的作业运行缓慢。 因此,调优 Hadoop MapReduce 性能就是在 Hadoop 集群上获得均衡的资源,而不仅仅是调优一个或多个变量。

简而言之,调优 Hadoop MapReduce 作业流程由多个分析组成,这些分析调查 Hadoop 指标和指示符,以便了解执行时间、使用的内存量以及要读取或存储在本地文件系统中的字节数,等等。

Hadoop 性能调优是一个迭代过程。 您启动一个作业,然后分析 Hadoop 计数器,调整它们,然后重新运行该作业。 然后重复此过程,直到达到 Hadoop 群集的最终性能。 以下步骤描述了此过程:

  1. Create a baseline, as you first need to evaluate the overall system performance. You will run your job the first time using the default configuration settings of your Hadoop cluster. This will be your baseline.

    拥有此基线后,您将开始调优变量值以最佳地执行作业。 因此,我们可以说,性能调优是测量和分析时间消耗情况的主要手段。

  2. 您将分析 Hadoop 计数器,修改和调整一些配置设置,然后重新运行该作业。 将结果与基线进行比较。 分析完成后,您可以查看结果并接受或拒绝推论。

  3. 重复步骤 2,直到作业的执行时间最短。

下图说明了 Hadoop 性能调整过程:

Performance tuning

创建绩效基准

让我们首先为我们的系统创建一个性能基准。 在创建基线时,您应该保留 Hadoop 的默认配置设置,并使用 TeraSort 基准测试工具,该工具是随 Hadoop 分发包提供的示例 JAR 文件的一部分。 TeraSort 被认为是比较 Hadoop 性能的行业标准基准。 此基准测试工具尝试使用整个 Hadoop 群集尽可能快地对 1 TB 数据进行排序,并分为三个主要模块:

  • TeraGen:此模块用于生成所需大小的文件作为输入,通常范围在 500 GB 到 3TB 之间。 一旦 TeraGen 生成输入数据,它就可以在文件大小相同的所有运行中使用。
  • TeraSort:此模块将跨 Hadoop 集群对输入文件进行排序。 TeraSort 在计算、网络带宽和 I/O 响应方面强调了 Hadoop 集群在两层(MapReduce 和 HDFS)上的作用。 排序后,将创建单个简化任务,以检查排序阶段的输出是否有效。
  • TeraValidation:此模块用于提高准确性并验证排序后的数据。

为了运行 TeraSort 基准测试工具并获得基线,您首先需要生成将按 TeraSort 排序的样例输入数据。 使用以下命令行指定文件大小(104857600=10 GB)和输出 DFS 目录(/data/input),运行 TeraGen 以生成 10 GB 大小的文件(根据您的群集容量,您可以生成更大的文件大小,最高可达 3 TB):

hadoop jar $HADOOP_PREFIX/hadoop-*examples*.jar teragen 104857600 /data/input

备注

/data/inputHDFS 目录必须为空,否则将出现 Java 异常错误。 如果不是空的,可以使用以下命令行清除此目录:

hadoop dfs -rmr /data/input

要检查样例数据文件是否已正确生成,请打开 Hadoop DFS 主页(http://machinename:50070/dfshealth.jsp)并检查DFS Used行,该行应该反映生成的数据的大小。 您还可以浏览 DFS 文件系统中的/data/input目录,该目录应该包含所有生成的文件(part-0000-part-00*)。

生成输入数据后,使用以下命令行运行TeraSort,该命令行指定输入和输出数据文件夹:

hadoop jar $HADOOP_PREFIX/hadoop-*examples*.jar terasort /data/input /d
ata/output

备注

优化和调整群集的唯一真正方法是分析计数器,更改设置的配置,重新运行 MapReduce 作业,然后返回更改设置并重新运行作业,直到您将完成时间降低到可能的最低值。

TeraSort 作业完成后,您将获得性能基准。 现在我们可以转到迭代步骤(在上一节中讨论),然后分析这些设置。

为了说明如何使用这个性能基准,让我们假设我们想要在一个三节点 Hadoop 集群上处理一个 10 GB 的文件(每个节点有一个带四个核心的 CPU、4 GB RAM 和 40 GB 硬盘空间)。 根据默认设置(在下表中报告),此作业耗时 4 分 3 秒。

这是一个具有中等数据大小上下文的轻型 Hadoop 集群。 因此,可以按如下方式配置群集:

  • 复制因子可以降低到 2。
  • 数据块大小最高可增加到 128 MB。
  • io.sort.factor参数取决于节点上的可用内存。 每个节点有 4 GB 的内存;因此,我们可以为中间映射数据提供更多内存。
  • io.sort.mb值应该是io.sort.factor10,即 3510=350MB,这是允许映射中间数据的大小。
  • 可以将mapred.tasktracker.map.tasks.maximummapred.tasktracker.reduce.tasks.maximum的值设置为 CPU 核数减 1。 因此,该值应为4-1=3
  • 建议将mapred.reduce.tasks的值设置在集群容量的 50%到 99%之间,这样所有的 Reduce 任务都可以在一次浪潮中完成。 因此,此参数可以设置为0.953(节点)3=8.55个减速器插槽,向下舍入为 8。

默认值mapred.child.java.opts可以增加到 500 MB,以允许更多内存用于映射和减少器任务。 但是,这不应超过节点上的可用 RAM。 因此,您可以使用以下公式设置此值:(mapred.tasktracker.map.tasks.max+mapred.tasktracker.duce e.tasks.max)要分配的内存(MB)<可用 RAM-保留内存,即(3+3)500<4096-350(保留的中间映射输出)。

现在,我们再次运行相同的 MapReduce 作业,报告配置的设置,并将结果与基线的结果进行比较。 我们可以选择接受或拒绝调整结果,然后再次重新调整设置,直到我们对结果满意为止。

下表总结了所有这些设置值:

|

Hadoop 参数

|

基数 / 起点 / 底线 / 基线

|

调谐 1

|

调谐 2

|

调谐 3

|
| --- | --- | --- | --- | --- |
| dfs.replication | 3. | 2 个 |   |   |
| dfs.block.size | 67108864 | 134217728 |   |   |
| dfs.namenode.handler.count | 10 个 | 20 个 |   |   |
| dfs.datanode.handler.count | 3. | 5. |   |   |
| io.sort.factor | 10 个 | 35 岁 |   |   |
| io.sort.mb | 100 个 | 350 |   |   |
| mapred.tasktracker.map.tasks.maximum | 2 个 | 3. |   |   |
| mapred.map.tasks | 2 个 | 2 个 |   |   |
| mapred.reduce.tasks | 1. | 8 个 |   |   |
| mapred.tasktracker.reduce.tasks.maximum | 2 个 | 3. |   |   |
| mapred.reduce.parallel.copies | 5. | 5. |   |   |
| mapred.job.reduce.input.buffer.percent | 0 | 0 |   |   |
| mapred.child.java.opts | -Xmx200m | -Xmx500m |   |   |
| … |   |   |   |   |
| 输入数据大小 | 10 GB | 10 GB |   |   |
| 群集的节点号 | 3. | 3. |   |   |
| 作业执行时间(秒) | 二百四十三 | 185 |   |   |
| 对基线的改进(百分比) |   | 23.86% |   |   |

确定资源瓶颈

通常,当系统的一个资源消耗的时间超过完成其任务所需的时间,并迫使其他资源等待,从而降低整体系统性能时,就会出现瓶颈。

在深入调优 Hadoop 集群之前,最好确保集群是稳定的,并且 MapReduce 作业是可操作的。 我们建议您验证群集的硬件组件是否配置正确,如有必要,请将 Hadoop 堆栈的任何软件组件升级到最新的稳定版本。 您还可以执行 MapReduce 作业(如 TeraSort 或 PI Estimator)来对集群施加压力。 这是获得健康和优化的 Hadoop 集群的非常重要的一步。

一旦您的集群硬件和软件组件得到了良好的配置和更新,您就需要创建一个基准性能压力测试。 为了给 Hadoop 集群施加压力,您可以使用 Hadoop 微基准测试,比如 TeraSort、TestDFSIO、NNBench 或 MRBench。 所有这些基准测试都是 Hadoop 发行包的一部分。

但是,请记住,MapReduce 作业实际上是一个具有多个阶段的管道,每个阶段需要不同类型的资源。 下图描述了 Hadoop 完成 MapReduce 作业所需的主要资源(CPU、RAM、I/O 和网络带宽),这些资源可能会造成潜在的资源瓶颈。

Identifying resource bottlenecks

识别 RAM 瓶颈

RAM(内存)是一个潜在的瓶颈来源,可能会对 MapReduce 作业性能产生重大影响。 每个节点上的可用内存量应该足以处理要处理的作业的需求。 您需要确保正确配置和设置集群节点的内存。 否则,在工作负载密集的上下文中,Map 和 Reduce 任务可能会启动,但会立即失败。 此外,当 Hadoop 没有足够的内存来处理数据时,它将使用系统存储来存储其数据,内存和存储之间的这些交换可能会很耗时,并会降低系统速度。

为了识别潜在的内存瓶颈,您应该使用监控工具(如 Ganglia、vmstat)突出显示交换内存的使用情况。 这将帮助您确定 map 和 Reduce 任务是否有足够的内存来完成其工作。 否则,您可能应该扩展节点的物理内存或调整映射器和减少器的数量。

以下屏幕截图显示了vmstat输出报告,其中显示了交换到磁盘的内存量(so列)和从磁盘交换的内存量(si列):

Identifying RAM bottlenecks

识别资源弱点中,您将了解如何根据每个节点上的物理内存配置映射的数量和归约任务。

识别 CPU 瓶颈

CPU 是在 Map 和 Reduce 计算阶段处理数据时的关键资源。 密集的 CPU 活动可能是map和/或reduce用户函数代码中密集计算的结果。 这种高 CPU 利用率可能是一个潜在的瓶颈。 此外,如果在处理数据之前需要等待其他资源馈送数据,则 CPU 通常可能处于空闲状态。 这通常是由于 map 和 Reduce 任务配置错误以及 Hadoop 框架未充分利用 CPU 资源造成的。 CPU 瓶颈的症状并不难识别。 通常,处理器负载(时间)通常超过 90%;在多处理器系统上,总处理器负载(时间)超过 50%。 但是,这些症状并不总是表明处理器有问题。

要确定您的节点是否处理非常高的 CPU 活动,您应该使用监视工具检查处理器实际运行的所有进程和线程。 然后,您应该确定是否有个特定进程独占 CPU,并了解为什么会这样做。 调优 map 和 Reduce 任务的数量可能会解决瓶颈,作为最后的手段,您可能需要升级到速度更快的处理器或添加额外的处理器,这对整个 MapReduce 作业是有好处的。

确定存储瓶颈

存储 I/O 是第二个最常见的瓶颈来源,不幸的是,Hadoop 在 MapReduce 管道过程的许多阶段都需要该资源。 存储资源会降低 MapReduce 作业的性能,成为执行流水线各个阶段的瓶颈。 在执行任何存储调整操作之前,建议对您的存储进行基准测试,以了解其 I/O 吞吐量能力。 您可以通过运行 Hadoop HDFS 基准测试 TestDFSIO 来实现这一点,这是一个针对 HDFS 的读/写测试。 此外,要运行分布式基准测试,您可以使用 DFSCIOTest,它是libhdfs的 I/O 分布式基准测试。

存储瓶颈表现为磁盘活动率持续高于 85%。 但这种症状也可能是内存或 CPU 瓶颈的结果,看起来像是磁盘瓶颈。 这就是为什么在尝试识别磁盘瓶颈之前,您应该首先检查是否存在任何内存或 CPU 瓶颈。

使用 TestDFSIO 基准测试工具将帮助您了解集群的 NameNode 和 DataNode 存储有多快。 下图显示了典型的 TestDFSIO 输出日志:

Identifying storage bottlenecks

要使用 TestDFSIO 启动读/写基准测试,您可以使用以下命令行,该命令行写入/读取 10 个文件,每个文件大小为 1000MB。

hadoop jar hadoop-test.jar TestDFSIO -write -nrFiles 10 -fileSize 1000
hadoop jar hadoop-test.jar TestDFSIO -read -nrFiles 10 -fileSize 1000

因此,使用 TestDFSIO 的 log 信息输出,可以使用以下公式计算存储吞吐量:

总读取吞吐量和总写入吞吐量=文件数吞吐量(MB/秒)*。

识别资源弱点中,您将了解如何确定给定节点的存储容量。

识别网络带宽瓶颈

网络带宽也可能是一个可能的瓶颈源。 通常,当您必须通过网络传输大量数据时,就会出现此瓶颈。 在 Hadoop 上下文中,当存在大量数据时,就会出现这个瓶颈。 当 Reduce 任务在混洗阶段从 MAP 任务拉取数据时,以及作业将最终结果输出到 HDFS 中时,就会出现高网络利用率。

对于存储系统,建议对 Hadoop 群集施加压力,以了解您的网络带宽能力,并确保网络利用率在用于特定作业时不会成为瓶颈。 需要持续监控网络带宽,以便能够确定您的数据是否可以在集群的节点之间高效传输。

提示

要分析系统的性能,您还可以使用 Linux OS 实用程序,如dstattophtopiotopvmstatiostatsarnetstat,它们有助于捕获系统级性能统计信息。 然后使用收集到的数据研究 Hadoop 作业如何利用集群的不同资源,以及哪些资源会造成瓶颈或处于争用状态。

要识别和深入分析由软件和/或硬件事件引起的潜在性能瓶颈,您还可以使用 Linux 分析器,如perfstrace

网络性能分析是其他监控工作的后续工作。 通常,您应该从检查您的网络硬件开始,包括电缆、集线器、交换机等外部元素。 然后,确保您的网络组件使用的是最新的网络适配器和最新的设备驱动程序版本。 您还应该检查网络的配置,并确保将其设置为尽可能高和最宽的带宽。

要识别潜在的网络带宽瓶颈,您应该监视和检查网络数据和中断率(通过网络接口卡发送和接收的字节数)。 如果数据速率接近或等于可用带宽的八分之一,则可以推断这可能是网络过载的迹象。 此外,较高的中断率意味着您的系统因网络流量的中断而超载。

提示

您可以使用dstat作为dstat --nf来显示数据速率,使用dstat -idstat -if来显示中断速率,来检查网络流量数据和中断速率。

摘要

在本章中,我们介绍了性能调优过程周期,并了解了 Hadoop 计数器。 我们介绍了 TeraSort 基准测试及其 TeraGen 模块,并学习了如何生成性能基准,该基准将在调优 Hadoop 集群时用作参考。 我们还学习了通过一个三节点 Hadoop 集群示例来优化 Hadoop 集群的方法,并建议调整一些设置参数以提高集群的性能。

然后,我们继续讨论资源瓶颈,以及每个 MapReduce 阶段都涉及哪些组件,或者哪些组件可能是瓶颈的根源。 对于每个组件(CPU、RAM、存储和网络带宽),我们了解了如何识别系统瓶颈,并提出建议试图消除它们。

在下一章中,我们将学习如何识别 Hadoop 集群资源弱点以及如何正确配置 Hadoop 集群。 继续读!

四、识别资源弱点

每个 Hadoop 集群由不同的机器和不同的硬件组成。 这意味着每个 Hadoop 安装都应该针对其独特的集群设置进行优化。 为了确保 Hadoop 高效地执行作业,您需要检查集群并识别潜在的瓶颈,以便消除它们。

本章介绍了一些识别群集弱点的方案和技术。 然后,我们将介绍一些公式,这些公式将有助于确定 NameNode 和 DataNode 的最佳配置。 之后,您将学习如何正确配置集群,以及如何确定集群的映射器和减少器的数量。

在本章中,您将了解以下内容:

  • 根据一些场景检查集群的弱点
  • 识别 CPU 争用和不适当数量的映射器和减少器
  • 识别大量 I/O 和网络流量
  • 要调整群集大小并定义其大小,请执行以下操作
  • 要正确配置群集,请执行以下操作

识别群集弱点

事实证明,根据集群的硬件和节点数量调整 Hadoop 框架的配置可以提高性能。 为了确保 Hadoop 框架有效地使用您的硬件,并且您已经正确定义了映射器和减少器的数量,您需要检查您的环境,以确定是否存在节点、CPU 或网络弱点。 然后,您可以决定 Hadoop 框架是应该作为一组新的配置运行,还是需要优化。

在接下来的几节中,我们将介绍可能导致您的工作表现不佳的常见情况。 每个场景都有自己的技术来显示如何识别问题。 该场景涵盖集群节点的健康状况、输入数据大小、大量 I/O 和网络流量、并发任务不足、和 CPU 争用(当任何较高优先级的 CPU 绑定任务正在运行时,所有较低优先级的任务都必须等待,并且没有其他 CPU 可以处理其他工作时,就会发生这种情况)。

检查 Hadoop 群集节点的运行状况

通常,在多节点 Hadoop 群集中,节点可能会由于硬件故障(如硬盘故障或电源故障)而出现故障。 在这种情况下,Hadoop 无法使用该节点处理作业,并且该节点将被框架标记为黑名单灰名单、或排除。 这也可能发生在单节点群集中。 在这种情况下,节点关闭,您无法处理任何 MapReduce 作业。 检查 Hadoop 是否正在使用集群中的所有节点是获得集群完全性能的第一步。

要确定您的群集中是否有未使用的节点,您应该检查 JobTracker 页面上显示的群集摘要。 群集摘要必须显示黑名单节点灰名单节点排除的节点的每个0节点。 以下屏幕截图显示了部分群集摘要,其中显示了列入黑名单、灰名单和排除的节点:

Checking the Hadoop cluster node's health

您的群集不应该有任何未使用的节点。

Hadoop 框架会忽略个排除的节点,并且不允许它们连接到群集。 此外,Hadoop 不会调度任何作业处理到列入黑名单的节点,它们不会对集群做出贡献;从集群中删除列入黑名单的节点可能会对作业的性能产生重大影响。

灰色列出的节点是具有间歇性故障(可能成为永久性故障)的节点,并且会由于任务失败而影响作业的性能和执行时间。 您应该通过更换或删除节点故障来快速做出反应。 虽然管理员通过维护dfs.hosts.exclude属性(对于 HDFS)和mapred.hosts.exclude属性(对于 MapReduce)来控制排除的节点,但是最好使用 Hadoop 集群管理工具(如NagiosGanglia、或Cacti)为列入黑名单和灰色名单的节点设置监视。

检查输入数据大小

在某些情况下,您可能会运行 MapReduce 作业,这会花费一定的时间来处理数据,如果您重新运行相同的作业,您会发现处理输入数据所需的时间更长。 这不一定是由于 MAP 功能的低效实现或资源瓶颈,而可能是由于在第一次和第二次运行之间增长的输入数据。 例如,如果您在特定日期运行 Web 日志处理作业,而这一天的日志文件比正常日志文件大得多,则可能会发生这种情况,这可能是映射作业运行缓慢的原因。

要确定映射中是否存在不同的输入数据大小(或减小输入大小),可以将慢速作业的映射输入和输出数据大小与以前作业的基线的输入和输出大小进行比较。 基本上,您需要比较四个计数器:HDFS_BYTES_WRITEDReduce Shuffle BytesMap Output BytesMap Input Bytes

下面的屏幕截图显示了您需要关注的 Hadoop 计数器,并将它们的值与您的基准值进行比较:

Checking the input data size

检查海量 I/O 和网络流量

正如在第 3 章检测系统瓶颈中所讨论的,磁盘 I/O 或网络带宽上的瓶颈可能会导致作业性能降低,甚至导致 Java 异常(通常是超时异常)。 读取输入数据或写入输出数据时可能会发生大量 I/O。 如果磁盘 I/O 或网络流量的吞吐量不高,计算资源将受到限制,只能等待传入的数据,并将花费时间等待数据进行处理。

读取非常大的输入数据时可能会出现瓶颈,这可能会限制映射器在处理输入数据之前等待(或空闲)。 要识别大量输入数据导致的大量 I/O,您应该关注两个 Hadoop 计数器值:FILE_BYTES_READHDFS_BYTES_READ。 如果您有非常大的输入数据,则这些计数器值应该很大。

下面的屏幕截图显示了您应该关注的 Hadoop 计数器,以便识别由于大量输入数据而产生的大量 I/O:

Checking massive I/O and network traffic

大量输出数据也可能导致大量 I/O。 当 Reducer 将其输出写入 HDFS 时,通常会出现这种情况,这需要大量的 I/O 写入操作。 要识别任何巨大的瓶颈 I/O 或网络流量,如果看到很大的数字,可以检查Bytes WriteHDFS_Bytes_WritedHadoop 计数器值。 如果出现这样的瓶颈,您应该观察到两个计数器的高值。 下面的屏幕截图显示了应特别注意的计数器,以识别由大量输出数据引起的大量 I/O 和网络流量:

Checking massive I/O and network traffic

您还应该考虑复制因子(通过dfs.replication参数配置)。 它确定集群中存储的每个块的默认块复制数量(请参阅第 2 章Hadoop 参数概述)。 如果您的复制系数大于 1,则意味着要读/写的数据更多,数据将通过网络复制。 因此,您通常会在 I/O 和网络使用率的计数器中观察到较高的值。

要解决任何海量 I/O 或网络带宽瓶颈,您可以压缩数据或使用组合器。 这些技术将在第 6 章优化 MapReduce 任务中讨论。

检查并发任务是否不足

糟糕的 MapReduce 作业配置可能会使许多资源处于空闲状态。 基本上,并发任务由集群的 Map 和 Reduce 插槽的总容量以及为处理特定作业而定义的 Map 和 Reduce 作业的数量决定。

如果群集或作业的并发任务配置错误,CPU 资源通常会空闲或超载。 最常见的错误配置是使集群中的 CPU 核心处于空闲状态,没有分配给它们任何工作。 这种错误配置还可能导致 I/O 吞吐量和网络带宽未得到充分利用,因为没有来自 CPU 核心的请求。 如果您错误配置了集群或作业容量,您将看到 DataNode 上的 CPU 利用率很低。

要检查未充分利用的配置,应关注 Hadoopnum TasksRunningMap Task CapacityReduce Task Capacity计数器,这些计数器有助于检查 CPU 核心是否已满负荷使用。 Map Task CapacityReduce Task Capacity值应至少设置为 CPU 的核数,即 1。

以下屏幕截图显示了 MapReduce 管理页面中的部分群集摘要(映射任务容量归约任务容量):

Checking for insufficient concurrent tasks

下面的屏幕截图显示了部分作业详细信息,您可以在其中找到运行 Hadoop 计数器的Num Tasks。 在本章后面的部分中,您将了解如何正确设置这些值。

Checking for insufficient concurrent tasks

检查 CPU 争用

如今,所有操作系统都是多任务系统,并使用多核 CPU。 为了实现多任务处理,并使多个进程共享一个 CPU,操作系统需要在进程之间切换时存储和恢复 CPU 状态(上下文),以便以后可以从相同的时间点恢复执行。 这就是所谓的上下文切换。 操作系统为将由 CPU 执行的每个任务分配优先级。 当所有较低优先级的任务在任何较高优先级的 CPU 绑定任务正在运行时等待,并且没有其他 CPU 可以处理其他任务时,就会发生争用。 因此,当每秒的上下文切换次数很高时,这意味着 CPU 很忙,并且花费了大量时间来存储和恢复进程状态。

因此,过度的上下文切换意味着您可能在主机上运行了太多任务。 配置 MapReduce 时,根据经验,mapred.tasktracker.map.tasks.maximummapred.tasktracker.reduce.tasks.maximum的总和应该在总 CPU 核心数左右,即在正常情况下为 1,或主机上逻辑核心数的 120%到 150%。 由于超线程,CPU 可能一次处理多个作业。

为了显示上下文切换的统计信息,您可以使用vmstatLinux 工具,该工具非常有助于显示 CPU 的利用率统计信息(cs=上下文切换)。

下面的屏幕截图显示了一台不常用机器上的vmstat输出。 列中的表示每秒中断数。 如果该数字很高(1000*或更高),则表示机器使用率很高或硬件有问题。

Checking for CPU contention

备注

vmstat工具是使用–S M 1命令启动的。 –S参数用于设置单位大小,M表示单位大小:1048576,最后一个参数1表示更新之间的延迟。

下面的屏幕截图显示了一台使用率很高的机器上的vmstat输出。 您可以观察到,与使用率较低的机器相比,每秒的中断次数(列中的)较高。

Checking for CPU contention

调整您的 Hadoop 群集

正如前面所讨论的,Hadoop 的性能取决于多种因素,这些因素基于配置良好的软件层和规模合理的硬件资源,这些资源可以有效地利用其 CPU、内存、硬盘(存储 I/O)和网络带宽。

规划 Hadoop 集群仍然是一项复杂的任务,需要最少的 Hadoop 体系结构知识,这可能超出了本书的范围。 这就是我们在本节中试图通过提供解释和公式来帮助您最好地估计您的需求来更清楚地说明这一点。 我们将介绍一个基本指导原则,它将帮助您在调整群集规模时做出决策,并回答一些如何规划有关群集需求的问题,如下所示:

  • 如何规划我的存储空间?
  • 如何规划我的 CPU?
  • 如何规划我的记忆?
  • 如何规划网络带宽?

在调整 Hadoop 集群的大小时,您还应该考虑最终用户将在集群上处理的数据量。 这个问题的答案将引导您确定集群中需要多少台机器(节点)才能有效地处理输入数据,并确定每台机器(节点)的磁盘/内存容量。

Hadoop 是主/从架构,需要大量内存和 CPU 限制。 它有两个主要组件:

  • JobTracker:这是此体系结构中的关键组件,用于监视群集上运行的作业
  • TaskTracker:这将在群集的每个节点上运行个任务

要高效工作,HDFS 必须拥有高吞吐量硬盘驱动器以及支持 HDFS 读写模式(大数据块)的底层文件系统。 此模式每次定义一个大的读取(或写入),块大小分别为 64MB、128MB 和 256MB。 此外,网络层应该足够快,以处理中间数据传输和阻塞。

HDFS 本身基于主/从体系结构,有两个主要组件:NameNode/辅助 NameNode 和 DataNode 组件。 这些都是关键组件,需要大量内存来存储文件的元数据信息,如属性和文件本地化、目录结构、名称以及处理数据。 NameNode 组件可确保在群集中正确复制数据块。 第二个组件是 DataNode 组件,它管理 HDFS 节点的状态并与其数据块交互。 它需要大量的 I/O 来处理和数据传输。

通常,MapReduce 层有两个主要先决条件:输入数据集必须足够大,足以填充一个数据块,并拆分成较小的独立数据块(例如,一个 10 GB 的文本文件可以拆分成 40,960 个块,每个块大小为 256 MB,并且任何数据块中的每行文本都可以单独处理)。 第二个先决条件是它应该考虑数据局部性,这意味着 MapReduce 代码被移动到数据所在的位置,而不是相反(移动几兆字节的代码以接近要处理的数据比通过网络或磁盘移动许多数据块更有效)。 这涉及到拥有一个分布式存储系统,该系统公开数据局部性并允许在任何存储节点上执行代码。

关于网络带宽,它在两种情况下使用:在复制过程中和在文件写入之后,以及在节点发生故障时的复制因子平衡期间。

调整 Hadoop 群集的最常见做法是根据所需的存储量调整群集。 输入系统的数据越多,需要的机器就越多。 每次向群集中添加新节点时,除了新的存储容量外,还会获得更多的计算资源。

让我们考虑一个基于存储的示例集群增长计划,并了解如何确定所需的存储、内存量和集群中的 DataNode 数量。

| **每日数据输入** | 100 GB | *每日数据输入占用的存储空间=每日数据输入*复制系数=300 GB* | | **HDFS 复制系数** | 3. | | **月度增长** | 5% | *月量=(300*30)+5%=9450 GB**一年后=9450*(1+0.05)^12=16971 GB* | | **中间 MapReduce 数据** | 25% | *专用空间=硬盘大小*(1-每个磁盘的非 HDFS 保留空间/100+中间 MapReduce 数据/100)**=4*(1-(0.25+0.30))=1.8 TB(节点容量)* | | **每个磁盘的非 HDFS 保留空间** | 百分之三十 | | **硬盘大小** | 4 TB | | **需要处理的 DataNode 数量:***整月数据=9.450/1800~=6 个节点**12 个月数据=16.971/1800~=10 个节点**全年数据=157.938/1800~=88 节点* |

提示

请勿在 DataNode 上使用 RAID 阵列磁盘。 HDFS 提供自己的复制机制。 还需要注意的是,对于每个磁盘,其容量的 30%应该保留给非 HDFS 使用。

很容易确定 NameNode 和辅助 NameNode 所需的内存。 NameNode 管理内存中的 HDFS 集群元数据所需的内存必须与操作系统所需的内存相加。 通常,辅助 NameNode 所需的内存应该与 NameNode 相同。 然后,您可以应用以下公式来确定内存量:

| **NameNode 内存** | 2 GB-4 GB | *内存量=HDFS 集群管理内存+NameNode 内存+操作系统内存* | | **辅助 NameNode 内存** | 2 GB-4 GB | | **操作系统内存** | 4 GB-8 GB | | **HDFS 内存** | 2 GB-8 GB | | *至少 NameNode(二级 NameNode)内存=2+2+4=8 GB* |

确定 DataNode 内存量也很容易。 但是,这一次,内存量取决于安装在每个 DataNode 上的个物理 CPU 的核心编号。

| **数据节点进程内存** | 4 GB-8 GB | *内存量=每个 CPU 核的内存量*CPU 核数+DataNode 进程内存+DataNode TaskTracker 内存+OS 内存* | | **DataNode TaskTracker 内存** | 4 GB-8 GB | | **操作系统内存** | 4 GB-8 GB | | **CPU 核心号** | 4+4+ | | **每个 CPU 核心的内存** | 4 GB-8 GB |   | | *至少数据节点内存=4*4+4+4+4=28 GB* |

关于如何确定 CPU 和网络带宽,我们建议使用现在使用的多核 CPU,每个 CPU 至少有四个物理核心。 物理 CPU 内核越多,就越能提高作业的性能(根据讨论的所有规则,以避免未充分利用或过度利用)。 对于网络交换机,我们建议使用具有高吞吐量(例如 10 Gb)的以太网机架和 N x 10 Gb 以太网机架。

正确配置您的群集

要运行 Hadoop 并获得最高性能,需要对其进行正确配置。 但问题是如何做到这一点。 那么根据我们的经验呢,我们可以说这个问题没有一个单一的答案。 这些经验清楚地告诉我们,Hadoop 框架应该适用于它运行的集群,有时还应该适用于作业。

为了正确配置您的集群,我们建议您第一次使用默认配置运行 Hadoop 作业以获得基准(请参阅第 3 章检测系统瓶颈)。 然后,您将通过分析作业历史日志文件来检查资源的弱点(如果存在),并报告结果(运行作业所花费的测量时间)。 之后,您将反复调优 Hadoop 配置并重新运行该作业,直到获得满足业务需求的配置。

作业应该使用的映射器和减速器任务的数量很重要。 为作业选择合适的任务量可能会对 Hadoop 的性能产生巨大影响。

减少器任务数应小于映射器任务数。 谷歌报告说,一个减速器对应 20 个映射员;其他减速器给出了不同的指导方针。 这是因为映射器任务通常会处理大量数据,而这些任务的结果会传递给 Reducer 任务。 通常,Reducer 任务只是一个聚合函数,与映射器任务相比,它只处理一小部分数据。 此外,还必须考虑减速器的正确数量。

映射器和减少器的数量与 DataNode 上的物理核心数相关,这决定了可以在 DataNode 上并行运行的最大作业数。

在 Hadoop 集群中,主节点节点通常由机器组成,其中一台机器被设计为 NameNode,另一台机器被设计为 JobTracker,而集群中的所有其他机器都是充当 DataNode 和 TaskTracker 的从节点。 启动集群时,您开始启动主节点上的 HDFS 守护进程和所有数据节点计算机上的 DataNode 守护进程。 然后,在主节点上启动 MapReduce 守护进程:JobTracker,在所有从节点上启动 TaskTracker 守护进程。 下图显示了 Hadoop 守护进程的伪公式:

Configuring your cluster correctly

在配置集群时,您需要考虑需要分配给这些守护进程的 CPU 核心和内存资源。 在大数据环境中,建议在每个 DataNode 上为 HDFS 和 MapReduce 守护进程预留 2 个 CPU 核心。 在中小型数据环境中,您只能在每个 DataNode 上保留一个 CPU 核心。

一旦确定了最大映射器的插槽编号,就需要确定减速机的最大插槽编号。 根据我们的经验,DataNode 上的 Map 和 Reduce 任务之间有一种分布,可以提供良好的性能结果,将 Reducer 的插槽编号定义为与映射器的插槽编号相同或至少等于三分之二的映射器插槽。

让我们学习如何正确配置映射器和缩减器的数量,并假设以下集群示例:

|

集群机

|

犯规投球 / 同 no-ball

|

中等数据大小

|

大数据量

|
| --- | --- | --- | --- |
| DataNode CPU 核心 | 8 个 | 预留 1 个 CPU 核心 | 预留 2 个 CPU 核心 |
| DataNode TaskTracker 后台进程 | 1. | 1. | 1. |
| DataNode HDFS 守护程序 | 1. | 1. | 1. |
| 数据块大小 |   | 128 MB | 256 MB |
| 数据节点 CPU 利用率% |   | 95%至 120% | 95%至 150% |
| 群集节点 |   | 20 个 | 40 岁 |
| 复制因子 |   | 2 个 | 3. |

我们希望至少使用 95%的 CPU 资源,并且由于超线程,一个 CPU 内核可能会同时处理多个作业,因此我们可以将超线程因子范围设置在 120%和 170%之间。

| 大型数据上下文中一个节点上的最大映射器插槽数 | *=物理核数-保留核数*(0.95->1.5)**TaskTracker 保留核心=1+HDFS*保留核心=1 | | 假设节点上的 CPU 将使用高达 120%的空间(使用超线程):*最大映射器插槽数=(8-2)*1.2=7.2 向下舍入为 7* | | 让我们应用 2/3 映射器/减少器技术:*减速器槽的最大数量=7*2/3=5* | | 让我们定义群集的插槽数量:*映射器插槽:=7*40=280**减速器槽:=5*40=200* |

块大小(在第 2 章,Hadoop 参数概述中讨论)也用于增强性能。 默认的 Hadoop 配置使用 64MB 的块,而我们建议在您的配置中对中等数据上下文使用 128MB,对非常大的数据上下文使用 256MB。 这意味着映射器任务只能通过打开一个块来处理一个数据块(例如,128MB)。 在默认 Hadoop 配置(默认设置为2)中,需要两个映射器任务来处理相同数量的数据。 这可能会被认为是一个缺点,因为初始化一个以上的映射器任务和打开一个以上的文件需要更多的时间。

摘要

在本章中,我们介绍了一些场景和技术,它们可以帮助您识别集群的弱点。 您了解了如何检查 Hadoop 集群节点的运行状况以及如何识别大量 I/O 流量。 此外,我们还讨论了如何使用vmstatLinux 工具识别 CPU 争用。

然后,我们了解了一些公式,您需要使用这些公式来正确调整 Hadoop 集群的大小。 此外,在上一节中,您了解了如何使用新的专用公式正确配置映射器和减少器的数量。

在下一章中,您将了解更多关于分析映射和归约任务的信息,并将更深入地研究 Hadoop 映射和归约任务。

五、增强映射和归约任务

Hadoop 框架已经包含了几个计数器,比如读取和写入的字节数。这些计数器对了解框架活动和使用的资源非常有帮助。这些计数器由工作节点定期发送到主节点。

在本章中,对于 map 和 reduce,我们将学习如何增强每个阶段,查看哪些计数器,以及应用哪些技术来分析性能问题。然后,您将学习如何使用适当的值调整正确的配置参数。

在本章中,我们将涵盖以下主题:

  • 块大小和输入数据的影响
  • 如何处理小而不可分割的文件
  • 减少映射侧溢出记录
  • 改进减少阶段
  • 计算映射并降低任务吞吐量
  • 调整映射并减少参数

增强映射任务

当执行一个 MapReduce 作业时,Hadoop 框架将在一系列明确定义的处理阶段中执行该作业。除了用户定义的函数(映射、减少和组合器)之外,其他 MapReduce 阶段的执行时间在不同的 MapReduce 作业中是通用的。处理时间主要取决于流经每个阶段的数据量和底层 Hadoop 集群的性能。

为了提高 MapReduce 的性能,首先需要通过运行一组具有不同数据量的不同作业(每个映射/reduce 任务)来对这些不同的阶段进行基准测试。需要运行这些作业来收集测量值,例如每个阶段的持续时间和数据量,,然后分析这些测量值(对于每个阶段)以导出平台缩放函数。

要识别映射端瓶颈,您应该概述映射任务执行流程的五个阶段。下图表示映射任务的执行顺序:

Enhancing map tasks

让我们看看每个阶段的作用:

  • 读取阶段,映射任务通常会从 Hadoop 分布式文件系统 ( HDFS )读取固定大小的数据块(例如,64 MB)。但是,写入的数据文件是不同的,可能是任意大小的,例如 80 MB。在这种情况下,为了存储数据,将有两个块:一个 64 MB,另一个 16 MB。分析此阶段时,我们将测量 读取阶段的持续时间以及映射任务读取的数据量。
  • 要描述映射阶段,您需要测量整个映射功能的持续时间和已处理记录的数量,然后标准化每个记录的执行时间。在测量执行时间时,您应该检查任何倾斜的数据,这通常是大量小输入文件或大的不可分割文件的结果。您应该比较所有映射任务(同一作业)的输入大小,以检查是否有任何倾斜的数据。
  • 溢出阶段,框架对中间数据进行本地排序,并为不同的归约任务进行分区,如果可用,应用合并器,然后将中间数据写入本地磁盘。为了描述这个阶段,我们将测量完成所有这些所花费的时间。如果您正在使用组合器(在第 6 章Optimizing MapReduce Tasks中讨论),处理时间将包括它们的执行时间。
  • 提取阶段,我们将测量框架将映射阶段输出缓冲到内存中所花费的时间以及生成的中间数据量。在最后一个阶段,即 合并阶段,我们将测量框架为每个归约任务将不同的溢出文件合并成单个溢出文件所花费的时间。

输入数据和块大小影响

在读取阶段之前,数据应该位于文件系统上。您的数据方案也将影响您的 MapReduce 作业性能。为了高效地运行映射任务,数据必须是可拆分的,例如文本文件,这样 MapReduce 就可以将任务分成块并独立处理每个任务。分割文件应该大到足以填满一个块大小。块大小很重要,因为它决定了如何分割数据,并且每个输入分割都将分配给映射器。因此,如果您有一个非常大的数据集,但块大小很小,这可能会导致大量的映射器。这意味着映射绘制者会很快完成,但需要时间来分割和完成。因此,对于一个大的输入文件,这个值应该很高(例如,256 MB)。默认的 Hadoop 数据块大小为 64 MB(小数据集的好选择)、128 MB(中数据集)和 256 MB(大数据集)。更大的块大小可能会加快磁盘输入/输出,但会增加网络上的数据传输,还可能导致在映射阶段溢出记录。

映射任务有两种方式从存储系统读取数据,直接从磁盘(直接输入/输出)或通过流式传输数据(通过进程间通信方案(如 TCP/IP 或 JDBC)从存储系统流式传输输入/输出),这种方式更通用,可用于从本地节点和远程节点读取数据。

Hadoop 将数据分发到多个节点,以平衡集群的工作负载,并将任务分配给输入数据所在的计算节点。这就是为什么数据局部性很重要,可能会影响集群的性能。如果数据不在映射器将处理它的节点上,数据将通过网络传输,这将增加网络流量。如果映射函数从本地节点(数据-本地映射)读取数据,则直接输入/输出效率更高。如果映射函数不是数据本地映射,则流输入/输出是唯一的选择。

处理小而不可分割的文件

您的业务可能要求您处理大小、二进制或压缩文件,这些文件是 HDFS 无法分割的。二进制和压缩文件本质上不是基于块的。因此,由于数据局部性的丢失,这可能会影响 MapReduce 作业的性能。

Hadoop 不是为处理小文件而设计的,而 HDFS 是为存储和处理大数据集(万亿字节)而设计的。然而,在 HDFS 存储大量小文件效率很低。大量小文件的问题是处理这些文件会有大量的并行任务,过多的并行会消耗不必要的资源,影响你工作的运行时间。此外,在系统中存储大量小文件的情况下,它们的元数据占据了系统的很大一部分,这受到名称节点物理内存容量的限制。

如果分割文件的大小小于 HDFS 块大小(默认为 64 MB),则认为该文件很小;如果其大小超过块大小,则认为该文件很大。为了检查输入文件的大小,在http://machinename:50070/dfshealth.jsp浏览 Hadoop DFS 主页,然后点击浏览文件系统。下面的截图显示了一个包含小文件的 DFS 目录;每个文件的大小为 1 MB,块大小为 256 MB:

Dealing with small and unsplittable files

用 Hadoop 解决 HDFS 小文件问题最简单的方法是将它们打包成一个更大的文件。在这种情况下,您可以在 NameNode 内存中存储更少的文件,并改善数据交换,所有小文件都将存储在本地磁盘的单个文件中。为了在使用 Hadoop 时打包小文件,您可以选择以下任一选项:

  • 使用 Avro 创建容器文件来序列化数据。Avro 是一个 Apache 开源项目(http://avro.apache.org/),为 Hadoop 和数据交换服务提供数据序列化,可以一起使用,也可以独立使用。
  • 使用 Hadoop 档案(HAR 档案)。HAR 文件是特殊格式的档案,通过在 HDFS 之上构建一个分层文件系统来工作,以便 Hadoop 可以并行、透明、高效地访问原始文件,而无需扩展文件。要创建 HAR 文件档案,您可以使用hadoop archive命令并使用har://网址来访问文件。
  • 使用序列文件(sequence file)将小文件存储到一个更大的单个文件中。SequenceFile 的结构与键/值对相同,其中您将文件名定义为键,将其内容定义为值。使用序列文件的另一个优点是这种文件是可拆分的,并且允许块压缩。

要确定文件是否适合一个块,您需要计算每个映射任务的平均输入字节数,并将其与块大小进行比较。如果你发现平均值大于块大小,那么你需要调查为什么 Hadoop 没有分割你的文件。这可能是因为您的文件不符合TextInputFormatLzoTextInputFormat界面。

Hadoop 压缩编解码器(如 Deflate、gzip、lzo 和 Finder)不可拆分。

在映射阶段减少溢出记录

在映射阶段,映射函数可能会将大量数据写入本地文件系统。当映射任务运行时,它们生成中间数据输出,该输出存储在默认设置为 100 兆字节的内存缓冲区中(io.sort.mb)。这个缓冲区是一大块保留内存,是映射 JVM 堆空间的一部分。一旦达到占用阈值(io.sort.spill.percent),缓冲区的内容就会被刷新到本地磁盘,这就是我们所说的溢出。为了存储溢出记录的元数据(每条记录的长度为 16 字节),Hadoop 框架分配了由io.sort.mb(参数,io.sort.record.percent)分配的 0.05%的内存,因此 5 MB 被分配给元数据,95 MB 留给缓冲区使用。请看下图:

Reducing spilled records during the Map phase

上图所示的每个参数如下表所示:

|

参数

|

缺省值

|

优化建议

|
| --- | --- | --- |
| io.sort.mb | 100 | 此参数指示分配给排序和存储映射任务输出的内存量(以兆字节为单位)。要设置该值,最好不要超过数据节点上五分之一可用内存的 70%到 75%。 |
| io.sort.factor | 10 | 此排序因子参数指示一次合并的文件数。根据您的数据节点内存大小,该值应设置为io.sort.mb定义的内存量的十分之一。 |
| io.sort.record.percent | 0.05 | 此参数确定用于存储映射输出元数据的io.sort.mb的百分比。我们建议保留该参数的默认值。 |
| io.sort.spill.percent | 0.80 | 此参数确定映射输出缓冲区的百分比,之后缓冲区将溢出到磁盘。我们建议您保留此参数的默认值。要几乎使用全部缓冲容量,您可以将该值设置为0.99。 |
| tasktrakker.http.threads | 40 | 此参数决定了为映射器的输出服务的线程数。该值是按跟踪器设置的,不应由单个作业设置。在非常大的集群上,该值可能会增加。 |

当记录多次溢出到磁盘时,可能会出现性能问题和读取开销。分析这些 MapReduce 数据流步骤意味着检测您的映射任务是否正在执行附加溢出。为了确定是否有额外的溢出,您应该比较映射输出记录溢出记录 Hadoop 计数器,如果溢出记录的数量大于映射输出记录,则可以确定正在发生额外的溢出。以下屏幕截图报告了已溢出记录的 MapReduce 作业的 Hadoop 计数器:

Reducing spilled records during the Map phase

为了在这个阶段增强框架的性能并消除对磁盘的额外溢出,您需要为内存缓冲区分配一个准确的值,并将io.sort.spill.percent设置为0.99以使用几乎全部的缓冲区容量。

要确定缓冲区所需的内存空间,您应该计算缓冲区的总大小(记录+元数据)。要计算io.sort.record.percentio.sort.mb,需要计算以下值(假设我们使用上一张截图中报告的计数器):

  • 记录长度 ( RL ) =映射输出字节/映射输出记录,即 671,089,000 / 6,710,890 = 100 字节
  • 溢出记录大小 ( RS ) =溢出记录数*记录长度,即 1,342,178 * 100 = 134,217,800 字节= 128 MB
  • 元数据大小 ( MS ) =元数据长度*溢出记录数,即 1,342,178 * 16 = 21,474,848 字节= 20.48 MB

计算完 RL、RS 和 MS 的值后,现在可以计算缓冲区的总大小(记录+元数据),如下所示:

  • io.sort.record.percent =元数据长度/(元数据长度+记录长度),即 16 / (16 + 100) = 0.138
  • io.sort.mb =元数据大小+溢出记录大小,即 128 + 20.48 ≈ 149 MB

计算映射任务的吞吐量

在映射阶段,映射任务可能会因小文件而变慢,这意味着 Hadoop 正在花费大量时间来启动和停止任务。当处理大型不可分割文件时,Hadoop 会花费输入/输出时间从另一个节点读取数据。此外,糟糕的读/写磁盘操作也会影响 Hadoop 的 MapReduce 性能。

要确定您的映射任务是否运行缓慢且吞吐量较低,您需要根据单个映射任务写入(或读取)的文件大小以及处理作业所用的时间,使用 Hadoop 输入/输出计数器计算此吞吐量。然后,如果计算的吞吐量接近本地 I/O 吞吐量,您可以将其视为最佳吞吐量,否则,其他一些因素会影响映射任务的性能。

假设您有一个使用N映射任务的 MapReduce 作业;吞吐量以字节/秒(每秒字节数)计算,如下所示:

Throughput (N map tasks) = sum (each map input size in bytes) / sum (each map execution time in seconds)

以下屏幕截图显示了计算映射任务吞吐量所需的 Hadoop 计数器,您可以在任务统计历史记录中找到该计数器:

Calculating map tasks' throughput

报告的映射执行时间如下图所示:

Calculating map tasks' throughput

因此,通过使用此单样本映射任务的计数器值,吞吐量为,67,108,900 / 24 = 2,796,204.16 ≈ 2,66 MB/sec

建议根据平均映射任务吞吐量而不是一个映射的吞吐量来确定映射任务的性能吞吐量,除非您的作业只使用一个映射任务。您可以使用公式average throughput = sum (each map input size in bytes / time in seconds) / number of map tasks计算平均映射任务吞吐量。

计算并发平均吞吐量以确定集群能力也很有意思。假设您有一个集群,其容量为 100 个映射插槽,5.231 兆字节/秒的输入/输出吞吐量,4.863 兆字节/秒的平均输入/输出吞吐量,并且您想要处理 5000 个文件(每个文件 256 兆字节)。在这种情况下,由于集群容量,该作业将在 50 个 MapReduce 波(5000 / 100 = 50)中处理。要计算并发吞吐量,请将吞吐量乘以文件数量(本例中为 5000)和集群中可用映射插槽数量(本例中为 100)的最小值。

在我们的示例中,您可以将并发吞吐量计算为5.231 * 100 = 523.1 MB/s。平均并发吞吐量可计算为4.863 * 100 = 486.3 MB/s

计算平均并发吞吐量将帮助您估计使用 Hadoop 集群处理输入文件所需的时间。因此,在我们的示例中,将在(5000 files * 256 MB each) / 486.3 MB/s average throughput = 2632.12 sec ≈ 44 min中处理 5000 个文件。

增强归约任务

简化任务处理由一系列三个阶段组成。只有用户定义的 reduce 函数的执行是自定义的,其持续时间取决于流经每个阶段的数据量和底层 Hadoop 集群的性能。对每个阶段进行分析将有助于您识别潜在的瓶颈和低数据处理速度。下图显示了归约任务的三个主要阶段:

Enhancing reduce tasks

让我们详细看看每个阶段:

  • 分析洗牌阶段意味着您需要测量将中间数据从映射任务转移到精简任务,然后将它们合并和排序在一起所花费的时间。在混洗阶段,获取由映射阶段生成的中间数据。此阶段的处理时间在很大程度上取决于 Hadoop 配置参数和用于归约任务的中间数据量。
  • 归约阶段,为每个归约任务分配一个具有固定关键范围的映射输出中间数据的分区;所以 reduce 任务必须从集群中每个 map 任务的输出中获取这个分区的内容。执行时间是在输入键上应用用户提供的 reduce 函数以及与之对应的所有值所花费的时间。要分析缩减阶段,请测量执行时间,这也取决于输入数据的大小。
  • 分析 阶段,也就是最后一个阶段,意味着测量 Hadoop 将缩减输出写到 HDFS 需要多长时间。

计算归约任务的吞吐量

在缩减方面,速度的降低可能是由坏的或未优化的缩减功能用户代码、硬件问题或 Hadoop 框架的错误配置引起的。要确定归约任务的吞吐量,您可以使用 Hadoop 输入/输出计数器计算该吞吐量(就像您计算映射任务的吞吐量一样)。

对于使用N归约任务的给定 MapReduce 作业,吞吐量使用公式throughput (N reduce tasks) = sum (each reduce input shuffle in bytes) / sum (each reduce execution time in seconds)以字节/秒(字节/秒)计算。

下面的截图显示了计算归约任务吞吐量所需的 Hadoop 计数器:

Calculating reduce tasks' throughput

下面的截图显示了reduce函数的执行时间:

Calculating reduce tasks' throughput

Reduce 阶段的总执行时间是三个步骤的执行时间的总和:Shuffle 执行时间、Sort 执行时间和 Reduce 函数执行时间。每个步骤的执行时间由 Hadoop 日志报告,如上一张截图所示。

计算洗牌和排序步骤的吞吐量可能也很有趣。您可以通过将随机播放字节除以随机播放执行时间(以秒为单位)来计算随机播放吞吐量。此外,要计算排序步骤吞吐量,您需要用无序字节除以排序执行时间(以秒为单位)。Shuffle throughput计算为Shuffle bytes / Shuffle timeSort throughput计算为Shuffle bytes / Sort time

如您所见,如果随机播放或排序时间等于零,这些公式可能会导致除以零的错误。无序播放和排序吞吐量与无序播放和排序时间成反比。这意味着,混洗或排序时间越接近零,混洗或排序吞吐量就越大,这意味着吞吐量在最低混洗或排序时间值时最大。

改善减少执行阶段

一旦映射任务有了处理的数据,在不同时间混洗的映射输出数据需要被合并到一个单一的缩减输入文件中,并通过一个关键字进行排序,然后归约任务才能使用它。映射输出的大小取决于其输入数据集的大小。增强无序播放和排序阶段性能的最简单方法是减少要排序和合并的数据量。这通常通过使用组合器、数据压缩和/或数据过滤来完成。(使用组合器和实现压缩在第 6 章Optimizing MapReduce Tasks.中讨论)

本地磁盘问题和网络问题是洗牌和排序阶段性能问题的常见来源,因为 MapReduce 框架读取溢出的本地输入,并将它们馈送给 Reduce 代码。

在缩减阶段,由于映射和归约任务之间的数据传输,通常会观察到大量的网络流量。Hadoop 有几个配置参数,您可以对其进行调整,以便在此阶段提高其性能。下表显示了为增强减少阶段而调整的常用参数:

|

参数

|

缺省值

|

优化建议

|
| --- | --- | --- |
| mapred.reduce.parallel.copies | 5 | 此参数控制减速器任务中的线程数。 |
| io.sort.factor | 10 | 此排序因子参数指示一次合并的文件数。根据您的数据节点内存大小,该值应设置为io.sort.mb定义的内存量的十分之一。 |
| mapred.job.reduce.input.buffer.percent | 0.0 | 此参数确定在缩减阶段保留映射输出的内存百分比(相对于最大内存堆大小)。 |
| mapred.job.shuffle.input.buffer.percent | 0.7 | 此参数确定在混洗阶段从最大内存堆大小分配到存储映射输出的内存百分比。 |

通过将mapred.job.reduce.input.buffer.percent值设置为0.8,缓冲器将被使用高达 80%,这将保持减速器输入数据在内存中。因为默认值 0.0 意味着映射输出被合并到本地磁盘而不是内存中。

还建议在 Shuffle 阶段最大化分配给存储映射输出的内存。因此,您可以将mapred.job.shuffle.input.buffer.percent值增加到0.9,以使用 90%的内存堆大小,而不是默认大小的 70%。

调整映射并减少参数

为一项工作选择合适数量的任务会对 Hadoop 的性能产生巨大影响。在第 4 章Identifying Resource Weaknesses中,您学习了如何正确配置映射器和减速器的数量。但是,正确调整映射器和缩减器的数量不足以获得映射缩减作业的最大性能。当集群中的每台机器在执行作业的任何给定时间都有事情要做时,就会出现最佳状态。请记住,Hadoop 框架有超过 180 个参数,大多数都不应该保持默认设置。

在这一节中,我们将介绍其他技术来计算映射绘制者和缩小者的数量。尝试多种优化方法可能更有成效,因为我们的目标是为给定的作业找到一个特定的配置,该配置使用集群上的所有可用资源。这一变化的结果是使用户能够并行运行尽可能多的映射器和缩减器,以充分利用可用资源。

映射器的理论上限可以通过将输入文件大小除以块大小来计算。另一方面,您的输入文件由具有固定数量 CPU 内核的机器处理(#mappers = number of physical cores - reserved core * (0.95 to 1.75))。这个意味着在一个由三个节点组成的集群上处理一个 10 GB 的文件,每个节点有八个中央处理器内核和 256 兆字节的块大小,映射器的最佳数量在 37 到 40 之间。mapper 的上限为 10 GB / 0.25 GB = 40。映射器的最佳数量可以计算如下:

  • #mappers based on CPU core number = (8 cores - 1 reserved cores) * 1.75 = 12.25
  • #cluster mappers capacity = 12.25 * 3 = 37.5

您可以使用不同的技术来准确确定映射器和缩减器的数量。所有这些技术都基于计算,并根据现实世界的经验进行调整或调整。您不应该只关注一种技术,而是应该在您自己的环境中尝试每一种技术,以找到让您的集群以最佳方式运行的技术。

第三章Detecting System Bottlenecks中,我们建议将减速器的数量设置在集群容量的 50%到 99%之间,这样所有减速器都可以一波完成。该技术建议您使用以下公式计算减速器数量:(0.5 to 0.95) * nodes number * reducers slots number

第 4 章Identifying Resource Weaknesses中,我们建议将减速器的槽数设置为与映射器的槽数相同,或者至少设置为映射器的三分之二。经验表明,这种计算技术是在小型集群或开发环境中设置减速器数量的一种简单快捷的方法。

要确定缩减器数量的下限,您应该将每个节点的 CPU 内核数量除以 2((cores per node) / 2)。要确定上限,需要将 CPU 内核数乘以 2(2 * (cores per node))。您也可以使用三分之二映射器技术来指定此范围内的减速器数量。

在本节中,我们将提出两个公式,根据集群节点和 CPU 内核的数量来确定映射器和缩减器的数量。您可以使用这些公式作为计算这些数字的起点,然后在您的环境中对它们进行微调,以获得最佳值,如下所示:

  • 使用群集节点的数量,使用公式number of reducers = (0.95 to 1.75) * (number of nodes * mapred.reduce.parallel.copies)计算映射器和缩减器的数量。这里,0.95 到 1.75 是 CPU 超线程因子,mapred.reduce.parallel.copies是决定可以并行运行的最大减压器数量的配置参数。
  • 使用公式number of reducers = (0.95 to 1.75) * (number of CPU cores - 1).使用 CPU 内核数计算映射器和缩减器的数量

让我们回到我们的集群示例(三个节点,每个节点有一个 CPU *四个内核、4 GB RAM 和 40 GB HDD 空间,这是我们在第三章Detecting System Bottlenecks中用来创建基线的)。下表总结了不同的计算技术:

|

计算技术

|

要使用的公式

|

mappers

|

减速器

|
| --- | --- | --- | --- |
| 将减压器的数量设置在集群的 50%到 99%之间 | #mappers = CPU cores - 1 * 1.75``#reducers = (0.5 to 0.95) * nodes number * reducers' slots number | 4 - 1 * 1.75 = 5 | 0.95 * 3 * 5 = 14.25 ≈ 14 |
| 确定减速器的上限和下限 | #mappers = CPU cores - 1 * 1.75``#reducers LB = (cores per node) / 2``#reducers UB = 2 * (cores per node)``If you apply the 2/3 mappers technique: =5*2/3 = 3.33 ≈ 3 (which is in the range) | 5 | LB = 4/2 = 2``UB = 2*4= 8 |
| 基于节点号 | #mappers = CPU cores - 1 * 1.75``#reducers = 1.75 * (number of nodes * mapred.reduce.parallel.copies) | 5 | 1.75 * (3 * 5) = 26.25 ≈ 25 |
| 基于中央处理器内核 | #mappers = CPU cores - 1 * 1.75``#reducers = (0.95 to 1.75) * (number of CPU cores -1) | 5 | 1.75 * 3 = 5.25 ≈ 5 |

我们将第三种和第四种技术的计算结果应用于测试集群环境,并在下表中的调谐 2 和调谐 3 列中报告了结果(基线和调谐 1 列来自第 3 章Detecting System Bottlenecks)。与 Tuned 1 列相比,我们将使用更大的块大小(256 MB,在上一次迭代中为 128 MB),并使用mapred.child.java.opts参数分配更多内存(-Xmx550m):

|

Hadoop 参数

|

基线

|

调谐 1

|

调谐 2

|

调谐 2

|
| --- | --- | --- | --- | --- |
| dfs.replication | 3 | 2 | 2 | 2 |
| dfs.block.size | 6,7108,864 | 134,217,728 | 268,435,456 | 268,435,456 |
| dfs.namenode.handler.count | 10 | 20 | 20 | 20 |
| dfs.datanode.handler.count | 3 | 5 | 5 | 5 |
| io.sort.factor | 10 | 35 | 35 | 35 |
| io.sort.mb | 100 | 350 | 350 | 350 |
| mapred.tasktracker.map.tasks.maximum | 2 | 5 | 5 | 5 |
| mapred.map.tasks | 2 | 2 | 2 | 2 |
| mapred.reduce.tasks | 1 | 8 | 26 | 5 |
| mapred.tasktracker.reduce.tasks.maximum | 2 | 5 | 5 | 5 |
| mapred.reduce.parallel.copies | 5 | 5 | 5 | 5 |
| mapred.job.reduce.input.buffer.percent | 0 | 0 | 0 | 0 |
| mapred.child.java.opts | -Xmx200m | -Xmx500m | -Xmx550m | -Xmx550m |
| Input data size | 10 GB | 10 GB | 10 GB | 10 GB |
| Cluster's nodes number | 3 | 3 | 3 | 3 |
| Job execution time (sec) | 243 | 185 | 169 | 190 |
| Improvement over Baseline (%) |   | 23.86% | 30.45% | 21.81% |

这些工作都是在测试环境中使用自动生成的数据完成的。在集群上尝试这些技术时,您可能会得到不同的结果。

总结

在本章中,我们学习了映射端和简化端任务的增强,并介绍了一些可能有助于提高映射简化作业性能的技术。我们了解了块大小的影响有多重要,以及如何识别由于小且不可分割的文件而导致的映射端性能下降。此外,我们还了解了溢出文件以及如何通过分配准确的内存缓冲区来消除它们。

然后,我们继续学习如何在缩减阶段的洗牌和合并步骤中识别低性能作业。在最后一节中,我们介绍了不同的技术来计算 mappers 和 Reduce 的数量,以调整您的 MapReduce 配置并提高其性能。

在下一章中,我们将了解更多关于 MapReduce 任务的优化,并了解组合器和中间数据压缩将如何提高 MapReduce 作业的性能。继续读!

六、优化映射归约任务

大多数 MapReduce 程序是为数据分析而编写的,通常需要花费大量时间来完成。许多公司正在采用 Hadoop 对需要完成时间保证的大型数据集进行高级数据分析。效率,尤其是 MapReduce 的输入/输出成本,仍然需要解决才能成功。

在这一章中,我们将讨论一些优化技术,例如使用压缩和使用组合器来提高作业执行。在本章中,您还将学习优化映射器和缩减器代码的基本准则和规则,以及使用和重用对象实例的技术。

本章将涵盖以下主题:

  • 使用组合器的好处
  • 使用压缩的重要性
  • 学习使用合适的可写类型
  • 如何巧妙重用类型
  • 如何优化映射器和减速器的代码

使用组合器

您可以使用 组合器来提高整体 MapReduce 性能。组合器相当于局部减少操作,可以有效提高后续全局减少操作的速率。基本上,它用于初步优化和最小化映射器和缩减器之间通过网络传输的键/值对的数量。组合器将使用映射操作处理键/值对输出的中间结果,并且它不影响在mapreduce函数中编码的转换逻辑。

使用组合器的标准惯例只是将您的减速器功能重新调整为您的组合器。计算逻辑应该是 交换式 (加法等运算的处理顺序对最终结果没有影响)和 联想式(我们应用加法运算的顺序对最终结果没有影响)。

要获取有关交换和关联属性的更多信息,您可以浏览以下链接:

http://en.wikipedia.org/wiki/Commutative_propertyT2】

http://en.wikipedia.org/wiki/Associative_propertyT2】

实现一个组合器意味着实现一个组合器类。一旦实现并添加了定制类,Map 函数就不会立即写入输出以产生键/值对的中间结果。相反,它们将被收集到列表中,每个键对应一个值列表。组合器类将以键/值对的形式输出键和相应的值列表。当组合器缓冲区达到一定数量的键/值对时,缓冲区中的数据将被清除并传输到减少功能。

类型

为作业调用您的Combiner定制类类似于如何设置映射和缩减类:

job.setCombinerClass(MyCombine.class);

下面的截图显示了使用 Combiners 时应该关注的 Hadoop 计数器:

Using Combiners

在这个截图中,你可以观察组合输入记录组合输出记录的记录数,0 ,因为作业没有实现任何组合器类。因此减少输入记录的数量与映射输出记录相同。下面的截图显示了组合器在实现后的效果:

Using Combiners

在这个截图中,注意合并输入记录合并输出记录的记录数,减少输入记录映射输出记录的记录数。您将观察到实现合并器减少了传输到减少功能的数据量。

与上图相比,由于使用了组合器功能,该作业的组合记录数量有所增加。减少输入数据量已降至 685,760 ,而最初为 10,485,760 。在非常大的数据环境中,使用 Combiners 是提高 MapReduce 作业整体性能的有效方法。

下面的代码片段说明了从 MapReduce 作业中提取的自定义组合器类:

Using Combiners

可以看到,Combine类实现了Reducer接口,像一个 reducer 函数,会用映射输出的多个值调用。这个类用自己的代码重写reduce()方法。第 12 行和第 23 行之间的示例代码遍历由mapper函数传输的值列表,如果当前键与前一个键不同,它调用 collect()方法输出结果。

使用压缩

压缩减少了从底层存储系统(HDFS)读取或写入的字节数。压缩提高了网络带宽和磁盘空间的效率。在 Hadoop 中使用数据压缩非常重要,尤其是在非常大的数据环境和密集工作负载下。在这种情况下,输入/输出操作和网络数据传输需要相当长的时间才能完成。而且洗牌合并过程也会面临巨大的 I/O 压力。

由于磁盘 I/O 和网络带宽是 Hadoop 中宝贵的资源,数据压缩有助于节省这些资源,将 I/O 磁盘和网络传输降到最低。实现更高的性能和节省这些资源并不是免费的,尽管它是在压缩和解压缩操作时以低 CPU 成本完成的。

每当输入/输出磁盘或网络流量影响您的 MapReduce 作业性能时,您可以通过在任何 MapReduce 阶段启用压缩来改善端到端处理时间并减少输入/输出和网络流量。

压缩映射输出将始终有助于减少映射之间的网络流量并归约任务。

压缩可以在 MapReduce 作业的任何阶段启用,如下图所示:

Using compression

  • Compress Input: This should be considered in a very large data context that you plan to process repeatedly. Therefore, you do not need to explicitly specify a codec to use. Hadoop will automatically check for the extension of your files and if it detects an appropriate extension, it will use the appropriate codec to compress and decompress your files. Otherwise, no compression codec will be used by Hadoop.

    使用复制时,压缩输入文件可以节省存储空间并加快数据传输。要压缩输入数据,您应该使用可拆分的算法,如bzip2,或者使用SequenceFile格式的zlib

  • Compress Mapper output: Compression should be considered at this stage if your map tasks output a large amount of intermediate data. This will significantly improve the internal shuffle process, which is the most resource-consuming Hadoop process. You should always consider using compression if you observe slow network transfers due to large data volumes. To compress Mapper outputs, use faster codecs such as LZO, LZ4, or Snappy.

    Limpel-Zif-Oberhumer(LZO)是 Hadoop 中常用的压缩编解码器,用于压缩数据。它的设计是为了跟上硬盘的读取速度,因此将速度视为优先事项,而不是压缩率。与 gzip 编解码器相比,它的压缩速度快了大约五倍,解压缩速度快了两倍。使用 LZO 压缩的文件比使用 gzip 压缩的相同文件大 50%,但仍然比原始文件小 25-50%,这有利于提高性能,并且映射阶段完成的速度大约快四倍。

  • Compress Reducer output: Enabling compression at this stage will reduce the amount of data to be stored and therefore the required disk space. This is also useful if you chain MapReduce jobs together while the input files of the second job are already compressed.

    压缩 reduce 输出应考虑用于存储和/或归档、更好的写入速度或 MapReduce 作业。要压缩 Reducer 输出,请使用标准实用程序,如用于数据交换的gzipbzip2,以及用于链接作业的更快编解码器。

下面的截图显示了在映射输出文件上启用压缩之前,MapReduce 作业写入和读取的字节数:

Using compression

为了启用映射输出文件压缩,您可以如下更改这些配置参数(在mapred-site.xml文件中):

Using compression

默认情况下,mapred.compress.map.output值设置为falsemapred.output.compression.type值设置为RECORD。将该值更改为BLOCK可提高压缩比。

对映射输出文件启用压缩后,与上图( 858 )相比,reduce 函数( 268 )读取的字节数大幅减少,如下图所示:

Using compression

为了在 Hadoop 中启用压缩,您可以设置如下表所示的配置参数:

|

参数

|

缺省值

|

阶段

|

推荐

|
| --- | --- | --- | --- |
| io.compression.codec | DefaultCodec | 输入压缩 | Hadoop 使用文件扩展名来确定是否支持压缩编解码器 |
| mapreduce.map.output.compress | false | 映射器输出 | 将该参数设置为true以启用压缩 |
| mapreduce.map.output.compress.codec | DefaultCodec | 映射器输出 | 在此阶段使用LZOLZ4Snappy编解码器压缩数据 |
| mapreduce.output.fileoutputformat.compress | false | 减速器输出 | 将该参数设置为true以启用压缩 |
| mapreduce.output.fileoutputformat.compress.codec | DefaultCodec | 减速器输出 | 使用标准工具/编解码器,如gzipbzip2 |
| mapreduce.output.fileoutputformat.compress.type | RECORD | 减速器输出 | 用于顺序文件输出的压缩类型:NONEBLOCK |

使用适当的可写类型

Hadoop 使用自定义数据类型序列化/RPC 机制,并定义了自己的 box 类型类。这些类用于操作字符串(Text)、整数(IntWritable)等等,它们实现了定义反序列化协议的Writable类。

因此,Hadoop 中的所有values都是Writable类型对象,所有keys都是WritableComparable的实例,定义了排序顺序,因为它们需要比较。

可写对象是可变的,并且更加紧凑,因为不需要存储元信息(类名、字段、超类等),直接的随机访问提供了更高的性能。由于二进制Writable类型将占用更少的空间,这将减少映射或组合函数写入的中间数据的大小。减少中间数据可以通过减少网络传输和输入/输出磁盘所需的存储空间来大幅提高性能。

在代码中使用适当的可写类型将有助于提高 MapReduce 作业的整体性能。这主要是通过使用Text类型而不是String类型来消除串分裂的时间来完成的。此外,使用VIntWritableVLongWritable有时比使用常规的intlong原始 Java 数据类型更快。

在 Shuffle 和排序阶段,比较中间键可能是一个瓶颈,Hadoop 可能会在这个阶段花费时间。实现新的比较机制可以提高您的 MapReduce 性能。有两种方法可以比较您的密钥:

  • 通过实现org.apache.hadoop.io.WritableComparable接口
  • 通过实现RawComparator接口

根据我们的经验,使用RawComparator实现原始字节比较提高了 MapReduce 作业的整体性能,并且比WritableComparable有优势。

WritableComparable类的典型实现看起来像下面的代码片段:

Using appropriate Writable types

要实现RawComparator,还可以扩展WritableComparator类,实现RawComparator。以下代码片段说明了 WritableComparator类的典型实现:

Using appropriate Writable types

扩展 WritableComparator类允许您使用该类的继承方法来操作您的中间 MapReduce 键。

WritableComparator继承的 readInt()方法将 4 个连续的字节转换成一个原始的 Java int(即 4 个字节)。

类型

要连接您的RawComparator自定义类实现,请按如下方式设置其排序比较器类:

job.setSortComparatorClass(MyClassComparator.class);

根据您想要处理的数据,您可能需要定义如何将您的数据文件读入 Mappers 实例。Hadoop 允许您通过实现InputFormat接口来定义自己的数据格式,并附带了它的几种实现。

InputFormat类是 Hadoop 框架的基本类之一,它负责定义两个主要的东西:InputSplitRecordReader

一个InputFormat类描述了如何向映射器呈现数据以及数据的来源。InputSplit界面定义了单个映射任务的大小及其执行服务器。RecordReader界面负责从输入文件中读取记录,并将它们(作为键/值对)提交给映射器。InputFormat类的另一项重要工作是将输入文件源分割成片段,由FileInputSplit实例表示。这些片段被用作各个映射器的输入。为了提高作业的性能,此过程必须足够快且便宜(它应该使用最少的 CPU、I/O 存储和网络资源)。

类型

创建自己的InputFormat类时,最好子类化FileInputFormat类而不是直接实现InputFormat

智能重用类型

通常,Hadoop 问题是由某种形式的内存管理不善引起的,节点不会突然出现故障,而是会随着输入/输出设备的损坏而变慢。Hadoop 有许多选项可以在几个粒度级别上控制内存分配和使用,但它不检查这些选项。因此,一台计算机上所有守护程序的组合堆大小都有可能超过物理内存量。

每个 Java 进程本身都有一个配置的最大堆大小。根据 JVM 堆大小、操作系统限制或物理内存是否首先耗尽,这将分别导致内存不足错误、JVM 中止或严重交换。

你应该注意内存管理。应该删除所有不必要的已分配内存资源,以最大化 MapReduce 作业的内存空间。

重用类型是一种技术,用于最小化资源使用,如中央处理器和内存空间。当您处理数百万条数据记录时,重用现有实例总是比创建新实例更便宜。MapReduce 作业中最简单的可重用性是使用现有的 Hadoop 可写类型,这在大多数情况下都是可能的。

初学者在编写mapreduce函数时最常见的错误之一是为每个输出分配一个新的对象,这通常是在forforeach循环中完成的,这可能会创建数千或数百万个新的Writable实例。这些实例具有非常短的 TTL(生存时间),并强制 Java 的垃圾收集器执行大量工作来处理它们所需的所有已分配内存。

下面的代码片段显示了一个映射器函数,它为每个输出分配一个新的Writable实例(您应该避免这样编码)。

Reusing types smartly

为了检测您是否在为不必要的对象分配资源上花费了时间,您应该检查任务日志并分析垃圾收集器活动。如果它代表了大量的时间和频繁,这意味着您应该检查您的代码,并通过消除不必要的创建新对象来增强它。

这是因为如果内存不足,那么只要超过某个内存阈值,并且垃圾收集器必须更频繁地运行,创建新对象就会存储在堆内存中。

重用可写变量理论上更好,但基于阿姆达尔定律(http://en.wikipedia.org/wiki/Amdahl%27s_law),这种改进可能并不明显。

日志通常用于诊断问题。通常,表示问题的那一行被埋在成千上万行中。挑出相关的线是一项漫长而又挑剔的任务。要更详细地检查 MapReduce 任务日志,应该在mapred-site.xml配置文件中设置 JVM 内存参数,如下表所示:

|

参数

|

缺省值

|

推荐

|
| --- | --- | --- |
| mapred.child.java.opts | -Xmx200m | 添加:-verbose:gc -XX:+PrintGCDetails |

下面的代码片段展示了一种更好的方法来发现重用Writable变量的好处:

Reusing types smartly

JVM 重用是一种针对多个任务重用 JVM 的优化技术。如果启用,一个 JVM 可以顺序执行多个任务。您可以通过更改mapred-site.xml配置文件中的适当参数来启用 JVM 重用,如下表所示:

|

参数

|

缺省值

|

推荐

|
| --- | --- | --- |
| mapred.job.reuse.jvm.num.tasks | 1 | 更改此变量以运行所需数量的任务(例如,2运行两个任务)。如果将该变量设置为-1,则 JVM 可以执行的任务数量不受限制。 |

优化映射器和缩减器代码

在细节中优化 MapReduce 代码端性能超出了本书的范围。在本节中,我们将提供一个基本指南,其中包含一些有助于提高 MapReduce 作业性能的规则。

Hadoop 的一个重要特性是所有数据都在一个被称为记录的单元中处理。虽然记录的大小几乎相同,但理论上,处理这些记录的时间应该是相同的。然而,在实践中,任务内记录的处理时间变化很大,当从存储器中读取记录、处理记录或将记录写入存储器时,可能会出现缓慢。此外,在实践中,还有两个因素可能会影响映射器或缩减器的性能:输入/输出访问时间和溢出,以及大量输入/输出请求导致的开销等待时间。

效率是可测量的,由产出与投入的比率定量确定。

MapReduce 提供了易用性,而程序员只使用 Map 和 Reduce 函数定义他的工作,而不必指定他的工作在节点间的物理分布。因此,Hadoop 提供了一个固定的数据流来执行 MapReduce 作业。这就是为什么许多复杂的算法很难仅在 MapReduce 作业中使用映射器和缩减器来实现。此外,由于 MapReduce 的数据流最初被设计为读取单个输入并生成单个输出,因此一些需要多个输入的算法没有得到很好的支持。

优化一个 MapReduce 作业意味着:

  • 在更短的时间内获得相同的输出
  • 用更少的资源在相同的时间获得相同的产出
  • 用相同的资源在相同的时间获得更多的产出

在任何编程语言中,分解代码都是优化的第一步。如果您运行多个作业来处理相同的输入数据,则可能有机会将它们重写为更少的作业。此外,在编写映射器或减速器功能代码时,您应该选择最高效的底层算法,这将有助于加快您的工作速度,否则您可能需要处理缓慢和/o r 不良性能。因此,代码中的低效率会降低您的 MapReduce 作业速度。

类型

使用以下命令检查作业跟踪器的当前日志记录级别:

hadoop daemonlog -getlevel {HadoopMachineName}:50030 org.apache.hadoop.mapred.JobTracker

为作业跟踪器设置 Hadoop 日志调试级别,如下所示:

hadoop daemonlog -setlevel {HadoopMachineName}:50030 org.apache.hadoop.mapred.JobTracker DEBUG

第二步是确定执行 MapReduce 作业时是否有问题。当所有映射器/缩减器看到此作业的失败任务数为零时,它们应该成功终止。如果这个数字非零,基本上你的程序有问题。如果数量少(两个或三个),节点可能不稳定。

有时候不仅仅是你的工作导致了问题,还有其他原因可能导致了问题。

有些算法在处理过程中需要全局状态信息,而 MapReduce 在执行过程中不处理状态信息。MapReduce 迭代读取数据,并在每次迭代中将中间结果物化在本地磁盘上,这需要大量的 I/O 操作。如果需要实现这样的算法,应该考虑使用第三方工具,比如 halopp(http://code.google.com/p/haloop/)或者 Twister(http://www.iterativemapreduce.org/)。

您可以通过减少 Hadoop 分发繁重应用所需的分发时间和网络带宽来增强您的 MapReduce 作业。请记住,MapReduce 框架假设了一个计算密集型应用,因为计算任务需要传输到集群中计划并行运行的每个节点。跨集群节点传输具有非常大的代码占用空间的应用将花费大量时间,这将完全耗尽并行执行多个实例所带来的吞吐量优势。

尝试在所有节点上预安装计算任务;它将完全避免任务分配的需要和相关的时间损失。

请记住,MapReduce 旨在处理非常大的数据量作为输入记录。如果由于某种原因,您的映射器无法读取输入记录,那么每次失败时终止任务将会适得其反,因为最终结果将保持不变,映射器任务仍然会失败。

为了防止这种情况,您应该在阅读器中处理输入错误并报告该错误(有时您需要创建一个自定义OutputFormat类),以便它可以被管理员跟踪或在调试会话时跟踪。

类型

如果您的应用允许记录跳过,Hadoop 将为您提供一个通过SkipBadRecords类跳过记录的功能:

setMapperMaxSkipRecords(Configuration conf,long maxSkipRecs)
setReducerMaxSkipGroups(Configuration conf,long maxSkipGrps)

总结

在本章中,我们了解了 MapReduce Combiners,以及它们如何帮助改善整体执行作业时间。此外,我们还介绍了为什么使用压缩很重要,尤其是在大数据量环境中。

然后,我们介绍了 Java 代码端优化,并学习了如何选择合适的可写类型以及如何智能地重用这些类型。我们还学习了WritableComparatorRawComparator自定义类的实现。

在最后一节中,我们介绍了基本的指导原则以及一些调整 Hadoop 配置和提高其性能的规则。

在下一章中,我们将了解更多关于 MapReduce 优化的最佳实践。继续读!

七、最佳实践和建议

好了,这是压轴大戏!到目前为止,我们已经学习了如何优化 MapReduce 作业性能,并将本书的主要部分用于奠定一些重要的基础。请记住,设置 Hadoop 集群基本上是一项挑战,需要将高可用性、负载平衡的要求与您希望从集群服务器获得的服务的个别要求结合起来。

在本章中,我们将描述您可以用来优化您的 Hadoop MapReduce 作业的硬件和应用配置清单。

本章将涵盖以下主题:

  • 常见的 Hadoop 集群清单
  • 基本输入输出系统清单和操作系统建议
  • Hadoop 最佳实践和建议
  • 应用中使用的 MapReduce 模板类

硬件调整和操作系统建议

系统调优的建议取决于系统的内在能力。以下各节建议了不同的推荐技术和技巧,您可以在参与 MapReduce 优化过程时将其用作提醒基准。

Hadoop 集群清单

以下清单仅描述了让您的 Hadoop 集群最佳工作所需的最少步骤:

  • 检查并确保所有群集节点都可以相互通信,并且您对每个群集节点都具有物理和/或远程管理访问权限
  • 检查您的集群是否具有良好的规模,并且能够补偿每个服务(至少)一个节点的故障
  • 检查集群环境的限制(硬件可用性资源/机架空间、主机参数等)
  • 为故障转移定义集群策略,以确保服务的高可用性
  • 定义需要备份的内容、需要保存的内容和位置,以便最大限度地提高您的 Hadoop 存储容量

Bios 调整清单

本主题列出了在最佳环境中安装 Hadoop 集群节点时应该检查的内容。将进行以下检查:

  • 检查硬件上的所有 CPU 核心是否被充分利用;否则,可以降级 CPU 频率。
  • 启用 原生命令排队模式 ( NCQ ),通过优化驱动器磁头的移动,帮助提高现代硬盘的 I/O 性能。通常可以通过 BIOS 中的高级主机控制器接口 ( AHCI ) 选项启用 NCQ 模式。

检查是否有任何默认的基本输入输出系统设置可能会对您的 Hadoop MapReduce 作业产生负面影响。

操作系统配置建议

在这个最小清单中,我们给出了一些系统调优的建议,它们是中央处理器、输入/输出和内存技术的结合。以下是建议:

  • 选择支持 EXT4 文件系统的 Linux 发行版。

  • By default, every file's read operation triggers a disk write operation in order to maintain the time the file was last accessed. This extra disk activity associated with updating the access time is not desired. You can disable this logging of access time for both files and directories using noatime on the filesystem.

    nodiratime : 这将禁用打开目录时访问时间的更新,以便在枚举目录时不修改访问时间。

  • 避免使用用于管理磁盘驱动器和类似大容量存储设备的逻辑卷管理(【LVM】),因为这会影响磁盘的输入/输出性能。

  • 将 Linux 内核的交换内存设置为低值。这通知 Linux 内核应该尽可能避免交换。

  • Linux 内核的输入/输出调度控制着输入/输出操作将如何提交给存储。试用 I/O 调度器的完全公平排队 ( CFQ ) ,类似于循环算法,I/O 操作实现为循环队列,每个 I/O 操作允许固定的执行时间。

  • 增加 Linux 操作系统最大打开文件描述符,这可能会提高 MapReduce 作业的性能。

Hadoop 最佳实践和建议

为了提高 Hadoop 性能,以下是一些配置提示和建议,代表了运行在 Hadoop 框架上的应用的最佳实践概要。

部署 Hadoop

Hadoop 可以通过从官网下载其存档文件并复制到集群中手动安装。这是可行的,但是如果您希望在四个以上的节点集群上安装 Hadoop,则不建议这样做。在大型集群上手动安装 Hadoop 可能会导致维护和故障排除问题。任何配置更改都需要使用安全复制协议 ( SCP ) 或安全外壳 ( SSH )手动应用到所有节点。

要在大型集群上部署 Hadoop,建议使用配置管理系统和/或自动化部署工具,如 Cloudera(http://www.cloudera.com)、Hortonworks(http://hortonworks.com)和 MapR(http://www.mapr.com)管理系统。对于额外的工作,例如应用部署,使用 Yum 和 Puppet 是很好的。

您可以使用这些工具为以下内容构建和维护 Hadoop 集群:

  • 设置
  • 配置
  • 可量测性
  • 监视
  • 维护
  • 解决纷争

Puppet 是一个强大的开源工具,它可以帮助您基于集中的规范执行管理任务,如添加用户、安装软件包和更新服务器配置。您可以通过浏览以下链接了解更多关于木偶的信息:http://puppetlabs.com/puppet/what-is-puppet

Hadoop 调优建议

本节中给出的清单和建议将有助于准备和遵循 MapReduce 性能建议。

以下是记忆建议清单:

  • 调整内存设置以避免因内存不足而导致作业挂起
  • 设置或定义 JVM 重用策略
  • 验证 JVM 代码缓存,并在必要时增加它
  • 分析垃圾收集器 ( GC ) 周期(使用详细日志),观察其是否有密集周期(这意味着内存中创建了大量对象实例),并检查 Hadoop 框架堆使用情况

以下是大量的输入/输出调优建议,以确保输入/输出操作不会出现挫折:

  • 在大量输入数据的情况下,压缩源数据以避免/减少大量输入/输出调整

  • Reduce spilled records from map tasks when you experiment with large spilled records

    通过调谐减少溢出的记录:io.sort.mbio.sort.record.percentio.sort.spill.percent

  • 压缩映射输出以最小化输入/输出磁盘操作

  • Implement a Combiner to minimize massive I/O and network traffic

    用下面一行代码添加一个组合器:

    job . setcombinerclass(reduce . class);

  • Compress the MapReduce job output to minimize large output data effects

    压缩参数为mapred.compress.map.outputmapred.output.compression.type

  • 更改复制参数值,以最大限度地减少网络流量和大量 I/O 磁盘操作

验证硬件资源的 Hadoop 最小配置清单如下:

  • 定义需要安装(和维护)的 Hadoop 生态系统组件
  • 定义您将如何手动或使用自动化部署工具(如 Puppet/Yum)安装 Hadoop
  • 选择底层核心存储,如 HDFS、HBase 等
  • 检查编排、作业调度等是否需要其他组件
  • 检查第三方软件依赖项,如 JVM 版本
  • 检查 Hadoop 的关键参数配置,如 HDFS 块大小、复制因子和压缩
  • 定义监控策略;应该监控什么,使用什么工具(例如 Ganglia)
  • 安装一个监控工具,如 Nagios 或 Ganglia,来监控您的 Hadoop 集群资源
  • 确定(计算)存储作业数据所需的磁盘空间量
  • 确定(计算)执行作业所需的节点数量
  • 检查名称节点和数据节点是否具有所需的最小硬件资源,例如内存量、CPU 数量和网络带宽
  • 计算最大化 CPU 使用率所需的映射器和缩减器任务的数量
  • 检查 MapReduce 任务的数量,以确保有足够的任务正在运行
  • 避免在生产环境中使用虚拟服务器,仅将其用于 MapReduce 应用开发
  • 消除映射端溢出并减少磁盘输入/输出

使用 MapReduce 模板类代码

大多数 MapReduce 应用彼此相似。通常,您可以创建一个基本的应用模板,自定义map()reduce()功能,并重用它。

下面截图中的代码片段向您展示了一个 MapReduce 模板类,您可以对其进行增强和定制以满足您的需求:

Using a MapReduce template class code

在前面的截图中,第 1-16 行是将在应用中使用的所有 Java 类的声明。

在前面的截图中,第 18 行的MapReduceTemplate类是一个声明。这个类扩展了Configured类,实现了工具接口。以下屏幕截图显示了映射器功能:

Using a MapReduce template class code

在上一个截图中,从第 20-36 行开始,static class Map定义代表您的映射函数,如上一个截图中的所示。这个类扩展了Mapper 类,map()函数应该被你的代码覆盖。建议您捕捉任何异常,以防止映射任务失败而不终止其进程。下面截图中的代码片段代表静态类 Reduce 定义:

Using a MapReduce template class code

在上一个截图中,从第 38-54 行开始, static class Reduce definition代表您在上一个代码截图中显示的Reducer功能。这个类扩展了减速器类和 reduce ()功能应该被你的代码覆盖。建议您捕捉任何异常,以防止 reduce 任务在终止其进程之前失败:

Using a MapReduce template class code

在前面的截图中,从第 56-85 行开始,不同的类实现是可选的。这些类将帮助您定义一个自定义的关键从业者、组和排序类比较器,就像前面截图中提到的。run()方法实现如下截图所示:

Using a MapReduce template class code

在前面的截图中,来自的第 87-127 行包括run()方法实现。配置 MapReduce 作业,其参数在该方法内设置,如 Map 和 Reduce 类,如上一张截图所示。您现在可以启用压缩,设置OutputKey类、FileOutputFormat类等。下面截图中的代码代表main()方法:

Using a MapReduce template class code

在上一个截图中,从第 133-138 行,指定了main()方法实现。在这个方法中,会创建一个新的配置实例,并调用run()方法来启动 MapReduce 作业。

撰写申请时需要考虑的其他一些因素如下:

  • 当您有大的输出文件(多个千兆字节)时,通过设置dfs.block.size考虑使用更大的输出块大小。
  • 要提高 HDFS 写入性能,请选择合适的压缩程序编解码器(压缩速度与效率)来压缩应用的输出。
  • 避免每次减少写多个输出文件。
  • 为减速器的输出使用适当的文件格式。
  • 仅使用可拆分的压缩编解码器来写出大量压缩的文本数据。像zlib/gzip/lzo这样的编解码器会适得其反,因为它不能被拆分和处理,这就迫使 MapReduce 框架在单个映射中处理整个文件。考虑使用SequenceFile文件格式,因为它们是压缩和可分割的。

总结

在本章中,我们了解了 Hadoop MapReduce 优化的最佳实践。为了提高 Hadoop 的工作性能,以清单的形式给出了几个建议和提示,这将有助于您确保 Hadoop 集群得到良好的配置和规模。

我们优化和增强 MapReduce 作业性能的学习之旅到此结束。我希望你和我一样喜欢它。我鼓励你使用谷歌、Hadoop 官方网站和互联网上的其他来源发布的 MapReduce 论文,继续了解本书涵盖的更多主题。优化 MapReduce 作业是一个迭代且可重复的过程,您需要在找到最佳性能之前完成它。此外,我建议您尝试不同的技术来找出在您的工作环境中哪些调优解决方案最有效,并尝试组合不同的优化技术!

posted @ 2025-10-01 11:29  绝不原创的飞龙  阅读(28)  评论(0)    收藏  举报