当前位置:文档之家› Yahoo的Hadoop教程

Yahoo的Hadoop教程

Yahoo的Hadoop教程
Yahoo的Hadoop教程

第一章:教程介绍

原文地址:https://www.doczj.com/doc/805137447.html,/hadoop/tutorial/module1.html

译者:Koala++ / 屈伟

介绍

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

本章的目标:

1.理解Hadoop适用的问题范围。

2.理解Hadoop在看待这些问题时与其它分布式系统的不同。

大纲

1.介绍

2.本章的目标

3.大纲

4.适用的问题范围

1.大规模数据的挑战

2.摩尔定律

5.Hadoop方法

1.与现有其它技术比较

2.数据分布

3.MapReduce:隔离的进程(s)

4.水平的可扩展性(flat scability)

6.教程的其它部分

适用问题范围

Hadoop是一个大规模分布式批处理架构,虽然它在单台计算机上也能使用,但它的真正能力是在成百上千计算机上运行时才显现出来,Hadoop可以高效地将大量工作高效地分布到一组计算机上。

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

大规模的挑战

进行大规模计算是很困难的,要处理大规模数据需要将数据分布到多台机器上并行处理,第当多台机器之间需要协作时,失败的几率就很升高。在单台机器环境中,失败并不是设计者经常关心的问题,因为机器崩溃了,反正是无法将程序恢复的。

但在分布式环境中,局部失败是经常会发生的,比如网络会因为交换机和路由器崩溃局部失败或全部失败;数据有可能因为意外的网络阻塞没有按时到达特定结点;运算结点可能因为过热导致磁盘崩溃或用完了内存或磁盘空间;数据可能出错,也可能传输时发生错误;不同实现或不同版本软件在使用协议时有细微的差别;时钟可能变的不同步;锁文件可能没释放;可能在分布式不可打断的传输时受到干扰,导致网络连接中途断开,等等。在上述每一种情况下,分布式系统的正常工作部分应该可以从失败中恢复,或使用户不需要关心这些错误,也可以继续正常工作。显然,提供这种弹性是软件工程的巨大挑战。

不同的分布式系统会着重处理不同的几种失败,而不太关注另外的失败类型。Hadoop没有提供安全模型,也没有防止恶意插入数据的安全机制,比如它无法控制一个在结点间的攻击,然而,它在硬件失败和数据阻塞方面的设计非常健壮,其它的分布式系统数据它们所要处理的问题(比如,高安全性)需求做出了不同的权衡。

在考虑上述的bugs和挑战之外,我们还需要意识到,计算硬件都只有有限的资源,主要的资源包括:

1.处理器时间

2.内存

3.磁盘空间

4.网络带宽

单台计算机通常只有几G内存,如果输入数据是TB级的,那就需要上千台计算机才能将这些数据放入内存,即便如此,但是单台计算机仍无法处理和寻址这些数据。

磁盘的空间要多的多,单台机器的磁盘空间现在有几TB左右,但在大规模计算中,计算机产生的中间数据通常是输入数据的几倍,这也就将占有输入数据的几倍磁盘空间。在处理过程中,一些结点磁盘满了,分布式系统也许需要把这些数据传输到其它可以保存这些溢出数据的结点上去。

最后,即使在内网名,带宽也仍是稀缺资源。假设一组结点用千兆以太网连接,也许它们之间的有很高的传输速度,但如果所有的结点同时传输几G的数据集合,这样很容易使交换机带宽饱和。另外,如果机器都分在多个主机架中,那么可用以数据传输的带宽就会更少,更严重的是,用这个信道的RPC请求或其它数据传输请求会延时或被丢弃。

故一个成功的分布式系统必须能高效地使用上述资源,更重要的是,它在分配这些资源时,应将系统视为一个整体,将尽可能多的时间用于核心计算。

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

摩尔定律

为什么要用一个分布式系统呢?它们听起来麻烦比价值大,随着计算机硬件的快速的设计脚步,似乎单台计算机的硬件会提高到处理更大的数据量,毕竟摩尔定律(以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分钟。

Hadoop方法

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

与现有方法比较

在大规模数据上进行分布式计算以前也是有的。Hadoop的独特之处在于它简单的编程模型,它可使用户很快地编写和测试分布式系统,和它高效,自动分布数据和工作到不同机器的方式,以及利用多核并行处理。

计算机的网络调度可以用其它系统,比如Condor,但Condor不能自动分布数据:必须管理一个单独的SAN,不止如此,多个结点的协作也必须管理交互系统,如MPI,这种编程模型极难掌握,并且会引起一些难以理解的错误。

数据分布

在Hadoop集群中,数据被分布到集群的各个结点,Hadoop Distributed File System (HDFS)将大的数据文件分成块,将这些块交由集群中的结点处理,并且每个块都会复制到不同的几个机器上,所以一台机器崩溃不会导致数据丢失,监测系统会对结点崩溃做出反应,重新复制数据,即部分存储(partial storage)。即便文件块被复制分布到不同的机器,但它们形成了一个单一的命名空间,所以它们的内容还是全局可访问的。

在Hadoop的编程框架中,概念上来说,数据是面向记录的。每个输入文件都被以行或其它特定的应用逻辑格式分开。集群中的每个结点的每个进程都会处理这分开文件的一部分,Hadoop框架再根据分布式文件系统的信息调试进程到数据/记录的位置,因为文件以块的形式存在于分布式文件系统中,每个结点上的每个计算进程处理数据的一个子集。哪些数据要被一个结点处理是由数据本身的存放位置决定的。大部分数据是直接地从磁盘读入CPU,这减少了网络带宽的限制,也防止了不必要的网络传输。这种策略是将计算移动到数据,而不是将数据移动到计算,这使得Hadoop有丰很高的数据本地性,从而它就有着很高的性能。

图1.1 在数据载入时数据分布到各个结点

MapReduce:隔离的进程

Hadoop限制了进程的通信量,因为每个任务所处理的单个记录与其它记录无关,这初看起来似是一个极大的限制,但它是整个框架更可靠,Hadoop不会运行任意一个程序并将它分布到整个集群,程序必须符合一个特定的编程模型,称为MapReduce。

在MapReduce中,Mapper任务是将记录分开,Mapper的输出会作为Reducer任务的输入,Reducer再将不同的Mapper结果合并。

在一个Hadoop集群中,不同的结点仍会相互通信,但是与其它传统的分布式系统通信有所不同,传统的作法是应用设计者要显式地通过socket或MPI缓冲将字节流从一个结点传到另一个结点,但在Hadoop上的通信是隐式的,数据片断都有着key值,通过它Hadoop就知道如何将相关信息位发送到一个目标结点。Hadoop在内部管理数据传输和集群拓扑。

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

图1.2 在结点上运行的Mapping和Reducing任务,其中记录也在图中有显示

水平的可扩展性

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

的程序在从十台机器的级别到成百上千台机器需要大量的重构工作,这也许要程序重写几次,并且其它框的基础元素会限制应用的规模大小。

但是特别设计的Hadoop有着水平的可扩展性,一个Hadoop程序写完后,在10个结点上运行,如果迁徙到更大的集群上运行,几乎不需要做什么工作,Hadoop平台会管理数据和硬件资源并提供与可用资源成比较的可靠性能。

第二章:Hadoop分布式文件系统

原文地址:https://www.doczj.com/doc/805137447.html,/hadoop/tutorial/module2.html

译者:Koala++ / 屈伟

介绍

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

本章目标

1. 理解HDFS的基本设计,以及它与基本的分布式系统概念的关系。

2. 学习如何通过命令行建立和使用HDFS。

3. 学习在你的应用上使用HDFS。

大纲

1.介绍

2.本章目标

3.大纲

4.分布式文件系统基础

5.配置HDFS

6.与HDFS交互

1.常用操作示例

2.HDFS命令行参考

3.HFSAdmin命令行参考

7.在MapReduce中使用HDFS

8.编程中使用HDFS

9.HDFS权限和安全性

10.其它HDFS任务

1.块的重新负载

2.拷贝大的文件集合

3.停止结点运行

4.检验文件系统正常度

5.对主机架的考虑

11.HDFS Web接口

12.参考

分布式文件系统基础

分布式文件系统是设计来保存大数据量的数据,并提供客户端可访问分布在网络上数据的方法。有许多分布式系统以不同的方法来解决这个问题。

NFS,Network File System,是最常见的分布式文件系统,它也是仍然在使用的最古老的分布式文件系统,它的设计非常直观,便有很大的局限性,NFS提供远程访问单台机器的单个逻辑卷的能力,一个NFS服务器将它本地的对外部客户端可见,客户端可以将这个远程的文件系统直接挂载到它们自己的Linux文件系统上,再以对本地磁盘相同的方式访问它。

这种模型一个最大的优点是它的透明性,客户端并不需要特别关注它们是在操作远程的数据文件,标准的库函数如open,close,fread等等,都可通过NFS使用。

但作为一个分布式文件系统,它的能力很有限,一个NFS的文件都要存在一台机器上,这意味着它只能保存一台机器所能保存的数据量,并且不提供这台机器崩溃的可靠性保证(比如通过复制文件到其它服务器上),如果大量客户端要操作数据,会使服务器压力很重,并且客户端必需总是将数据读到它们本地机器上才能操作。

HDFS的设计相对其它分布式文系统,比如NFS,对几个问题是健壮的,特别是:

1.HDFS设计用来保持非常大数据量的信息,这需要将数据分布到大量的机器上,它支持比NFS大得多的文件大小。

2.HDFS的数据保存是可靠的,如果集群中的机器工作不正常,数据仍然是可用的。

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.1 数据结点保存着多个文件的块,复制数为2,名字结点中保存着文件名与块id的关系

大多数基于块的文件系统的块大小为4KB或8KB,与之相比,HDFS中默认使用块大小为64MB,比前者大出几个数量级。这使得HDFS减少了对每个文件元数据的存储量,不只如些,通过很大量数据连续化存放在磁盘上,就可以快速地流式读取数据,这种设计的结果是HDFS倾向于处理大文件,并顺序地读取不同于诸如NTFS 或EXT。这些要处理许多小文件的文件系统。HDFS倾向于保存大量超大文件,每个几百M或几G,因为100MB 的文件才不过能分成2个块,在你计算机上的文件也许经常被随机访问,即应用程序从一个文件中不同位置读取少量信息,而不是顺序访问,相反,HDFS希望程序从开始顺序读到结尾的方式读,这使它在第四章所介绍的MapReduce形式的编程中特别有用。

HDFS是在一些机器中以块的形式保存文件,但这些文件并不是普通文件系统的一部分,在运行Hadoop服务的数据结点上输入ls命令,可以显示普通文件系统的内容,但它不能显示HDFS中的文件,这是因为HDFS

在一个不同的命名空间中运行,它与本地文件内容是隔离的,HDFS中的文件(更准确性,组成这些文件的块)是保存在数据结点服务管理的一个特定目录下,这些文件没有名字,只有块id,你无法通过Linux文件命令与HDFS中的文件交互。

但HDFS有它自己的文件管理命令,并且还与你熟悉的Linux文件命令很相似,本章将在后面部分向你介绍这些命令。

文件系统可靠保存它的元数据是很重要的,更准确地说,因为文件数据是以一次写入,多次读取的方式访问的,元数据结构(比如:文件名,目录名)会被大量客户端同时修改,元数据结构信息户不使去同步是很重要的,所以这些只由一台机器来处理,它被称为名字结点(NameNode),保存着文件系统中所有元数据。因为每个文件都有较少的元数据(它只保存文件名,权限,和文件每块的位置),所有的元数据都保存在NameNode的内存中,可以进行快速访问元数据。

要打开一个文件,客户端可从NodeNode取得该文件每个块的位置,即是哪些结点保存着这个文件的块,客户端再直接从DataNode中读取每个块,这个过程可以并行读取,NameNode并不直接参与块数据传输,这可使它的压力减至最小。

当然,即使名字结点失败名字结点的信息也必须保存,所以有多个冗余系统可以使名字结构完全崩溃时,也可以保存元数据信息。名字结点崩溃比如数据结点崩溃更严重,当单个数据结点崩溃,整个集群也会正常运行,但名字结点崩溃后,集群将不可访问,除非名字结点手工恢复,幸运的是名字结点参与相比是非常少的,所以它崩溃的可能也远低于任一数据结点。

配置HDFS

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

首先要把Hadoop下载并解压,第五章会介绍Hadoop如何运行,第七章介绍如何建立一个大的集群,并为Hadoop做一些预设置,包括下载依赖的软件。

HDFS配置文件是一组XML文件,它们位于Hadoop的配置目录conf下,conf目录在Hadoop的安装目录下(即解压Hadoop后产生的目录)。conf/hadoop-defaults.xml文件中包含了Hadoop中的所有参数默认值,这个文件一般视为是只读的,你可以在conf/hadoop-site.xml中设置新的值,以覆盖默认值,这个文件会被复制集群中的所有机器上。

配置设置是以一组键-值对的格式:

property-name

property-value

在配置中加入true可防止属性被别的应用覆盖。这在大多系统范围配置选项中是有用的。

下列的设置在HDFS配置中是必需的:

key value example

https://www.doczj.com/doc/805137447.html, protocol://servername:port hdfs://https://www.doczj.com/doc/805137447.html,:9000 dfs.data.dir pathname/home/username/hdfs/data

https://www.doczj.com/doc/805137447.html,.dir pathname/home/username/hdfs/name

这些设置的描述如下:

https://www.doczj.com/doc/805137447.html,,这是集群名字结点的URI(协议,主机名,端口)。Hadoop系统中的结点都需要知道名字结点的地址才能进行操作,数据结点要与这个名字结点一起注册,才吏它们的数据通过名字结点变为可用的,客户端要通过个地址取得文件块的真正存放地址再去取数据。

dfs.data.dir,这是数据结点保存数据的本地文件系统的路径,因为它们将运行在不同的机器上,并不需要所有的数据结点都将数据保存到相同前缀的本地路径,集群中的机器是不同的,也是可行的。但如果这个目录在整个系统都是统一的,那么配置也就简单些,Hadoop默认将dfs.data.dir设为/tmp。这在测试时是没关系的,但在生产环境中,这样做很容易丢失数据,所以必需覆盖。

https://www.doczj.com/doc/805137447.html,.dir,这是名字结点保存元数据的本地路径,它仅用于名字结点找到它自己的信息,它在数据结点上不存在,它的默认值也是/tmp,所以它也必须覆盖。

另一个上面没有提及的参数dfs.replication,它是文件系统中每个块的默认复制数,在生产环境集群中,一般就用它的默认值3,你可以随意增加复制数,但它会无谓地用去很多空间,如果把它设小,会影响数据的可用性和可靠性。

在单结点上的配置中,可以直接把下面的配置复制到hadoop-site.xml中(译者注:这是0.18版的做法,在0.20下不会成功,日后我会更新到0.20的方法的)。

https://www.doczj.com/doc/805137447.html,

hdfs://https://www.doczj.com/doc/805137447.html,:9000

dfs.data.dir

/home/username/hdfs/data

https://www.doczj.com/doc/805137447.html,.dir

/home/username/hdfs/name

当然,https://www.doczj.com/doc/805137447.html,和username是需要修改的,而使用端口9000是任意选择的。

将以上配置拷入con/hadoop-site.xml文件后,将con/目录拷到集群中所有机器上。主结点需要知道所有数据结点机器的地址,因为启动脚本依赖它,同样在con/目录中,编辑文件子结点,使它包含子结点的完整主机名列表,每行一个主机名,主结点(比如localhost)通常不在这个文件中出现。

然后创建下面的目录

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

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

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

启动HDFS

现在我们需要格式化我们设置的文件系统:

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

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

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

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

与HDFS交互

这节将向你介绍与HDFS交互的命令,这些命令可以载入数据,取得数据,操作文件。

所有与集群交互的命令是通过一个脚本bin/hadoop来进行的,它可以用Java虚拟机载入Hadoop系统,来执行用户命令,命令的格式如下:

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

modulteName是要使用的Hadoop功能的模块名,-cmd是用这个模块的命令,它的参数在命令名之后。

有两个模块与HDFS相关:dfs和dfsadmin,它们的用途将在下面描述。

常用的操作示例

dfs模块也被称为“FsShell”,它提供基本的文件操作功能,下面将进行介绍。

只有集群有有用信息时,这个集群才是有用的。所以首先要进行的操作是载入数据到集群,在示例中,我们假设用户为“someone”,你自己尝试时使用自己的用户名就可以了,同样注意对文件的操作可以是集群中任一一个结点的操作,在这个结点上的conf/hadoop-sicte.xml中fs,https://www.doczj.com/doc/805137447.html,为你集群的名字结点。我们称我们操作的机器为anynode,命令行在你安装Hadoop的hadoop目录下运行,也许它在的机器的路径是

/home/someone/hadoop,在别的机器上是/home/foo/hadoop,开始介绍的命令主要是载入数据,查看它是否在那里,再从HDFS中取回数据。

列出文件

如果我们现在就尝试去查看HDFS,我们什么也不会看到:

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

someone@anynode:hadoop$

ls命令什么也没有返回。不带参数,-ls将列出HFFS的主目录下的文件,不要忘记它和/home/$USER不是同一目录(HDFS与本地文件不在一个命名空间中),HDFS中没有当前工作目录这个概念,也没有cd命令。

如果提供-ls参数,你会看到一些初始目录的内容:

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系统将必要的数据移到不同的结点,细节在第四章中解释。

载入数据到集群

因为在一般的Unix或Linux系统中,用户的文件保存在/home/$USER中,所以HDFS将文件保存在

/user/$USER,对于一些如ls的命令,需要用户给出目录名却没有给出时,就默认/home/$USER目录(有的命令要求明确指名源路径和目标路径),在HDFS或Hadoop MapReduce中,参数中所有的相对路径,或系统的其它地方,都假设相对的是/home/$USER目录。

第一步:如果你的主目录不存在,先创建它。

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

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

然后我们可以创建你的主目录了。

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

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

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

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

这个命令会将/home/someone/interestingFile.txt从本地目录拷贝到HDFS的

/user/yourUserName/interestingFile.txt。

第三步:检查在HDFS中的文件,我们可用下面任一命令来查看文件。

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

/user/$USER/bar

exists in HDFS

bin/hadoop dfs -put foo bar

/user/$USER/bar is a

directory

Uploads local file foo to a file named

/user/$USER/bar/foo

bin/hadoop dfs -put foo

somedir/somefile

/user/$USER/somedir

does not exist in HDFS

Uploads local file foo to a file named

/user/$USER/somedir/somefile, creating

the missing directory

bin/hadoop dfs -put foo bar

/user/$USER/bar is

already a file in HDFS

No change in HDFS, and an error is

returned to the user.

当用put命令操作一个文件,这个操作或是全部完成,或是一点不做。一个文件上传到HDFS,首先要把数据复制到数据结点,当数据结点表明它们已经接收了全部的数据,并且文件句柄也已经关闭,那么这时文件才对系统其余部分是可见的,所以根据put命令的返回值,你可以确定文件或是成功上传,或是“完全失败”,你将永远不会得到一个文件部分上传或是文件内容部分可见的状态,如果遇到上传断开连接,或上传内容未完成的情况,HDFS表现地就像没有上传发生一样。

第四步:一次上传多个文件。put命令可以一次操作多个文件,还可以上传整个目录到HDFS中。

创建一个目录,然后用cp命令拷贝几个文件到这个目录中,在示例中,我们有如下目录内容:

someone@anynode:hadoop$ ls -R myfiles

myfiles:

file1.txt file2.txt subdir/

myfiles/subdir:

anotherFile.txt

someone@anynode:hadoop$

整个myfiles/目录可以用下面命令拷贝到HDFS中:

someone@anynode:hadoop$ bin/hadoop -put myfiles /user/myUsername

someone@anynode:hadoop$ bin/hadoop -ls

Found 1 items

/user/someone/myfiles

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 186731 2008-06-12 20:59 rw-r--r-- someone supergroup

/user/someone/myfiles/file2.txt 168 2008-06-12 20:59 rw-r--r-- someone supergroup

/user/someone/myfiles/subdir

2008-06-12 20:59 rwxr-xr-x someone supergroup

上面展示了整个目录被正确地递归上传上,你会注意到除了文件路径外,ls命令还显示了每个文件的复制数(在中的1),文件大小,上传时间,权限,所有者信息。

另一个与put功能相同的命令-copyFromLocal,它在使用方法上与put完全一样。

从HDFS中取得数据

有多种多HDFS中取得数据的方法,最简单的方法是用cat命令将文将内容输出到标准输出(当然它也可以输出到管道用于其它应用或目标文件)。

第一步:用cat显示文件。

假设你已经上传了一个foo文件到HDFS的主目录,你可以用以下命令显示它的内容:

someone@anynode:hadoop$ bin/hadoop dfs -cat foo

(contents of foo are displayed here)

someone@anynode:hadoop$

第二步:将一个文件从HDFS拷贝到本地文件系统。

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

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)

停止HDFS

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

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

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

HDFS命令参考

除介绍的命令外,还有许多bin/hadoop.dfs命令,以上介绍的只是帮助你开始使用HDFS,运行bin/hadoop dfs 不带任何参数会列出所有FsShell系统提供的命令,当你遇到问题时执行bin/hadoop dfs –help commandName会显示这个命令的用法。

下面是所有命令的介绍,介绍之前先定义一下参数的意义:

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相似,但它还显示全部文件或目录磁盘使用情况

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

在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命令的使用信息,你需要把命令的“-”去掉。

DFSAdmin命令参考

dfs模块提供了常用的文件或目录操作,但它们都是对文件系统内部的操作,dfsadmin模块是对整个文件系统操作或查询,我们下面将介绍它。

得到全局状态。一个简洁的HDFS状态状态报告可以用bin/hadoop dfsadmin –report得到,它返回的是HDFS 集群的基本健康信息。

更详细的状态。如果你想知道名字结点元数据状态的更多信息,你可以使用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命令,使它继续进行(注意,在使用这个命令时,你最好知道你在干什么)。

当HDFS升级后,Hadoop保留了允许你降级到以前HDFS版本的信息,以防你想回到以前的Hadoop版本,如果你要降级就先停止集群,重装旧版的Hadoop,再运行bin/start-dfs.sh –rollback,它就会回到以前版本的HDFS 状态。

每次只能保存一个存档拷贝,所以在新版本下运行几天后(当感觉它稳定后),可以用bin/hadoop dfsadmin –finalizeUpgrade命令删除存档拷贝,而rollback命令将不再可用,但这也是进行第二次升级必要的一个工作。

获得帮助。与dfs中一样,输入bin/hadoop dfsadmin –help cmd命令会显示这个命令的用法。

MapReduce基础

这篇文章翻译自Yahoo的Hadoop教程,很久之前就看过了,感觉还不错。最近想总结一下以前学的东西,看到现在关于Hadoop的中文资料还比较少,就有了把它翻译出来的想法,希望能帮助到初学者。这只是Yahoo 的Hadoop教程中很小的一部分,我下去还会选一些来翻译,一般来说只翻译讲原理的,操作类的就不翻译了。水平有限,不妥之处,请指正(感觉翻译过来后,读起来倒是没英文的顺畅:)。废话说完,正文开始:

函数式编程概念

MapReduce程序是设计用来并行计算大规模海量数据的,这需要把工作流分划到大量的机器上去,如果组件(component)之间可以任意的共享数据,那这个模型就没法扩展到大规模集群上去了(数百或数千个节点),用来保持节点间数据的同步而产生的通信开销会使得系统在大规模集群上变得不可靠和效率低下。

实际上,所有在MapReduce上的数据元素都是不可变的,这就意味着它们不能够被更新。如果在一个mapping任务中你改变了一个输入键值对,它并不会反馈到输入文件;节点间的通信只在产生新的输出键值对((key,value)pairs)时发生,Hadoop系统会把这些输出传到下一个执行阶段。

列表处理(List Processing)

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

Mapping数据列表(Lists)

MapReduce程序的第一步叫做mapping,在这一步会有一些数据元素作为Mapper函数的输入数据,每次一个,Mapper会把每次map得到的结果单独的传到一个输出数据元素里。

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

Reducing数据列表(Lists)

Reducing可以让你把数据聚集在一起。reducer函数接收来自输入列表的迭代器,它会把这些数据聚合在一起,然后返回一个输出值。

图4.2 通过列表迭代器对输入数据进行reducing操作来输出聚合结果。

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

把它们一起放在MapReduce中

Hadoop的MapReduce框架使用了上面的那些概念并用它们来处理大规模的数据信息。MapReduce程序有着两个组件:一个实现了mapper,另一个实现了reducer。上面描叙的Mapper和Reducer术语在Hadoop 中有了更细微的扩展,但基本的概念是相同的。

键和值:在MapReduce中,没有一个值是单独的,每一个值都会有一个键与其关联,键标识相关的值。举个例子,从多辆车中读取到的时间编码车速表日志可以由车牌号码标识,就像下面一样:

AAA-123 65mph, 12:00pm

ZZZ-789 50mph, 12:02pm

AAA-123 40mph, 12:05pm

CCC-456 25mph, 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操作之间是独立执行的。

图4.3 不同颜色代表不同的键,有着相同键的数值都被传到同一个reduce任务里。

应用例子:词频统计(Word Count)

写一个简单的MapReduce程序就可以用来统计不同的词在一个文件集中出现的次数。比如,我们有这样的文件:

foo.txt: Sweet, this is the foo file

bar.txt: This is the bar file

我们期望输出会是这样子:

sweet 1

this 2

is 2

the 2

foo 1

bar 1

file 2

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

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)

列表4.1 MapReduce词频统计伪代码

若干个mapper函数的实例会被创建在我们的集群的不同机器上,每个实例接收一个不同的输入文件(这里假设我们有很多个文件)。mappers输出的(word,1)键值对会被转到reducers那里去。若干个reducer 方法实例也会在不同机子上被实例化。每个reducer负责处理关联到不同词的数值列表,数值列表中的值都是1;reducer把这些“1”值总和到一个关联了某个词的最终计数里。reducer然后生成最终的(word,count)输出,并把它写到一个输出文件里。

针对这个,我们可以在Hadoop MapReduce中写一个很相似的程序;它被包括在Hadoop分发包中,具体在src/examples/org/apache/hadoop/examples/WordCount.java。它的部分代码如下:

public static class MapClass extends MapReduceBase

implements Mapper {

private final static IntWritable one = new IntWritable(1);

private Text word = new Text();

public void map(LongWritable key, Text value,

OutputCollector 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 {

public void reduce(Text key, Iterator values,

OutputCollector output,

Reporter reporter) throws IOException {

int sum = 0;

while (values.hasNext()) {

sum += values.next().get();

}

output.collect(key, new IntWritable(sum));

}

}

列表4.2 MapReduce词频统计Java源码

实际Java实现与上述伪代码之间有一些微小的差别。首先,Java没有原生的emit关键字;你得到的OutputCollector输入对象会接收数值并emit到下一执行阶段。第二,Hadoop使用的默认输入格式把输入文件的每一行作为mapper单独的一个输入,不是一次整个文件。其中还使用了一个StringTokenizer对象用来把一行数据拆分为词组。这个操作没有对输入数据做任何规格化处理,所以“cat”,“Cat”,“cat,”都被认为是不同的字符串。注意,类变量word在每一次mapper输出另外一个(word,1)键值对时都被重复使用;这个举措节省了为每个输出创建一个新的变量的时间。output.collect()方法会拷贝它收到的数值作为输入数据,所以你可以覆盖你使用的变量。

驱动方法

Hadoop MapReduce程序的最后一个组件叫做Driver,它会初始化Job和指示Hadoop平台在输入文件集合上执行你的代码,并控制输出文件的放置地址。下面是Hadoop自带的Java实现例子里的一个整理版本driver的代码:

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);

}

列表4.3 Hadoop MapReduce词频统计驱动器

这个方法建立了一个在给定输入文件夹(inputPath参数)里的文件上执行词频统计程序的作业(Job)。reducers的输出被写到outputath指定的文件夹内。用于运行job的配置信息保存在JobConf对象里。通过setMapperClass()和setReducerClass()方法可以设定mapping和reducing函数。reducer生成的数据类型由setOutputKeyClass()和setOutputValueClass()方法设定。默认情况下假定这些也是mapper的输出数据类型。如果你想设定不同的数据格式的话,可以通过JobConf的setMapOutputKeyClass()和setMapOutputValueClass()方法设定。mapper的输入数据类型由InputFormat控制。输入格式在这里有详细的讨论。默认的输入格式是“TextInputFormat”,它会以(LongWritable,Text)键值对的方式加载数据。long值表示某一行在文件中的偏移量,Text对象则保存某一行的字符串内容。

通过调用JobClient.runJob(conf)即可向MapReduce提交job,这个调用会阻塞直到job完成。如果job 失败了,它会抛出一个IOException。JobClient还提供了一个非阻塞版本的调用方法submitJob()。

MapReduce数据流(一)

在上一篇文章中我们讲解了一个基本的

一起工作时如下图所示:

图4.4高层MapReduce工作流水线

MapReduce的输入一般来自HDFS中的文件,这些文件分布存储在集群内的节点上。运行一个MapReduce 程序会在集群的许多节点甚至所有节点上运行mapping任务,每一个mapping任务都是平等的:mappers 没有特定―标识物‖与其关联。因此,任意的mapper都可以处理任意的输入文件。每一个mapper会加载一些存储在运行节点本地的文件集来进行处理(译注:这是移动计算,把计算移动到数据所在节点,可以避免额外的数据传输开销)。

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

近距离观察

在上一图中,描述了Hadoop MapReduce的高层视图。从那个图你可以看到mapper和reducer组件是如何用到词频统计程序中的,它们是如何完成它们的目标的。接下来,我们要近距离的来来看看这个系统以获取更多的细节。

图4.5细节化的Hadoop MapReduce数据流

图4.5展示了流线水中的更多机制。虽然只有2个节点,但相同的流水线可以复制到跨越大量节点的系统上。

下去的几个段落会详细讲述MapReduce程序的各个阶段。

输入文件:文件是MapReduce任务的数据的初始存储地。正常情况下,输入文件一般是存在HDFS里。

这些文件的格式可以是任意的;我们可以使用基于行的日志文件,也可以使用二进制格式,多行输入记录或其它一些格式。这些文件会很大—数十G或更大。

输入格式:InputFormat类定义了如何分割和读取输入文件,它提供有下面的几个功能:

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

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

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

Hadoop自带了好几个输入格式。其中有一个抽象类叫FileInputFormat,所有操作文件的InputFormat 类都是从它那里继承功能和属性。当开启Hadoop作业时,FileInputFormat会得到一个路径参数,这个路径内包含了所需要处理的文件,FileInputFormat会读取这个文件夹内的所有文件(译注:默认不包括子文件夹内的),然后它会把这些文件拆分成一个或多个的InputSplit。你可以通过JobConf对象的setInputFormat()方法来设定应用到你的作业输入文件上的输入格式。下表给出了一些标准的输入格式:

默认的输入格式是TextInputFormat,它把输入文件每一行作为单独的一个记录,但不做解析处理。这对那些没有被格式化的数据或是基于行的记录来说是很有用的,比如日志文件。更有趣的一个输入格式是

KeyValueInputFormat,这个格式也是把输入文件每一行作为单独的一个记录。然而不同的是

TextInputFormat把整个文件行当做值数据,KeyValueInputFormat则是通过搜寻tab字符来把行拆分为键值对。这在把一个MapReduce的作业输出作为下一个作业的输入时显得特别有用,因为默认输出格式(下面有更详细的描述)正是按KeyValueInputFormat格式输出数据。最后来讲讲SequenceFileInputFormat,它会读取特殊的特定于Hadoop的二进制文件,这些文件包含了很多能让Hadoop的mapper快速读取数据的特性。Sequence文件是块压缩的并提供了对几种数据类型(不仅仅是文本类型)直接的序列化与反序列化操作。

Squence文件可以作为MapReduce任务的输出数据,并且用它做一个MapReduce作业到另一个作业的中间数据是很高效的。

MapReduce数据流(二)

输入块(InputSplit):一个输入块描述了构成MapReduce程序中单个map任务的一个单元。把一个MapReduce程序应用到一个数据集上,即是指一个作业,会由几个(也可能几百个)任务组成。Map任务可能会读取整个文件,但一般是读取文件的一部分。默认情况下,FileInputFormat及其子类会以64MB(与HDFS 的Block默认大小相同,译注:Hadoop建议Split大小与此相同)为基数来拆分文件。你可以在hadoop-site.xml (译注:0.20.*以后是在mapred-default.xml里)文件内设定mapred.min.split.size参数来控制具体划分大小,或者在具体MapReduce作业的JobConf对象中重写这个参数。通过以块形式处理文件,我们可以让多个map任务并行的操作一个文件。如果文件非常大的话,这个特性可以通过并行处理大幅的提升性能。更重要的是,因为多个块(Block)组成的文件可能会分散在集群内的好几个节点上(译注:事实上就是这样),这样就可以把任务调度在不同的节点上;因此所有的单个块都是本地处理的,而不是把数据从一个节点传输到另外一个节点。当然,日志文件可以以明智的块处理方式进行处理,但是有些文件格式不支持块处理方式。针对这种情况,你可以写一个自定义的InputFormat,这样你就可以控制你文件是如何被拆分(或不拆分)成文件块的。

自定义的文件格式在第五部分有描述。

输入格式定义了组成mapping阶段的map任务列表,每一个任务对应一个输入块。接着根据输入文件块所在的物理地址,这些任务会被分派到对应的系统节点上,可能会有多个map任务被分派到同一个节点上。任务分派好后,节点开始运行任务,尝试去最大并行化执行。节点上的最大任务并行数由

mapred.tasktracker.map.tasks.maximum参数控制。

记录读取器(RecordReader):InputSplit定义了如何切分工作,但是没有描述如何去访问它。

RecordReader类则是实际的用来加载数据并把数据转换为适合mapper读取的键值对。RecordReader实例是由输入格式定义的,默认的输入格式,TextInputFormat,提供了一个LineRecordReader,这个类的会把输入文件的每一行作为一个新的值,关联到每一行的键则是该行在文件中的字节偏移量。RecordReader会在输入块上被重复的调用直到整个输入块被处理完毕,每一次调用RecordReader都会调用Mapper的map()方法。

Mapper:Mapper执行了MapReduce程序第一阶段中有趣的用户定义的工作。给定一个键值对,map()方法会生成一个或多个键值对,这些键值对会被送到Reducer那里。对于整个作业输入部分的每一个map任务(输入块),每一个新的Mapper实例都会在单独的Java进程中被初始化,mapper之间不能进行通信。这就使得每一个map任务的可靠性不受其它map任务的影响,只由本地机器的可靠性来决定。map()方法除了键值对外还会接收额外的两个参数(译注:在0.20.×后的版本,接口已变化,由Context对象代替这两个参数):?OutputCollector对象有一个叫collect()的方法,它可以利用该方法把键值对送到作业的reduce阶段。?Reporter对象提供当前任务的信息,它的getInputSplit()方法会返回一个描述当前输入块的对象,并且还允许map任务提供关于系统执行进度的额外信息。setStatus()方法允许你生成一个反馈给用户的状态消息,incrCounter()方法允许你递增共享的高性能计数器,除了默认的计数器外,你还可以定义更多的你想要的

相关主题
文本预览
相关文档 最新文档