Page 1

Hadoop 教程 (OSSEZ.COM) CHS 技术参考, 2012-09-10

作者:

YUCHENG HU OSSEZ LLC (USA) OSSEZ (中国) 信息技术有限公司

技术支持: http://www.ossez.com http://wiki.ossez.com

相关工作: 技术文档格式化版本

版本历史: 版本

日期

作者

1.1

2012-09-10

YUCHENG HU

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

描述 创建新版本

2012-09-10 1 / 38


目录 1 介绍.............................................................................................................................................. 4 1.1 适用问题范围........................................................................................................................ 4 1.2 大规模的挑战........................................................................................................................ 4 1.3 摩尔定律............................................................................................................................... 6 1.4 Hadoop 方法......................................................................................................................... 7 1.5 与现有方法比较.................................................................................................................... 7 1.6 数据分布............................................................................................................................... 7 1.7 MapReduce:隔离的进程..................................................................................................... 8 1.8 水平的可扩展性.................................................................................................................... 9 2 Hadoop 分布式文件系统............................................................................................................. 11 2.1 分布式文件系统基础........................................................................................................... 11 2.2 配置 HDFS.......................................................................................................................... 14 2.3 集群配置............................................................................................................................. 14 2.4 启动 HDFS.......................................................................................................................... 16 2.5 与 HDFS 交互...................................................................................................................... 17 2.6 常用的操作示例.................................................................................................................. 17 2.7 列出文件............................................................................................................................. 18 2.8 载入数据到集群.................................................................................................................. 18 2.9 从 HDFS 中取得数据........................................................................................................... 20 2.10 停止 HDFS........................................................................................................................ 21 2.11 HDFS 命令参考................................................................................................................. 21 2.12 DFSAdmin 命令参考......................................................................................................... 23 3 MapReduce 基础........................................................................................................................ 26 3.1 函数式编程概念.................................................................................................................. 26 3.2 列表处理(List Processing).............................................................................................. 26 3.3 Mapping 数据列表(Lists)................................................................................................ 26 3.4 Reducing 数据列表(Lists)............................................................................................... 27 3.5 把它们一起放在 MapReduce 中.......................................................................................... 28 3.6 应用例子:词频统计(Word Count).................................................................................29 3.7 驱动方法............................................................................................................................. 31 3.8 MapReduce 数据流............................................................................................................. 32 3.9 近距离观察.......................................................................................................................... 33 3.10 容错性............................................................................................................................... 35 3.11 额外的 MapReduce 功能................................................................................................... 37 OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 2 / 38


OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 3 / 38


1 介绍 欢迎来到 Hadoop 教程!这个系列教程将向你介绍 Apache Hadoop 系统的许多方 向,还将向你展示:如何进行简单和高级的集群配置;如何使用分布式文件系统; 如何使用分布式文件系统,如何开发复杂的 Hadoop MapReduce 应用,并且其它 相关的分布式系统也提及。

1.1 适用问题范围 Hadoop 是一个大规模分布式批处理架构,虽然它在单台计算机上也能使用,但它 的真正能力是在成百上千计算机上运行时才显现出来,Hadoop 可以高效地将大量 工作高效地分布到一组计算机上。 它能处理多大量的工作?Hadoop 面对的处理工作比许多现在系统处理要高几个数 量级,几百 G 的数据,只不过在 Hadoop 眼里不过是小数据量。实际上 Hadoop 是设计来对付“We 级的”的数据,“Web 级”数据大小范围在几百 G 到 T 级,甚 至 P 级。在这种规模下,输入数据很可能甚至不能存入单个计算机的磁盘中,更不 用说内在了,所以 Hadoop 中包括一个分布式文件系统,它将输入文件分成块,将 这些块传输到你的集群中的计算机上保存,这样,原问题可以使用集群中所有计算 机并行处理,那么得到计算结果的效率也就最高。

1.2 大规模的挑战 进行大规模计算是很困难的,要处理大规模数据需要将数据分布到多台机器上并行 处理,第当多台机器之间需要协作时,失败的几率就很升高。在单台机器环境中, 失败并不是设计者经常关心的问题,因为机器崩溃了,反正是无法将程序恢复的。 但在分布式环境中,局部失败是经常会发生的,比如网络会因为交换机和路由器崩 溃局部失败或全部失败;数据有可能因为意外的网络阻塞没有按时到达特定结点; 运算结点可能因为过热导致磁盘崩溃或用完了内存或磁盘空间;数据可能出错,也

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 4 / 38


可能传输时发生错误;不同实现或不同版本软件在使用协议时有细微的差别;时钟 可能变的不同步;锁文件可能没释放;可能在分布式不可打断的传输时受到干扰, 导致网络连接中途断开,等等。在上述每一种情况下,分布式系统的正常工作部分 应该可以从失败中恢复,或使用户不需要关心这些错误,也可以继续正常工作。显 然,提供这种弹性是软件工程的巨大挑战。 不同的分布式系统会着重处理不同的几种失败,而不太关注另外的失败类型。 Hadoop 没有提供安全模型,也没有防止恶意插入数据的安全机制,比如它无法控 制一个在结点间的攻击,然而,它在硬件失败和数据阻塞方面的设计非常健壮,其 它的分布式系统数据它们所要处理的问题(比如,高安全性)需求做出了不同的权衡。 在考虑上述的 bugs 和挑战之外,我们还需要意识到,计算硬件都只有有限的资源, 主要的资源包括: 1. 处理器时间 2. 内存 3. 磁盘空间 4.

网络带宽

单台计算机通常只有几 G 内存,如果输入数据是 TB 级的,那就需要上千台计算机 才能将这些数据放入内存,即便如此,但是单台计算机仍无法处理和寻址这些数据。 磁盘的空间要多的多,单台机器的磁盘空间现在有几 TB 左右,但在大规模计算中, 计算机产生的中间数据通常是输入数据的几倍,这也就将占有输入数据的几倍磁盘 空间。在处理过程中,一些结点磁盘满了,分布式系统也许需要把这些数据传输到 其它可以保存这些溢出数据的结点上去。 最后,即使在内网名,带宽也仍是稀缺资源。假设一组结点用千兆以太网连接,也 许它们之间的有很高的传输速度,但如果所有的结点同时传输几 G 的数据集合, 这样很容易使交换机带宽饱和。另外,如果机器都分在多个主机架中,那么可用以 数据传输的带宽就会更少,更严重的是,用这个信道的 RPC 请求或其它数据传输 请求会延时或被丢弃。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 5 / 38


故一个成功的分布式系统必须能高效地使用上述资源,更重要的是,它在分配这些 资源时,应将系统视为一个整体,将尽可能多的时间用于核心计算。 多台机器之间的同步仍是分布式系统设计的最大挑战,如果结点在分布式系统中可 以直接交互,那么设计者必须认识到交互模式所带来的危险,它很容易产生超出系 统所能承受的 RPC 调用!进行多方数据交换也会引起死锁和竞争。最后,在面临 部分结点失败时,保持继续计算的能力是一个更大的挑战,比如,一个系统中有 100 个结点,其中一个崩溃了,但其它 99 个结点仍能继续计算,理想情况下,系 统只是损失了 1%的计算能力,更进一步,如果在分布架构上采用了复杂的交互网 络,那么决定如何重新计算失败结点的任务是最好,和通知网络拓扑的改变信息, 也许是实现的重要部分。

1.3 摩尔定律 为什么要用一个分布式系统呢?它们听起来麻烦比价值大,随着计算机硬件的快速 的设计脚步,似乎单台计算机的硬件会提高到处理更大的数据量,毕竟摩尔定律 (以 Gordon Moore 命名,Intel 创始人)说道:集成电路上可容纳的晶体管数目,约 每隔 18-24 个月便会增加一倍,性能也将提升一倍,而价格下降一半。但现在的事 实是芯片的设计趋势改变了,虽然我们仍可以将单位区域的晶体管数量翻倍,但这 已经不能提高单线程的计算速度了,新的 CPU,如 Intel Core 2 和 Itanium 2 现在 在架构上努力将一些小的 CPUs 或“核”嵌入到一个物理设备上,这会使多线程并 行地处理与单线程相比两倍的数据,但是每个线程的速度还是和以前是一样的。 即使将成百上千的 CPU 核放到一台计算机上,它也无法很快地把数据传输到这些 核去处理,单个磁盘读取速度大约为 60-100MB/s,虽然磁盘的读取速度一直在提 高,但却无法与处理器速度的提高相比,暂且乐观地假设速度为上限 100MB/s, 并假设有 4 个 I/O 通道,也就是有 400MB/s 的速度,那么一个 4TB 的数据集要用 10,000 秒去读大约 4 小时才能载入数据,但用 100 台只有两个 I/O 通道的机器做相 同的工作,只需要 3 分钟。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 6 / 38


1.4 Hadoop 方法 Hadoop 方法设计目的是将许多普通计算机连接起来并行地处理大量信息。一个上 面提到的 1000-CPU 的机器,比之 1000 个单核或 250 个四核机器的价格要高出许 多,Hadoop 将会将这些小型的低廉的机器连接起来成为一个性价比高的计算机集 群。

1.5 与现有方法比较 在大规模数据上进行分布式计算以前也是有的。Hadoop 的独特之处在于它简单的 编程模型,它可使用户很快地编写和测试分布式系统,和它高效,自动分布数据和 工作到不同机器的方式,以及利用多核并行处理。 计算机的网络调度可以用其它系统,比如 Condor,但 Condor 不能自动分布数据: 必须管理一个单独的 SAN,不止如此,多个结点的协作也必须管理交互系统,如 MPI,这种编程模型极难掌握,并且会引起一些难以理解的错误。

1.6 数据分布 在 Hadoop 集群中,数据被分布到集群的各个结点,Hadoop Distributed File System (HDFS)将大的数据文件分成块,将这些块交由集群中的结点处理,并且每 个块都会复制到不同的几个机器上,所以一台机器崩溃不会导致数据丢失,监测系 统会对结点崩溃做出反应,重新复制数据,即部分存储(partial storage)。即便文件 块被复制分布到不同的机器,但它们形成了一个单一的命名空间,所以它们的内容 还是全局可访问的。 在 Hadoop 的编程框架中,概念上来说,数据是面向记录的。每个输入文件都被以 行或其它特定的应用逻辑格式分开。集群中的每个结点的每个进程都会处理这分开 文件的一部分,Hadoop 框架再根据分布式文件系统的信息调试进程到数据/记录 的位置,因为文件以块的形式存在于分布式文件系统中,每个结点上的每个计算进 程处理数据的一个子集。哪些数据要被一个结点处理是由数据本身的存放位置决定 的。大部分数据是直接地从磁盘读入 CPU,这减少了网络带宽的限制,也防止了 OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 7 / 38


不必要的网络传输。这种策略是将计算移动到数据,而不是将数据移动到计算,这 使得 Hadoop 有丰很高的数据本地性,从而它就有着很高的性能。

1.7 MapReduce:隔离的进程 Hadoop 限制了进程的通信量,因为每个任务所处理的单个记录与其它记录无关, 这初看起来似是一个极大的限制,但它是整个框架更可靠,Hadoop 不会运行任意 一个程序并将它分布到整个集群,程序必须符合一个特定的编程模型,称为 MapReduce。 在 MapReduce 中,Mapper 任务是将记录分开,Mapper 的输出会作为 Reducer 任 务的输入,Reducer 再将不同的 Mapper 结果合并。 在一个 Hadoop 集群中,不同的结点仍会相互通信,但是与其它传统的分布式系统 通信有所不同,传统的作法是应用设计者要显式地通过 socket 或 MPI 缓冲将字节 流从一个结点传到另一个结点,但在 Hadoop 上的通信是隐式的,数据片断都有着 key 值,通过它 Hadoop 就知道如何将相关信息位发送到一个目标结点。Hadoop 在内部管理数据传输和集群拓扑。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 8 / 38


通过限制结点之间的通信,Hadoop 使分布式系统更可靠,单个结点崩溃可以通过 其它结点重新开始任务的方式正常工作。因为用户级任务不需要结点之间显式地通 信,所以不需要用户程序交换信息,同样结点不需要回滚到预定义的检测点去部分 地重新开始计算,在 Hadoop 中其它正常结点继续运行,好似没有错误发生,并将 部分重新开始程序这个挑战的方面留到 Hadoop 层处理。 如下图所示,在结点上运行的 Mapping 和 Reducing 任务,其中记录也在图中有显 示。

1.8 水平的可扩展性 与其它分布式系统相比,使用 Hadoop 的好处在于它的水平的可扩展性,在少量结 点上,用 Hadoop 处理有限的数据时,不能展示 Hadoop 的性能,因为开始 Hadoop 程序相关的代价比较高,其它并行/分布程序方式,比如 MPI (Message Passing Interface)可能在 2 台,4 台或许 10 多台计算机上有更好的性能,尽管在少 量机器上协同工作在这种系统上也许会取得更好的性能,但这种为性能所要付出的

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 9 / 38


努力是非线性的增长。用其它分布式框架所写的程序在从十台机器的级别到成百上 千台机器需要大量的重构工作,这也许要程序重写几次,并且其它框的基础元素会 限制应用的规模大小。 但是特别设计的 Hadoop 有着水平的可扩展性,一个 Hadoop 程序写完后,在 10 个结点上运行,如果迁徙到更大的集群上运行,几乎不需要做什么工作,Hadoop 平台会管理数据和硬件资源并提供与可用资源成比较的可靠性能。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 10 / 38


2 Hadoop 分布式文件系统 HDFS,Hadoop Distributed File System,是一个设计用来保存大数据量的数据的 分布式文件系统(TB 级甚至是 PB 级),并提供快速访问这些数据的能力,数据通过 冗余的方式保存在多台机器上,以来保存对失败的容错性和并行应用的高度可用性, 本章介绍分布式文件系统的设计以及如何使用它。

2.1 分布式文件系统基础 分布式文件系统是设计来保存大数据量的数据,并提供客户端可访问分布在网络上 数据的方法。有许多分布式系统以不同的方法来解决这个问题。 NFS,Network File System,是最常见的分布式文件系统,它也是仍然在使用的最 古老的分布式文件系统,它的设计非常直观,便有很大的局限性,NFS 提供远程访 问单台机器的单个逻辑卷的能力,一个 NFS 服务器将它本地的对外部客户端可见, 客户端可以将这个远程的文件系统直接挂载到它们自己的 Linux 文件系统上,再以 对本地磁盘相同的方式访问它。 这种模型一个最大的优点是它的透明性,客户端并不需要特别关注它们是在操作远 程的数据文件,标准的库函数如 open,close,fread 等等,都可通过 NFS 使用。 但作为一个分布式文件系统,它的能力很有限,一个 NFS 的文件都要存在一台机 器上,这意味着它只能保存一台机器所能保存的数据量,并且不提供这台机器崩溃 的可靠性保证(比如通过复制文件到其它服务器上),如果大量客户端要操作数据, 会使服务器压力很重,并且客户端必需总是将数据读到它们本地机器上才能操作。 HDFS 的设计相对其它分布式文系统,比如 NFS,对几个问题是健壮的,特别是: 1. HDFS 设计用来保持非常大数据量的信息,这需要将数据分布到大量的机器 上,它支持比 NFS 大得多的文件大小。 2. HDFS 的数据保存是可靠的,如果集群中的机器工作不正常,数据仍然是可 用的。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 11 / 38


3. HDFS 提供快速,可扩展的数据访问能力。 4. 通过简单地添加一些机器,就可以使集群能服务于更多的客户端是可能的。 5. HDFS 与 Hadoop MapReduce 能很好地集成,它在可能的情况下,能使数 据读取,计算本地化。 尽管 HDFS 有很强的可扩展性,但由于它高性能的设计也使它局限于某些应用范围, 它并不是如 NFS 通用,下面是 HDFS 做出的一些权衡: 1. 应用程序使用 HDFS 要使用流的方式读取文件,HDFS 对流式读取文件进地 了优化,但这时 seek 到文件的任一位置这种操作代价就高了。 2. 数据是一次写入,多次读取;Hadoop 不支持写操作关闭文件后,再更新文 件(Hadoop 0.19 支持追加文件)。 3. 由于文件很大,并且是流式读取,所以系统并不提供本地缓存机制,如果要 取读过的数据,只能再从 HDFS 文件中再次读取。 4. Hadoop 假设每个机器都极可能失败,或永远或暂时,集群必须能应付多台 机器同时失败的情况(比如,主机架倒了),这时集群性能下降应与损失的 机器成比例,系统作为一个整体不应变得很慢,也不应该有数据丢失, Hadoop 用复制数据的策略来克服这一困难。 HDFS 的设计是基于 GFS,Google File System 的设计,它的设计在论文 Google File System 中有描述。 HDFS 是块结构的文件系统,单个文件被分成相同大小的块,这些块被分布到集群 中的多台机器上,集群中的单个机器被称为数据结点(DataNode),一个文件可由 多个块组成,它们并不需要放到同一台机器上,保存每个块的机器是被随机选择的, 所以访问一个文件需要多台机器协作,但它所支持的文件大小会比单台机器的 DFS 大的多,并保持一个台机器存不下的文件。 如果使用多台机器保存一个文件,那么这些机器中任一一台崩溃都会导致文件不可 访问,HDFS 通过将每块复制到多台机器(默认 3 台)的方式来解决这个问题。 如下图所示:数据结点保存着多个文件的块,复制数为 2,名字结点中保存着文件 名与块 id 的关系。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 12 / 38


大多数基于块的文件系统的块大小为 4KB 或 8KB,与之相比,HDFS 中默认使用块 大小为 64MB,比前者大出几个数量级。这使得 HDFS 减少了对每个文件元数据的 存储量,不只如些,通过很大量数据连续化存放在磁盘上,就可以快速地流式读取 数据,这种设计的结果是 HDFS 倾向于处理大文件,并顺序地读取不同于诸如 NTFS 或 EXT。这些要处理许多小文件的文件系统。HDFS 倾向于保存大量超大文件, 每个几百 M 或几 G,因为 100MB 的文件才不过能分成 2 个块,在你计算机上的文 件也许经常被随机访问,即应用程序从一个文件中不同位置读取少量信息,而不是 顺序访问,相反,HDFS 希望程序从开始顺序读到结尾的方式读,这使它在第四章 所介绍的 MapReduce 形式的编程中特别有用。 HDFS 是在一些机器中以块的形式保存文件,但这些文件并不是普通文件系统的一 部分,在运行 Hadoop 服务的数据结点上输入 ls 命令,可以显示普通文件系统的 内容,但它不能显示 HDFS 中的文件,这是因为 HDFS 在一个不同的命名空间中运 行,它与本地文件内容是隔离的,HDFS 中的文件(更准确性,组成这些文件的 块)是保存在数据结点服务管理的一个特定目录下,这些文件没有名字,只有块 id,你无法通过 Linux 文件命令与 HDFS 中的文件交互。 OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 13 / 38


但 HDFS 有它自己的文件管理命令,并且还与你熟悉的 Linux 文件命令很相似,本 章将在后面部分向你介绍这些命令。 文件系统可靠保存它的元数据是很重要的,更准确地说,因为文件数据是以一次写 入,多次读取的方式访问的,元数据结构(比如:文件名,目录名)会被大量客户 端同时修改,元数据结构信息户不使去同步是很重要的,所以这些只由一台机器来 处理,它被称为名字结点(NameNode),保存着文件系统中所有元数据。因为 每个文件都有较少的元数据(它只保存文件名,权限,和文件每块的位置),所有 的元数据都保存在 NameNode 的内存中,可以进行快速访问元数据。 要打开一个文件,客户端可从 NodeNode 取得该文件每个块的位置,即是哪些结 点保存着这个文件的块,客户端再直接从 DataNode 中读取每个块,这个过程可以 并行读取,NameNode 并不直接参与块数据传输,这可使它的压力减至最小。 当然,即使名字结点失败名字结点的信息也必须保存,所以有多个冗余系统可以使 名字结构完全崩溃时,也可以保存元数据信息。名字结点崩溃比如数据结点崩溃更 严重,当单个数据结点崩溃,整个集群也会正常运行,但名字结点崩溃后,集群将 不可访问,除非名字结点手工恢复,幸运的是名字结点参与相比是非常少的,所以 它崩溃的可能也远低于任一数据结点。

2.2 配置 HDFS 在集群上配置 HDFS 只需要很短的时间,首先我们来看 Hadoop 配置有关的部分, 再看名字结点的格式。

2.3 集群配置 首先要把 Hadoop 下载并解压,第五章会介绍 Hadoop 如何运行,第七章介绍如何 建立一个大的集群,并为 Hadoop 做一些预设置,包括下载依赖的软件。 HDFS 配置文件是一组 XML 文件,它们位于 Hadoop 的配置目录 conf 下,conf 目 录在 Hadoop 的安装目录下(即解压 Hadoop 后产生的目录)。conf/hadoopdefaults.xml 文件中包含了 Hadoop 中的所有参数默认值,这个文件一般视为是只

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 14 / 38


读的,你可以在 conf/hadoop-site.xml 中设置新的值,以覆盖默认值,这个文件会 被复制集群中的所有机器上。 配置设置是以一组键-值对的格式: 1 2 3 4

<property> <name>property-name</name> <value>property-value</value> </property>

配置设置是以一组键-值对的格式: 在配置中加入<final>true</final>可防止属性被别的应用覆盖。这在大多系统范围 配置选项中是有用的。 下列的设置在 HDFS 配置中是必需的: Key

Value

Example

fs.default.name

protocol://servername:port

hdfs://alpha.milkman.org:9000

dfs.data.dir

pathname

/home/username/hdfs/data

dfs.name.dir

pathname

/home/username/hdfs/name

这些设置的描述如下: fs.default.name,这是集群名字结点的 URI(协议,主机名,端口)。Hadoop 系 统中的结点都需要知道名字结点的地址才能进行操作,数据结点要与这个名字结点 一起注册,才吏它们的数据通过名字结点变为可用的,客户端要通过个地址取得文 件块的真正存放地址再去取数据。 dfs.data.dir,这是数据结点保存数据的本地文件系统的路径,因为它们将运行在不 同的机器上,并不需要所有的数据结点都将数据保存到相同前缀的本地路径,集群 中的机器是不同的,也是可行的。但如果这个目录在整个系统都是统一的,那么配 置也就简单些,Hadoop 默认将 dfs.data.dir 设为/tmp。这在测试时是没关系的, 但在生产环境中,这样做很容易丢失数据,所以必需覆盖。 dfs.name.dir,这是名字结点保存元数据的本地路径,它仅用于名字结点找到它自 己的信息,它在数据结点上不存在,它的默认值也是/tmp,所以它也必须覆盖。 另一个上面没有提及的参数 dfs.replication,它是文件系统中每个块的默认复制数, 在生产环境集群中,一般就用它的默认值 3,你可以随意增加复制数,但它会无谓 地用去很多空间,如果把它设小,会影响数据的可用性和可靠性。 OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 15 / 38


在单结点上的配置中,可以直接把下面的配置复制到 hadoop-site.xml 中。 5 6 7 8 9 10 11 12 13 14 15 16 17 18

<configuration> <property> <name>fs.default.name</name> <value>hdfs://your.server.name.com:9000</value> </property> <property> <name>dfs.data.dir</name> <value>/home/username/hdfs/data</value> </property> <property> <name>dfs.name.dir</name> <value>/home/username/hdfs/name</value> </property> </configuration>

当然,your.server.name.com 和 username 是需要修改的,而使用端口 9000 是任 意选择的。 将以上配置拷入 con/hadoop-site.xml 文件后,将 con/目录拷到集群中所有机器上。 主结点需要知道所有数据结点机器的地址,因为启动脚本依赖它,同样在 con/目 录中,编辑文件子结点,使它包含子结点的完整主机名列表,每行一个主机名,主 结点(比如 localhost)通常不在这个文件中出现。 然后创建下面的目录 19 20

user@EachMachine$ mkdir -p $HOME/hdfs/data user@namenode$ mkdir -p $HOME/hdfs/name

拥有 Hadoop 实例的用户需要可以读写上面的两个目录,并不是所有的用户都需要 访问这两个目录,用 chmod 来设置权限是比较合理的。在大规模环境中,建议使 用者在每个结点上创建一个名为 hadoop 的用户,以来拥有和运行 hadoop 任务, 对于单个机器使用你自己的用户名,运行 Hadoop 是完全没有问题的,但不建议用 root 运行 Hadoop。

2.4 启动 HDFS 现在我们需要格式化我们设置的文件系统: 21

user@namenode:hadoop$ bin/hadoop namenode -format

上述操作只应进行一次,当它完成时,我们可以启动分布式文件系统了。 22

user@namenode:hadoop$ bin/start-dfs.sh

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 16 / 38


这个命令会启动主结点的名字结点(也就是 start.dfs.sh 被调用的地方),它同样 也启动每个子结点(slave machine)上的数据结点实例,在单机“集群”中,数据结 点与名字结点在同一台机器上,在两台或多台机器的真实集群上,这个脚本会 ssh 到每个子结点,启动数据结点实例。

2.5 与 HDFS 交互 这节将向你介绍与 HDFS 交互的命令,这些命令可以载入数据,取得数据,操作文 件。 所有与集群交互的命令是通过一个脚本 bin/hadoop 来进行的,它可以用 Java 虚拟 机载入 Hadoop 系统,来执行用户命令,命令的格式如下: 23

user@machine:hadoop$ bin/hadoop moduleName -cmd args...

modulteName 是要使用的 Hadoop 功能的模块名,-cmd 是用这个模块的命令, 它的参数在命令名之后。 有两个模块与 HDFS 相关:dfs 和 dfsadmin,它们的用途将在下面描述。

2.6 常用的操作示例 dfs 模块也被称为“FsShell”,它提供基本的文件操作功能,下面将进行介绍。 只有集群有有用信息时,这个集群才是有用的。所以首先要进行的操作是载入数据 到集群,在示例中,我们假设用户为“someone”,你自己尝试时使用自己的用户 名就可以了,同样注意对文件的操作可以是集群中任一一个结点的操作,在这个结 点上的 conf/hadoop-sicte.xml 中 fs,default.name 为你集群的名字结点。我们称我 们操作的机器为 anynode,命令行在你安装 Hadoop 的 hadoop 目录下运行,也许 它在的机器的路径是/home/someone/hadoop,在别的机器上 是/home/foo/hadoop,开始介绍的命令主要是载入数据,查看它是否在那里,再 从 HDFS 中取回数据。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 17 / 38


2.7 列出文件 如果我们现在就尝试去查看 HDFS,我们什么也不会看到: 24 25

someone@anynode:hadoop$ bin/hadoop dfs -ls someone@anynode:hadoop$

ls 命令什么也没有返回。不带参数,-ls 将列出 HFFS 的主目录下的文件,不要忘记 它和/home/$USER 不是同一目录(HDFS 与本地文件不在一个命名空间中), HDFS 中没有当前工作目录这个概念,也没有 cd 命令。 如果提供-ls 参数,你会看到一些初始目录的内容: 26 27 28 29

someone@anynode:hadoop$ bin/hadoop dfs -ls / Found 2 items drwxr-xr-x - hadoop supergroup 0 2008-09-20 19:40 /hadoop drwxr-xr-x - hadoop supergroup 0 2008-09-20 20:08 /tmp

这些都是系统创建的内容,示例输出假设 hadoop 是用户名,Hadoop 守护进程就 是在这个用户名下启动的。Supergroup 是 HDFS 实例启动用户(hadoop)所在的 组,示例的输出目录可使 Hadoop MapReduce 系统将必要的数据移到不同的结点。

2.8 载入数据到集群 因为在一般的 Unix 或 Linux 系统中,用户的文件保存在/home/$USER 中,所以 HDFS 将文件保存在/user/$USER,对于一些如 ls 的命令,需要用户给出目录名却 没有给出时,就默认/home/$USER 目录(有的命令要求明确指名源路径和目标路 径),在 HDFS 或 Hadoop MapReduce 中,参数中所有的相对路径,或系统的其 它地方,都假设相对的是/home/$USER 目录。 第一步:如果你的主目录不存在,先创建它。 30

someone@anynode:hadoop$ bin/hadoop dfs -mkdir /user

如果没有/user 目录,先创建它,其实它会在需要的时候自动创建,但作为示例, 现在我们手动创建也是合理的。 然后我们可以创建你的主目录了。 31

someone@anynode:hadoop$ bin/hadoop dfs -mkdir /user/someone

当然,别忘了把/user/someone 换成/user/yourUserName。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 18 / 38


第二步:上传一个文件,将单个文件载入 HDFS,我们可以用 put 命令: 32 33

someone@anynode:hadoop$ bin/hadoop dfs -put /home/someone/interestingFile.txt /user/yourUserName

这个命令会将/home/someone/interestingFile.txt 从本地目录拷贝到 HDFS 的/user/yourUserName/interestingFile.txt。 第三步:检查在 HDFS 中的文件,我们可用下面任一命令来查看文件。 34 35

someone@anynode:hadoop$ bin/hadoop dfs -ls /user/yourUserName someone@anynode:hadoop$ bin/hadoop dfs -ls

在下面将展示 put 命令的示例和它们的结果: 命令

bin/hadoop dfs -put foo bar

假设

结果

No file/directory named /user/$USER/bar

Uploads local file foo to a file named

exists in HDFS

/user/$USER/bar Uploads local file foo to a file named

bin/hadoop dfs -put foo bar

/user/$USER/bar is a directory

/user/$USER/bar/foo Uploads local file foo to a file named

bin/hadoop dfs -put foo somedir/somefile

/user/$USER/somedir/somefile, creating /user/$USER/somedir does not exist in HDFS the missing directory No change in HDFS, and an error is

bin/hadoop dfs -put foo bar

/user/$USER/bar is already a file in HDFS

returned to the user

当用 put 命令操作一个文件,这个操作或是全部完成,或是一点不做。一个文件上 传到 HDFS,首先要把数据复制到数据结点,当数据结点表明它们已经接收了全部 的数据,并且文件句柄也已经关闭,那么这时文件才对系统其余部分是可见的,所 以根据 put 命令的返回值 ,你可以确定文件或是成功上传,或是“完全失败”, 你将永远不会得到一个文件部分上传或是文件内容部分可见的状态,如果遇到上传 断开连接,或上传内容未完成的情况,HDFS 表现地就像没有上传发生一样。 第四步:一次上传多个文件。put 命令可以一次操作多个文件,还可以上传整个目 录到 HDFS 中。 创建一个目录,然后用 cp 命令拷贝几个文件到这个目录中,在示例中,我们有如 下目录内容: 36 37 38 39

someone@anynode:hadoop$ ls -R myfiles myfiles: file1.txt file2.txt subdir/

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 19 / 38


40 41 42

myfiles/subdir: anotherFile.txt someone@anynode:hadoop$

整个 myfiles/目录可以用下面命令拷贝到 HDFS 中: 43 44 45 46 47 48 49 50 51 52 53 54 55

someone@anynode:hadoop$ bin/hadoop -put myfiles /user/myUsername someone@anynode:hadoop$ bin/hadoop -ls Found 1 items /user/someone/myfiles <dir> 2008-06-12 20:59 rwxr-xr-x someone supergroup user@anynode:hadoop bin/hadoop -ls myfiles Found 3 items /user/someone/myfiles/file1.txt <r 1> 186731 2008-06-12 20:59 rw-r-r-someone supergroup /user/someone/myfiles/file2.txt <r 1> 168 2008-06-12 20:59 rw-r-r-someone supergroup /user/someone/myfiles/subdir <dir> 2008-06-12 20:59 rwxrxr-x someone supergroup

上面展示了整个目录被正确地递归上传上,你会注意到除了文件路径外,ls 命令还 显示了每个文件的复制数(在<r 1>中的 1),文件大小,上传时间,权限,所有 者信息。 另一个与 put 功能相同的命令-copyFromLocal,它在使用方法上与 put 完全一样。

2.9 从 HDFS 中取得数据 有多种多 HDFS 中取得数据的方法,最简单的方法是用 cat 命令将文将内容输出到 标准输出(当然它也可以输出到管道用于其它应用或目标文件)。 第一步:用 cat 显示文件。 假设你已经上传了一个 foo 文件到 HDFS 的主目录,你可以用以下命令显示它的内 容: 56 57 58

someone@anynode:hadoop$ bin/hadoop dfs -cat foo (contents of foo are displayed here) someone@anynode:hadoop$

第二步:将一个文件从 HDFS 拷贝到本地文件系统。 get 命令作用与 put 命令相反,它将一个文件或一个目录(递归地)从 HDFS 中拷 贝到你选择的本地文件系统中,一个功能相同的命令是 copyToLocal。 59 60 61 62 63

someone@anynode:hadoop$ bin/hadoop dfs -get foo localFoo someone@anynode:hadoop$ ls localFoo someone@anynode:hadoop$ cat localFoo (contents of foo are displayed here)

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 20 / 38


2.10 停止 HDFS 如果你想停止集群的 HDFS(或是因为不想不用 Hadoop 时,它还占用内存资源, 或是因为想升级配置重启集群),那么你可以登录名字结点所在的机器,运行: 64

someone@namenode:hadoop$ bin/stop-dfs.sh

这个命令必须由启动 HDFS,即运行 bin/start-dfs.sh 的用户执行。

2.11 HDFS 命令参考 除介绍的命令外,还有许多 bin/hadoop.dfs 命令,以上介绍的只是帮助你开始使 用 HDFS,运行 bin/hadoop dfs 不带任何参数会列出所有 FsShell 系统提供的命令, 当你遇到问题时执行 bin/hadoop dfs –helpcommandName 会显示这个命令的用法。 下面是所有命令的介绍,介绍之前先定义一下参数的意义: 1. 斜体:表示用户输入的变量。 2. path:表示文件名或目录名。 3. path…:表示一个或多个文件名或目录名。 4. file:表示任意文件名。 5. src 和 dest:表示 HDFS 的源路径和目标路径 6. localSrc 和 localDest:表示本地文件系统的源路径和目标路径。 7. 在[]中的参数是可选的。 1. -ls path 列出 path 目录下的内容,包括文件名,权限,所有者,大小和修改时间。 2. -lsr path 与 ls 相似,但递归地显示子目录下的内容。 3. -du path 显示 path 下所有文件磁盘使用情况下,用字节大小表示,文件名用完整的 HDFS 协议前缀表示。 4. -dus path 与-du 相似,但它还显示全部文件或目录磁盘使用情况 OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 21 / 38


5. -mv src dest 将文件或目录从 HDFS 的源路径移动到目标路径。 6. -cp src dest 在 HDFS 中,将 src 文件或目录复制到 dest。 7. –rm path 删除一个文件或目录 8. –rmr path 删除一个文件或递归删除目录 9. –put localSrc dest 将本地文件或目录 localSrc 上传到 HDFS 中的 dest 路径。 10. –copyFromLocal localSrc dest 与-put 命令相同 11. –moveFromLocal localSrc dest 将文件或目录从 localSrc 上传到 HDFS 中的 dest 目录,再删除本地文件或目录 localSrc。 12 –get [-crc] src localDest 将文件或目录从 HDFS 中的 src 拷贝到本地文件系统 localDest。 13 –getmerge src localDest [addnl] 将在 HDFS 中满足路径 src 的文件合并到本地文件系统的一个文件 localDest 中。 14 –cat filename 显示文件内容到标准输出上。 15. -copyToLocal [-crc] src localDest 与-get 命令相同。 16 -moveToLocal [-crc] src localDest 与-get 命令相似,但拷贝结束后,删除 HDFS 上原文件。 17 -mkdir path

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 22 / 38


在 HDFS 中创建一个名为 path 的目录,如果它的上级目录不存在,也会被创建, 如同 linux 中的 mkidr –p。 18 -setrep [-R] [-w] rep path 设置目标文件的复制数。 19 -touchz path 创建一个文件。时间戳为当前时间,如果文件本就存在就失败,除非原文件长充为 0。 20 -test –[ezd] path 如果路径(path)存在,返回 1,长度为 0(zero),或是一个目录(directory)。 21 –stat [format] path 显示文件所占块数(%b),文件名(%n),块大小(%n),复制数(%r),修改时间(%y %Y)。 22 –tail [-f] file 显示文件最后的 1KB 内容到标准输出。 23 –chmod [-R] [owner][:[group]] path… 递归修改时带上-R 参数,mode 是一个 3 位的 8 进制数,或是[augo]+/-{rwxX}。 24 –chgrp [-R] group 设置文件或目录的所有组,递归修改目录时用-R 参数。 25 –help cmd 显示 cmd 命令的使用信息,你需要把命令的“-”去掉。

2.12 DFSAdmin 命令参考 dfs 模块提供了常用的文件或目录操作,但它们都是对文件系统内部的操作, dfsadmin 模块是对整个文件系统操作或查询,我们下面将介绍它。 得到全局状态。一个简洁的 HDFS 状态状态报告可以用 bin/hadoop dfsadmin – report 得到,它返回的是 HDFS 集群的基本健康信息。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 23 / 38


更详细的状态。如果你想知道名字结点元数据状态的更多信息,你可以使用 bin/hadoop dfsadmin –metasave filename 命令,它将信息保存到 filename 中, metasave 命令枚举在复制的块列表。注意,这个命令的帮助信息是“保存名字结 点的主要数据结构”,但它是错误 的,名字结点的状态不能通过个命令保存,它 提供的是名字结点对数据块的处理信息。 安全模式。安全模式是 HDFS 的一种状态,在这种状态下,文件系统以只读方式被 挂载,不进行冗余复制,文件不能被创建也不能被删除,这是名字结点启动后自动 进入的状态,以来允许所有数据结点与名字结点时间同步,并声明它们所拥有的块, 然后名字结点才决定哪些块被复制,等等。名字结点要等到一定比例的数据块可见, 这个比例是由配置中的 dfs.safemode.threshold 参数来决定,在达到这个比例的阈 值后,安全模式自动退出,然后 HDFS 允许正常操作。bin/hadoop dfsadmin – safemode what 命令允许用户根据 what 的值操作安全模式,what 的值在下面列出: 1. enter 进入安全模式。 2. leave 强制名字结点退出安全模式。 3. get 返回标名安全模式是 ON 还是 OFF 的字符串。 4. wait 等待直到名字结点退出安全模式后自动返回。 修改 HDFS 成员。当停止结点时,要将它们逐步地与 HDFS 断开连接,来保证数据 不会丢失,在本章停止结点的小节中,介绍-refreshNodes dfasdmin 命令。 升级 HDFS 版本。当将 HDFS 版本升级到另一版本时,名字结点和数据结点的格式 有可能改变,当你要启动新的 Hadoop 时,你需要告诉 Hadoop 去修改 HDFS 版本, 用 bin/start-dfs.sh –upgrade 命令。它将开始升级 HDFS 版本,升级进行的信息可 以用 bin/hadoop dfsadmin –upgradeProgress status 命令查询,你可以用 bin/hadoop dfsadmin –upgradeProgress details 得到更详细的升级信息。如果升 级阻塞了,你可以用 bin/hadoop dfsadmin –updateProgress force 命令,使它继 续进行(注意,在使用这个命令时,你最好知道你在干什么)。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 24 / 38


当 HDFS 升级后,Hadoop 保留了允许你降级到以前 HDFS 版本的信息,以防你想 回到以前的 Hadoop 版本,如果你要降级就先停止集群,重装旧版的 Hadoop,再 运行 bin/start-dfs.sh –rollback,它就会回到以前版本的 HDFS 状态。 每次只能保存一个存档拷贝,所以在新版本下运行几天后(当感觉它稳定后),可 以用 bin/hadoop dfsadmin –finalizeUpgrade 命令删除存档拷贝,而 rollback 命令 将不再可用,但这也是进行第二次升级必要的一个工作。 获得帮助。与 dfs 中一样,输入 bin/hadoop dfsadmin –help cmd 命令会显示这个 命令的用法。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 25 / 38


3 MapReduce 基础 3.1 函数式编程概念 MapReduce 程序是设计用来并行计算大规模海量数据的,这需要把工作流分划到 大量的机器上去,如果组件(component)之间可以任意的共享数据,那这个模型就 没法扩展到大规模集群上去了(数百或数千个节点),用来保持节点间数据的同步 而产生的通信开销会使得系统在大规模集群上变得不可靠和效率低下。 实际上,所有在 MapReduce 上的数据元素都是不可变的,这就意味着它们不能够 被更新。如果在一个 mapping 任务中你改变了一个输入键值对,它并不会反馈到 输入文件;节点间的通信只在产生新的输出键值对((key,value)pairs)时发生, Hadoop 系统会把这些输出传到下一个执行阶段。

3.2 列表处理(List Processing) 从概念上讲,MapReduce 程序转变输入数据元素列表成输出数据元素列表。一个 MapReduce 程序会重复这个步骤两次,并用两个不同的术语描述:map 和 reduce,这些术语来自于列表处理语言,如:LISP,Scheme,或 ML。

3.3 Mapping 数据列表(Lists) MapReduce 程序的第一步叫做 mapping,在这一步会有一些数据元素作为 Mapper 函数的输入数据,每次一个,Mapper 会把每次 map 得到的结果单独的传 到一个输出数据元素里。 如下图所示,Mapping 通过对输入数据列表中的每一个元素应用一个函数创建了 一个新的输出数据列表。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 26 / 38


这里举一个 map 功能的例子:假设你有一个函数 toUpper(str),用来返回输入字符 串的大写版本。你可以在 map 中使用这个函数把常规字符串列表转换成大写的字 符串列表。注意,在这里我们并没有改变输入字符串:我们返回了一个新的字符串, 它是新的输出列表的组成部分之一。

3.4 Reducing 数据列表(Lists) Reducing 可以让你把数据聚集在一起。reducer 函数接收来自输入列表的迭代器, 它会把这些数据聚合在一起,然后返回一个输出值。 如下图所示,通过列表迭代器对输入数据进行 reducing 操作来输出聚合结果。

Reducing 一般用来生成”总结“数据,把大规模的数据转变成更小的总结数据。 比如,"+"可以用来作一个 reducing 函数,去返回输入数据列表的值的总和。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 27 / 38


3.5 把它们一起放在 MapReduce 中 Hadoop 的 MapReduce 框架使用了上面的那些概念并用它们来处理大规模的数据 信息。MapReduce 程序有着两个组件:一个实现了 mapper,另一个实现了 reducer。上面描叙的 Mapper 和 Reducer 术语在 Hadoop 中有了更细微的扩展, 但基本的概念是相同的。 键和值:在 MapReduce 中,没有一个值是单独的,每一个值都会有一个键与其关 联,键标识相关的值。举个例子,从多辆车中读取到的时间编码车速表日志可以由 车牌号码标识,就像下面一样: 65 66 67 68 69

AAA-123 ZZZ-789 AAA-123 CCC-456 ...

65mph, 50mph, 40mph, 25mph,

12:00pm 12:02pm 12:05pm 12:15pm

mapping 和 reducing 函数不是仅接收数值(Values),而是(键,值)对。这些 函数的每一个输出都是一样的:都是一个键和一个值,它们将被送到数据流的下一 个列表。 对于 Mapper 和 Reducer 是如何工作的,MapReduce 没有像其它语言那样严格。 在更正式的函数式 mapping 和 reducing 设置中,mapper 针对每一个输入元素都 要生成一个输出元素,reducer 针对每一个输入列表都要生成一个输出元素。但在 MapReduce 中,每一个阶段都可以生成任意的数值;mapper 可能把一个输入 map 为 0 个,1 个或 100 个输出。reducer 可能计算超过一个的输入列表并生成一 个或多个不同的输出。 根据键划分 reduce 空间:reducing 函数的作用是把大的数值列表转变为一个(或 几个)输出数值。在 MapReduce 中,所有的输出数值一般不会被 reduce 在一起。 有着相同键的所有数值会被一起送到一个 reducer 里。作用在有着不同键关联的数 值列表上的 reduce 操作之间是独立执行的。 如下图所示,不同颜色代表不同的键,有着相同键的数值都被传到同一个 reduce 任务里。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 28 / 38


3.6 应用例子:词频统计(Word Count) 写一个简单的 MapReduce 程序就可以用来统计不同的词在一个文件集中出现的次 数。 比如,我们有这样的文件: 70 71

  foo.txt: Sweet, this is the foo file   bar.txt: This is the bar file

我们期望输出会是这样子: 72 73 74 75 76 77 78

sweet this is the foo bar file

1 2 2 2 1 1 2

当然没问题,我们可以写一个 MapReduce 程序来计算得到这个输出。高层结构看 起来会是这样子: 79 80 81 82 83 84 85 86 87

mapper (filename, file-contents): for each word in file-contents: emit (word, 1) reducer (word, values): sum = 0 for each value in values: sum = sum + value emit (word, sum)

列表为 MapReduce 词频统计伪代码。 若干个 mapper 函数的实例会被创建在我们的集群的不同机器上,每个实例接收一 个不同的输入文件(这里假设我们有很多个文件)。mappers 输出的(word,1)

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 29 / 38


键值对会被转到 reducers 那里去。若干个 reducer 方法实例也会在不同机子上被实 例化。每个 reducer 负责处理关联到不同词的数值列表,数值列表中的值都是 1; reducer 把这些“1”值总和到一个关联了某个词的最终计数里。reducer 然后生成 最终的(word,count)输出,并把它写到一个输出文件里。 针对这个,我们可以在 Hadoop MapReduce 中写一个很相似的程序;它被包括在 Hadoop 分发包中,具体在 src/examples/org/apache/hadoop/examples/WordCount.java。 它的部分代码如下: 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121

public static class MapClass extends MapReduceBase implements Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text();

}

public void map(LongWritable key, Text value, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException { String line = value.toString(); StringTokenizer itr = new StringTokenizer(line); while (itr.hasMoreTokens()) { word.set(itr.nextToken()); output.collect(word, one); } }

/** * A reducer class that just emits the sum of the input values. */ public static class Reduce extends MapReduceBase implements Reducer<Text, IntWritable, Text, IntWritable> {

}

public void reduce(Text key, Iterator<IntWritable> values, OutputCollector<Text, IntWritable> output, Reporter reporter) throws IOException { int sum = 0; while (values.hasNext()) { sum += values.next().get(); } output.collect(key, new IntWritable(sum)); }

列表 MapReduce 词频统计 Java 源码   实际 Java 实现与上述伪代码之间有一些微小的差别。首先,Java 没有原生的 emit 关键字;你得到的 OutputCollector 输入对象会接收数值并 emit 到下一执行阶段。 第二,Hadoop 使用的默认输入格式把输入文件的每一行作为 mapper 单独的一个 输入,不是一次整个文件。其中还使用了一个 StringTokenizer 对象用来把一行数 OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 30 / 38


据拆分为词组。这个操作没有对输入数据做任何规格化处理,所以 “cat”,“Cat”,“cat,”都被认为是不同的字符串。注意,类变量 word 在每一次 mapper 输出另外一个(word,1)键值对时都被重复使用;这个举措节省了为每 个输出创建一个新的变量的时间。output.collect()方法会拷贝它收到的数值作为输 入数据,所以你可以覆盖你使用的变量。

3.7 驱动方法 Hadoop MapReduce 程序的最后一个组件叫做 Driver,它会初始化 Job 和指示 Hadoop 平台在输入文件集合上执行你的代码,并控制输出文件的放置地址。下面 是 Hadoop 自带的 Java 实现例子里的一个整理版本 driver 的代码: 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138

public void run(String inputPath, String outputPath) throws Exception { JobConf conf = new JobConf(WordCount.class); conf.setJobName("wordcount"); // the keys are words (strings) conf.setOutputKeyClass(Text.class); // the values are counts (ints) conf.setOutputValueClass(IntWritable.class); conf.setMapperClass(MapClass.class); conf.setReducerClass(Reduce.class); FileInputFormat.addInputPath(conf, new Path(inputPath)); FileOutputFormat.setOutputPath(conf, new Path(outputPath)); }

JobClient.runJob(conf);

列表 Hadoop MapReduce 词频统计驱动器 这个方法建立了一个在给定输入文件夹(inputPath 参数)里的文件上执行词频统 计程序的作业(Job)。reducers 的输出被写到 outputath 指定的文件夹内。用于 运行 job 的配置信息保存在 JobConf 对象里。通过 setMapperClass()和 setReducerClass()方法可以设定 mapping 和 reducing 函数。 reducer 生成的数据类型由 setOutputKeyClass()和 setOutputValueClass()方法设定。 默认情况下假定这些也是 mapper 的输出数据类型。如果你想设定不同的数据格式 的话,可以通过 JobConf 的 setMapOutputKeyClass()和 setMapOutputValueClass()方法设定。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 31 / 38


mapper 的输入数据类型由 InputFormat 控制。输入格式在这里有详细的讨论。默 认的输入格式是“TextInputFormat”,它会以(LongWritable,Text)键值对的方 式加载数据。long 值表示某一行在文件中的偏移量,Text 对象则保存某一行的字 符串内容。 通过调用 JobClient.runJob(conf)即可向 MapReduce 提交 job,这个调用会阻塞直 到 job 完成。如果 job 失败了,它会抛出一个 IOException。JobClient 还提供了一 个非阻塞版本的调用方法 submitJob()。

3.8 MapReduce 数据流 高层 MapReduce 工作流水线

MapReduce 的输入一般来自 HDFS 中的文件,这些文件分布存储在集群内的节点 上。运行一个 MapReduce 程序会在集群的许多节点甚至所有节点上运行 mapping 任务,每一个 mapping 任务都是平等的:mappers 没有特定“标识物”与其关联。 因此,任意的 mapper 都可以处理任意的输入文件。每一个 mapper 会加载一些存

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 32 / 38


储在运行节点本地的文件集来进行处理(译注:这是移动计算,把计算移动到数据 所在节点,可以避免额外的数据传输开销)。 当 mapping 阶段完成后,这阶段所生成的中间键值对数据必须在节点间进行交换, 把具有相同键的数值发送到同一个 reducer 那里。Reduce 任务在集群内的分布节 点同 mappers 的一样。这是 MapReduce 中唯一的任务节点间的通信过程。map 任务间不会进行任何的信息交换,也不会去关心别的 map 任务的存在。相似的, 不同的 reduce 任务之间也不会有通信。用户不能显式的从一台机器封送信息到另 外一台机器;所有数据传送都是由 Hadoop MapReduce 平台自身去做的,这些是 通过关联到数值上的不同键来隐式引导的。这是 Hadoop MapReduce 的可靠性的 基础元素。如果集群中的节点失效了,任务必须可以被重新启动。如果任务已经执 行了有副作用(side-effect)的操作,比如说,跟外面进行通信,那共享状态必须 存在可以重启的任务上。消除了通信和副作用问题,那重启就可以做得更优雅些。

3.9 近距离观察 细节化的 Hadoop MapReduce 数据流

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 33 / 38


展示了流线水中的更多机制。虽然只有 2 个节点,但相同的流水线可以复制到跨越 大量节点的系统上。下去的几个段落会详细讲述 MapReduce 程序的各个阶段。   输入文件:文件是 MapReduce 任务的数据的初始存储地。正常情况下,输入 文件一般是存在 HDFS 里。这些文件的格式可以是任意的;我们可以使用基于行的 日志文件,也可以使用二进制格式,多行输入记录或其它一些格式。这些文件会很 大—数十 G 或更大。   输入格式:InputFormat 类定义了如何分割和读取输入文件,它提供有下面的 几个功能: ?

选择作为输入的文件或对象;

?

定义把文件划分到任务的 InputSplits;

?

为 RecordReader 读取文件提供了一个工厂方法;

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 34 / 38


Hadoop 自带了好几个输入格式。其中有一个抽象类叫 FileInputFormat,所有操作 文件的 InputFormat 类都是从它那里继承功能和属性。当开启 Hadoop 作业时, FileInputFormat 会得到一个路径参数,这个路径内包含了所需要处理的文件, FileInputFormat 会读取这个文件夹内的所有文件(译注:默认不包括子文件夹内 的),然后它会把这些文件拆分成一个或多个的 InputSplit。你可以通过 JobConf 对象的 setInputFormat()方法来设定应用到你的作业输入文件上的输入格式。下表 给出了一些标准的输入格式: 默认的输入格式是 TextInputFormat,它把输入文件每一行作为单独的一个记录, 但不做解析处理。这对那些没有被格式化的数据或是基于行的记录来说是很有用的, 比如日志文件。更有趣的一个输入格式是 KeyValueInputFormat,这个格式也是把 输入文件每一行作为单独的一个记录。然而不同的是 TextInputFormat 把整个文件 行当做值数据,KeyValueInputFormat 则是通过搜寻 tab 字符来把行拆分为键值对。 这在把一个 MapReduce 的作业输出作为下一个作业的输入时显得特别有用,因为 默认输出格式(下面有更详细的描述)正是按 KeyValueInputFormat 格式输出数据。 最后来讲讲 SequenceFileInputFormat,它会读取特殊的特定于 Hadoop 的二进制 文件,这些文件包含了很多能让 Hadoop 的 mapper 快速读取数据的特性。 Sequence 文件是块压缩的并提供了对几种数据类型(不仅仅是文本类型)直接的 序列化与反序列化操作。Squence 文件可以作为 MapReduce 任务的输出数据,并 且用它做一个 MapReduce 作业到另一个作业的中间数据是很高效的。

3.10 容错性 使用 Hadoop 来运行你的作业的其中一个主要原因就是它的高容错性,就算在由高 失败率的节点或网络组成的大集群内运行的作业,Hadoop 都可以让作业成功完成。 Hadoop 实现容错的主要方法就是重新执行任务,单个任务节点(TaskTracker)会不 断的与系统的核心节点(JobTracker)进行通信,如果一个 TaskTracker 在一定时 间内(默认是 1 分钟)无法与 JobTracker 进行通信,那 JobTracker 会假设这个

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 35 / 38


TaskTracker 出问题挂了,JobTracker 了解给每个 TaskTracker 赋予了那些 map 和 reduce 任务。 如果作业仍然在 mapping 阶段,其它的 TaskTracker 会被要求重新执行所有的由前 一个失败的 TaskTracker 所执行的 map 任务。如果作业在 reduce 阶段,则其它的 TaskTracker 会被要求重新执行所有的由前一个失败的 TaskTracker 所执行的 reduce 任务。 Reduce 任务一旦完成会把数据写到 HDFS。因此,如果一个 TaskTracker 已经完成 赋予它的 3 个 reduce 任务中的 2 个,那只有第三个任务会被重新执行。Map 任务 则更复杂一点:即使一个节点已经完成了 10 个 map 任务,reducer 仍可能无法获 取这些 map 任务的所有的输出。如果此时节点挂了,那它的 mapper 输出就不可 访问了。所以已经完成的 map 任务也必须被重新执行以使它们的输出结果对剩下 的 reducing 机器可用,所有的这些都是由 Hadoop 平台自动操作完成的。 这个容错性强调需要程序的执行没有副作用影响,如果 Mapper 和 Reducer 有自身 的标识并和外部有通信,那重新执行一个任务可能需要其它节点去和新的 map 或 reduce 任务实例进行通信,并且重启的任务可能需要重建它们的中间状态。这个 过程是很复杂的并且容易出错。MapReduce 通过去除任务标识或任务间的通信而 大大简化了这个问题。单个任务只能看到它自己的输入和输出,这样就使得错误与 重启过程变成清晰可靠。 推测性的执行(Speculative execution):Hadoop 系统有一个问题,它把任务分 派到很多个节点,其中很有可能有一些慢的节点会限制剩下程序的执行速度。举个 例子,如果有个节点内有一个比较慢的磁盘控制器,那它读取输入数据的速度可能 只有所有其它节点的速度的 10%。所以当 99 个 map 任务都已经完成了,系统仍在 等待最后那个比较耗时的 map 任务完成。 通过强迫任务独立运行于其它的任务,使得单个任务之间不会知道它们的输入数据 来自哪里。任务相信 Hadoop 平台会派送合适的输入到它们那里。因此,对于相同 的输入数据,我们可以并行多次处理以利用不同机器的负载能力。因为作业中大多 数的任务都已经完成了,Hadoop 平台会在几个空闲的节点上调度执行剩余任务的

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 36 / 38


拷贝,这个过程叫做推测性的执行。当任务完成时,它会向 JobTracker 通告。任 何一个首先完成的拷贝任务将成为权威拷贝,如果其他拷贝任务还在推测性的执行 中,Hadoop 会告诉 TaskTracker 去终止这些任务并丢弃它们的输出,接着 Reducer 会从首先完成的 Mapper 那里获取输入数据。 推测性的执行默认是启用的,你可以通过设置 JobConf 中的 mapred.map.tasks.speculative.execution 和 mapred.reduce.tasks.speculative.execution 为 false 来禁用 mapper 和 reducer 的推 测性的执行。

3.11 额外的 MapReduce 功能 插入了 Combiner 的 MapReduce 数据流

Combiner:前面展示的流水线忽略了一个可以优化 MapReduce 作业所使用带宽的 步骤,这个过程叫 Combiner,它在 Mapper 之后 Reducer 之前运行。Combiner 是 可选的,如果这个过程适合于你的作业,Combiner 实例会在每一个运行 map 任务 的节点上运行。Combiner 会接收特定节点上的 Mapper 实例的输出作为输入,接

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 37 / 38


着 Combiner 的输出会被发送到 Reducer 那里,而不是发送 Mapper 的输出。 Combiner 是一个“迷你 reduce”过程,它只处理单台机器生成的数据。 词频统计是一个可以展示 Combiner 的用处的基础例子,上面的词频统计程序为每 一个它看到的词生成了一个(word,1)键值对。所以如果在同一个文档内“cat” 出现了 3 次,(”cat”,1)键值对会被生成 3 次,这些键值对会被送到 Reducer 那里。通过使用 Combiner,这些键值对可以被压缩为一个送往 Reducer 的键值对 (”cat”,3)。现在每一个节点针对每一个词只会发送一个值到 reducer,大大减 少了 shuffle 过程所需要的带宽并加速了作业的执行。这里面最爽的就是我们不用 写任何额外的代码就可以享用此功能!如果你的 reduce 是可交换及可组合的,那 么它也就可以作为一个 Combiner。你只要在 driver 中添加下面这行代码就可以在 词频统计程序中启用 Combiner。 139

conf.setCombinerClass(Reduce.class);

Combiner 应是 Reducer 接口的实例,如果你的 Reducer 由于不可交换或不可组合 不能作为 Combiner,你仍可以写一个第三方类来作为你的作业的 Combiner。

OSSEZ.COM-v1.0-技术模板简易版.ott 版权所有 © OSSEZ LLC 2006 - 2012

2012-09-10 38 / 38


Hadoop 教程  

Hadoop 的使用教程

Advertisement
Read more
Read more
Similar to
Popular now
Just for you