大数据基础学习-2.Hadoop1.0、MapReduce

一、Hadoop1.0

Hadoop是一个由Apache基金会所开发的分布式系统基础架构。Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,MapReduce为海量的数据提供了计算。Hadoop是Doug Cutting根据Google的三篇论文开源出来的。为了能对Hadoop有更好的理解,先从Hadoop1.0开始,再过度到Hadoop2.0。

1.HDFS系统架构

分布式文件系统,Hadoop Distributed File System,简称HDFS。核心作用就是用来存储文件,架构图如下。主要有NameNode、DataNode、secondary namenode和block等概念需要掌握。

http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html#Introduction

1)NameNode

NameNode节点主要是用来保存HDFS的元数据信息,比如命名空间信息,块信息等。在NameNode节点上将会运行名为namenode的进程,该进程运行的时候,HDFS的元数据信息将会被加载到内存中。

• NameNode保存的元信息具体如下:

    • 整个文件系统的目录树

    • 文件名与blockid的映射关系

    • blockid所在的DataNode信息

运行NameNode进程会占用大量内存和I/O资源,一般运行NameNode进程的节点上不会存储用户数据或执行MapReduce任务。

• 元信息持久化

NameNode的元数据也会往磁盘上去存,但是真正对外提供服务的是在内存中的元数据。本地磁盘还存放namenode的备份文件,即fsimage和edits。在系统运行期间,所有对元信息的操作将会更新内存中的元信息,并且这些操作信息将被保存到edits文件中。在下一次NameNode启动时,可以直接通过fsimage和edits将元信息快速加载进内存。具体实现细节将会在secondaryNameNode中进行讲解。

• Hadoop1.0集群只能运行一个NameNode,这会导致单点问题,即该节点一旦宕机,整个集群就无法正常工作。

两种解决方案:

1.将hadoop元数据写入到本地文件系统的同时,再实时同步到一个远程挂载的网络文件系统(NFS),但是这种做法比较烧钱。

2.当NameNode发生故障时,可以通过fsimage恢复数据。而fsimage的更新维护操作需要依赖于secondaryNameNode,这在讲解secondaryNameNode时会具体说明。这里先记住,secondaryNameNode保存的元信息状态总是滞后于NameNode中的元信息,所以这种方式难免会导致丢失部分数据。

• hadoop偏向于存大文件

一般来说,一条元信息记录会占用200byte内存空间。假设块大小为64MB,备份数量是3 ,那么一个1GB大小的文件将占用(1024/64)*3=48个文件块。而1000个1MB大小的文件,则会占用1000*3=3000个文件块。可以看出,如果文件越小,存储同等大小文件所需要的元信息就越多,占用空间越大,所以hadoop偏向于存大文件。

如果集群规模特别大的话,DataNode节点上有很多文件,文件又划分为多个block,block信息完全存在NameNode内存里面肯定是吃不消的。一方面是它不能够持久化,另外一方面不可能永远庞大下去,所以它就制约着整个集群的规模的扩大,只有在hadoop2.0才能解决(使用多个NameNode)。

2)DataNode

为了保证数据的安全性,DataNode上的数据块block一般至少有3个副本,并且副本存储在不同的机器节点上。DataNode功能特性可以总结如下:

• 负责存储数据块,为系统客户端提供数据块的读写服务(注意:不是NameNode提供读写服务)

• 根据NameNode的指示进行创建、删除和复制等操作

• 通过心跳机制,定期向namenode报告数据块列表信息。这里需要注意一下,fsimage中并没有数据块的位置信息,这些数据块信息是在集群启动后,DataNode节点向NameNode节点报告时候才会存在于NameNode的内存中。

• DataNode之间也会进行通信,进行块的副本处理

3)Block

• block是HDFS文件存储的最小单位,2.0默认大小是64M,1.0默认是32M。(可配置)

为什么不能远小于64M(普通文件系统的数据块大小一般为4KB)?

1.减少硬盘寻道时间

HDFS设计前提是支持大容量的流式数据操作,所以即使是一般的数据读写操作,涉及到的数据量都是比较大的。假如数据块设置过少,那需要读取的数据块就比较多,由于数据块在硬盘上非连续存储,普通硬盘因为需要移动磁头,所以随机寻址较慢,读越多的数据块就增大了总的硬盘寻道时间。当硬盘寻道时间比IO时间还要长的多时,那么硬盘寻道时间就成了系统的一个瓶颈。合适的块大小有助于减少硬盘寻道时间,提高系统吞吐量。

2.减少Namenode内存消耗,也就是上文中,hadoop偏向于存大文件的原因。

为什么不能远大于64M?

这里主要从上层的MapReduce框架来讨论。

1.Map进程崩溃问题:系统需要重新启动map进程,启动过程需要重新加载数据,数据块越大,数据加载时间越长,系统恢复过程越长。

2.监管时间问题:主节点监管其他节点的情况,每个节点会周期性的把完成的工作和状态的更新报告回来。如果一个节点保持沉默超过一个预设的时间间隔,主节点记录下这个节点状态为死亡,并把分配给这个节点的数据发到别的节点。对于这个“预设的时间间隔”,这是从数据块的角度大概估算的。假如是对于64MB的数据块,假设10分钟之内数据块使用完毕,然后向主节点汇报情况,如果超过10分钟没反应,就判断该节点的任务没有正常运行。可对于640MB或是1G以上的数据,应该要估算多长的时间?估算的时间短了,那就可能误判节点有问题,估算的时间长了,那等待的时间就过长了。所以对于过大的数据块,这个“预设的时间间隔”不好估算。

3.问题分解问题:数据量大小与解决问题的复杂度是成线性关系的。对于同个算法,处理的数据量越大,它的时间复杂度也就越大。

4.约束Map输出:在Map Reduce框架里,Map处理之后的数据是要经过排序才执行Reduce操作的。想想归并排序算法的思想,对小文件进行排序,然后将小文件归并成大文件的思想,然后就会懂这点了。

4)Secondary NameNode

一般情况下,secondaryNameNode和NameNode需要运行在不同的机器上。SecondNameNode的作用就是为了减少NameNode启动的时间,以及当NameNode挂掉时可以做数据恢复用。具体细节如下:

首先再明确fsimage和edits的含义, fsimage是在NameNode启动时对整个文件系统的快照,edit logs是在NameNode启动后,对文件系统改动的记录。这2个文件存在于NameNode节点上,当NameNode重新启动时,将会读取fsimage,并且重新执行edits中记录的操作信息,所以加载到内存中的元信息就是最新的元信息,也就是在NameNode停止前的状态。

• SecondaryNameNode节点会定期到NameNode去获取edits,此时NameNode上的edits文件停止更新,系统产生edits.new用来记录新的元数据操作信息。这个期间,SecondaryNameNode利用获取的edits更新fsimage(Secondary NameNode节点上的fsimage), 更新完fsimage文件后,将其拷贝回NameNode中,这时edits.new会再变为edits,此时SecondaryNameNode和NameNode上的fsimage就是最新的元数据备份。NameNode在下次重启时会使用这个新的fsimage文件,从而减少重启的时间。SecondaryNameNode不是要取代NameNode也不是NameNode的备份。

2.机架感知策略

1)副本存放策略

为了数据存储的安全,数据的副本将会存在不同的机器和机架上。

• 第一个副本,在客户端相同的节点(如果客户端是集群外的一台机器,就随机选择负载较低的节点)

• 第二个副本,放在不同机架(随机选择)的节点

• 第三个副本,放在与第二个副本同机架但是不同节点上

2)Hadoop中的网络拓扑

当用户访问Hadoop文件时,Hadoop会优先将离用户节点近的数据提供给用户,但是在Hadoop集群中如何衡量两个节点的远近呢?要知道,在高速处理数据时,数据处理速率的唯一限制因素就是数据在不同节点间的传输速度:这是由带宽的可怕匮乏引起的。所以我们把带宽作为衡量两个节点距离大小的标准。

但是计算两个节点之间的带宽是比较复杂的,而且它需要在一个静态的集群下才能衡量,但Hadoop集群一般是随着数据处理的规模动态变化的(且两两节点直接相连的连接数是节点数的平方)。于是Hadoop使用了一个简单的方法来衡量距离,它把集群内的网络表示成一个树结构,两个节点之间的距离就是他们离共同祖先节点的距离之和。树一般按数据中心(datacenter),机架(rack),计算节点(datanode)的结构组织。计算节点上的本地运算速度最快,跨数据中心的计算速度最慢(现在跨数据中心的Hadoop集群用的还很少,一般都是在一个数据中心内做运算的)。

下面是不同情况下两个节点的距离:

• distance(/D1/R1/H1,/D1/R1/H1)=0 相同的DataNode

• distance(/D1/R1/H1,/D1/R1/H2)=2 同一rack下的不同DataNode,距离共同的祖先节点R1都为1,所以两节点距离为2

• distance(/D1/R1/H1,/D1/R1/H4)=4 同一IDC下的不同DataNode,距离共同祖先节点D1都为2,所以两节点距离为4

• distance(/D1/R1/H1,/D2/R3/H7)=6 不同IDC下的DataNode,距离/都为3,所以两节点距离为6

3.数据完整性策略

转:https://blog.csdn.net/andrewgb/article/details/50626086

Hadoop 用户肯定都不希望系统在存储和处理数据时丢失或者损坏任何数据。接下来,我们来考究一下 HDFS 在为了保证数据完整性,所做的工作。

两种检验方法:

– 校验和:  检测损坏数据的常用方法是在第一次进入系统时计算数据的校验和,在通道传输过程中,如果新生成的校验和不完全匹配原始的校验和,那么数据就会被认为是被损坏的。

– 数据块检测程序 DataBlockScanner:   在DataNode节点上开启一个后台线程,来定期验证存储在它上所有块,防止物理介质出现损减情况而造成的数据损坏。

总的来说,HDFS 会对写入的数据计算校验和,并在读取数据时验证校验和。datanode负责收到数据后存储该数据及其校验和。datanode 的数据来源可分为两种,其一为是从客户端收到的数据,其二为从其他 datanode 复制来的数据。还有一种情况,正在写数据的客户端将数据及其校验和发送到由一系列 datanode 组成的管线,管线中最后一个 datanode 负责验证校验和。客户端从 datanode 读取数据时,也会验证校验和,将他们与 datanode 中存储的校验和进行比较。每个 datanode 都持久保存一个用于验证的校验和日志,所以会知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会告诉这个 datanode 来更新日志。对于检测损坏的磁盘很有价值。

不只是客户端读取数据库时会验证校验和,每个datanode也会在一个后台进程中运行一个DataBlockScanner,从而定期验证存储在这个datanode的所有数据库。该措施是解决物理存储媒体上位损坏的有力措施。

HDFS会存储每个数据块的复本,可以通过数据复本来修复损坏的数据块。 客户端在读取数据块时,如果检测到错误首先向 namenode 报告已损坏的数据块及其正在尝试读取操作的这个datanode 。namenode会将这个数据块标记为已损坏,对这个数据块的请求会被 namenode 安排到另一个复本上。之后,它安排这个数据块的另一个复本复制到另一个datanode上,如此,数据块的复本因子又回到期望水平。此后,已损坏的数据块复本会被删除。

4.数据读写

转:http://www.cnblogs.com/beanmoon/archive/2012/12/17/2821548.html

写:

1)首先客户端通过DistributedFileSystem上的create()方法指明一个欲创建文件的文件名(第一步),需要上传的文件会先进行分块(假设切分成2块,block1和block2,每块64M),DistributedFileSystem再通过RPC调用向NameNode申请创建请求(第二步,这时该文件还没有分配相应的block)。

2)namenode检查是否有同名文件存在以及用户是否有相应的创建权限,如果检查通过,namenode 记录block信息,返回可用的datanode ;否则,文件创建失败,客户端得到一个IOException异常。

3)DistributedFileSystem返回一个FSDataOutputStream以供客户端写入数据,FSDataOutputStream封装了一个DFSOutputStream用于处理namenode与datanode之间的通信

4)当客户端开始写数据时(第三步),DFSOutputStream将64M的block1按64k的package划分,放入一个中间队列——数据队列(data queue)中去。DataStreamer从数据队列中取数据,同时向namenode申请一个新的block来存放它已经取得的数据。

5)namenode选择一系列合适的datanode构成一个管道线(pipeline),假设replica为3,所以管道线中就有三个datanode。

6)DataSteamer把数据流式的写入到管道线中的第一个datanode中(第四步),第一个datanode再把接收到的数据转到第二个datanode中(第四步),以此类推。

7)DFSOutputStream同时也维护着另一个中间队列——确认队列(ack queue),确认队列中的包只有在得到管道线中所有的datanode的确认以后才会被移出确认队列(第五步)。

8)当block1写完后,客户端再次请求namenode上传block2,即重复5-7步。

如果某个datanode在写数据的时候当掉了,下面这些对用户透明的步骤会被执行:
1)管道线关闭,所有确认队列上的数据会被挪到数据队列的首部重新发送,这样可以确保管道线中宕掉的datanode下流的datanode不会因为宕掉的datanode而丢失数据包。
2)在还在正常运行的datanode上的当前block上做一个标志,这样当挂掉的datanode重新启动以后namenode就会知道该datanode上哪个block是刚才当机时残留下的局部损坏block,从而可以把它删掉。
3)已经宕掉的datanode从管道线中被移除,未写完的block的其他数据继续被写入到其他两个还在正常运行的datanode中去,namenode知道这个block还处在under-replicated状态(也即备份数不足的状态)下,然后他会安排一个新的replica从而达到要求的备份数,后续的block写入方法同前面正常时候一样。
有可能管道线中的多个datanode宕掉(虽然不太经常发生),但只要dfs.replication.min(默认为1)个replica被创建,我们就认为该创建成功了。剩余的replica会在以后异步创建以达到指定的replica数。
当客户端完成写数据后,它会调用close()方法(第六步)。这个操作会冲洗(flush)所有剩下的package到pipeline中,等待这些package确认成功,然后通知namenode写入文件成功(第七步)。这时候namenode就知道该文件由哪些block组成(因为DataStreamer向namenode请求分配新block,namenode当然会知道它分配过哪些blcok给指定文件),它会等待最少的replica数被创建,然后成功返回。

读:

1)客户端通过调用调用DistributedFileSystem对象的open()方法来打开文件(也即图中的第一步),DistributedFileSystem通过RPC(Remote Procedure Call)调用询问NameNode来得到此文件最开始几个block的文件位置(第二步)。

2)对每一个block来说,namenode返回拥有此block备份的所有datanode的地址信息(按集群的拓扑网络中与客户端距离的远近排序)。如果客户端本身就是一个datanode(如客户端是一个mapreduce任务)并且此datanode本身就有所需文件block的话,客户端便从本地读取文件。

3)以上步骤完成后,DistributedFileSystem会返回一个FSDataInputStream,客户端可以从FSDataInputStream中读取数据。FSDataInputStream包装了一个DFSInputSteam类,用来处理namenode和datanode的I/O操作。

4)客户端然后执行read()方法(第三步),DFSInputStream(已经存储了欲读取文件的开始几个block的位置信息)连接到第一个datanode(也即最近的datanode)来获取数据。通过重复调用read()方法(第四、第五步),文件内的数据就被流式的送到了客户端。

5)当读到该block的末尾时,DFSInputStream就会关闭指向该block的流,转而找到下一个block的位置信息然后重复调用read()方法继续对该block的流式读取。这些过程对于用户来说都是透明的,在用户看来这就是不间断的流式读取整个文件。

6)当整个文件读取完毕时,客户端调用FSDataInputSteam中的close()方法关闭文件输入流(第六步)。

如果在读某个block是DFSInputStream检测到错误,DFSInputSteam就会连接下一个datanode以获取此block的其他备份,同时他会记录下以前检测到的坏掉的datanode以免以后再无用的重复读取该datanode。DFSInputSteam也会检查从datanode读取来的数据的校验和,如果发现有数据损坏,它会把坏掉的block报告给namenode同时重新读取其他datanode上的其他block备份。
这种设计模式的一个好处是,文件读取是遍布这个集群的datanode的,namenode只是提供文件block的位置信息,这些信息所需的带宽是很少的,这样便有效的避免了单点瓶颈问题从而可以更大的扩充集群的规模。

二、MapReduce

简单来说,MapReduce是一个并行的计算框架。它极大地方便了编程人员在不会分布式并行编程的情况下,将自己的程序运行在分布式系统上。

1.MapReduce运行流程架构

具体在下文中讲解,先有个印象。

2.MapReduce运行流程

为了更好地理解MapReduce运行,结合经典的Wordcount程序进行讲解,参考上面两张图进行理解。

假设有源数据文件file,file包含的内如如下:

the weather is good

today is good

good weather is good

1)file文件split后调用Map函数

文件file以多个block的形式存储在DataNode上,运行MapReduce程序时,MapReduce框架将会启动inputformate类【包含数据分割(Data Splits)和记录读取器(Record Reader)】,Data Splits将file拆分成多个splitRecordReader会读取这些split,每读取1个split就会调用1次map函数。可以看出Map的个数依赖于这个split个数。需要注意,map的创建依赖于标准输入。

假设在本例中file文件总共被切分成3个split,调用了3个map函数。

2)Map阶段

在map阶段将进行业务逻辑处理。在Wordcount例中进行词频统计,输出结果为k-v形式,其中<key,value>对应着<词,词频>。所以在三个map任务中,运行的结果如下。

• (the 1),(weather 1), (is 1), (good 1)

• (today 1),(is 1), (good 1)

• (good 1),(weather 1), (is 1), (good 1)

3)Shuffle处理

经过map函数处理之后,将会进行shuffle(具体包括Partition,Sort, Spill, Meger, Combiner,Copy, Memery, Disk等等操作),在这个阶段可以进行很多的调优操作。

• Partition,决定数据分配给哪个Reducer处理。比如采用Hash法,假设有n个Reducer,如果{“is”: 1}的key“is”对n进行取模,返回m,原来的数据将会变成{partition,key, value}形式,这里的m值为第m个reduce。例如 返回(2,is,1)代表着“is”将会分到reduce2中进行处理。

• MemoryBuffer,内存缓冲区,每个map的结果和partition处理的key value结果都会保存在缓存区中,缓冲区大小默认100M,溢写阈值100M *0.8 = 80M,缓冲区中的数据为:{ partition key value }三元组数据,例如:{“1” , “are” ,1}。当内存缓冲区写到80M的时候,这80M区域将会被锁住,数据只能往剩下的20M进行写入。此时被锁住的80M空间将会溢写到本地磁盘,这个过程会进行一次排序,排序的依据就是partition的值,写到本地后,80M的内存空间就会被清空。由于这个spill to disk过程会产生很多小文件,系统会根据partition的值,再进行归并排序,将小文件再整合成大文件,即上面图中的merge on disk。具体过程如下。

回到我们的例子,经过shuffle阶段后,原来输入的file数据,这时候将会变成如下,并且保存在本地磁盘。

•  (1,good ,1),(2,is,1),(3,the,1),(4,weather,1)

•  (1 ,good 1),(2,is,1),(3,today,1)

•  (1,good,1),(1,good 1) ,(2,is,1),(4,weather,1)

4)reduce

经过map和shuffle之后,每条数据都已经通过partition知道该分到哪个reduce去进行后续处理。数据进入reduce后,还需要进行一次sort处理。这个过程将会把所有相同partition的结果都拷贝到同一个reduce中,然后再按照key进行1次排序。回到例子,这时候数据将会变成如下形式。

good, 1 ;good, 1;good, 1;good, 1

is, 1;is, 1;is, 1;

the, 1;today, 1

weather, 1;weather, 1

之后在reduce阶段进行词频统计的逻辑处理,得到最终每个词的词频结果。reduce也有内存缓冲区,将会把结果溢写到本地。

5)Python代码实现MapReduce的Wordcount

先将1.data放到hdfs文件系统上。

[root@masteractive test]# hadoop fs -put  1.data /

run.sh

HADOOP_CMD="/usr/local/src/hadoop-2.6.0-cdh5.7.0/bin/hadoop" #引入hadoop命令
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.0-cdh5.7.0/share/hadoop/tools/lib/hadoop-streaming-2.6.0-cdh5.7.0.jar" #引入streaming 工具

INPUT_FILE_PATH_1="/1.data" #指定输入目录,数据需先放到dfs文件系统上
OUTPUT_PATH="/output"       #指定输出目录

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH #如果存在输出目录就先删除,否则会出错

$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \ 
    -output $OUTPUT_PATH \
    -mapper "python map.py" \
    -reducer "python red.py" \
    -file ./map.py \     #将map.py文件进行分发
    -file ./red.py       #将red.py文件进行分发

【注意:\ 后面不能有多余的空格,否则会产生路径找不到的问题

map.py

import sys
for line in sys.stdin:
	ss = line.strip().split(' ')
	for word in ss:
		print '\t'.join([word.strip(), '1'])

red.py

import sys

cur_word = None
sum = 0
for line in sys.stdin:
	ss = line.strip().split('\t')
	if len(ss) != 2:
		continue
	word, cnt = ss
	if cur_word == None:
		cur_word = word
	if cur_word != word:
		print '\t'.join([cur_word, str(sum)])
		cur_word = word
		sum = 0
	sum += int(cnt)
print '\t'.join([cur_word, str(sum)])

可以先在本地进行模拟

[root@master mapreduce_wordcount_python]# cat 1.data | python map.py | sort -k1 | python red.py > result.data

本地运行无误,运行run.sh

[root@master mapreduce_wordcount_python]# bash run.sh #通过hadoop,查看output文件下生成的文件,与本地模拟结果进行对比

三、两个重要的进程(Hadoop1.0)

1.JobTracker

• 主进程,负责接收客户作业提交,调度任务到作节点上运行,并提供诸如监控工作节点状态及任务进度等管理功能,一个MapReduce集群有一个jobtracker一般运行在可靠的硬件上。

• tasktracker是通过周期性的心跳来通知jobtracker其当前的健康状态,每一次心跳包含了可用的map和reduce任务数目、占用的数目以及运行中的任务详细信息。 Jobtracker利用一个线程池来同时处理心跳和客户请求。

2.TaskTracker

• 由jobtracker指派任务,实例化用户程序,在本地执行任务并周期性地向jobtracker汇报状态。在每一个工作节点上永远只会有一个tasktracker。

Hadoop1.0namenode存在单点故障,且单NameNode制约HDFS的扩展性。Hadoop2.0即第二代Hadoop为克服Hadoop1.0中的不足:针对Hadoop1.0单NameNode制约HDFS的扩展性问题,提出HDFS Federation,它让多个NameNode分管不同的目录进而实现访间隔离和横向扩展,同时彻底解决了NameNode单点故障问题。

针对Hadoop1.0中的MapReduce在扩展性和多框架支持等方面的不足,Hadoop2.0中将JobTracker中的资源管理和作业控制分开,分别由ResourceManager(负责所有应用程序的资源分配)和ApplicationMaster(负责管理一个应用程序)实现,即引入了资源管理框架Yarn。同时Yarn作为Hadoop2.0中的资源管理系统,它是一个通用的资源管理模块,可为各类应用程序进行资源管理和调度,不仅限于MapReduce一种框架【MapReducer2.0具有与MRv1相同的编程模型和数据处理引擎,唯一不同的是运行时环境】,也可以为其他框架使用,如Tez、Spark、Storm等。下面就进入Hadoop2.0和yarn的学习。

四、Hadoop streaming

1.Streaming特点

Streaming优点

• 开发效率高

– 方便移植Hadoop平台,只需按照一定的格式从标准输入读取数据、向标准输出写数据就可以

– 原有的单机程序稍加改动就可以在Hadoop平台进行分布式处理

– 容易单机调试

• 程序运行效率高

– 对于CPU密集的计算,有些语言如C/C++编写的程序可能比用Java编写的程序效率更高一些

• 便于平台进行资源控制

Streaming框架中通过limit等方式可以灵活地限制应用程序使用的内存等资源

Streaming局限

• Streaming默认只能处理文本数据,如果要对二进制数据进行处理,比较好的方法是将二进制的key和value进行base64的编码转化成文本

• 两次数据拷贝和解析(分割),带来一定的开销

2.使用方式

以Python的Wordcount为例进行讲解。

HADOOP_CMD="/usr/local/src/hadoop-2.6.0-cdh5.7.0/bin/hadoop" #引入hadoop命令
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.0-cdh5.7.0/share/hadoop/tools/lib/hadoop-streaming-2.6.0-cdh5.7.0.jar" #引入streaming 工具

INPUT_FILE_PATH_1="/1.data" #指定输入目录,数据需先放到dfs文件系统上
OUTPUT_PATH="/output"       #指定输出目录

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH #如果存在输出目录就先删除,否则会出错

$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \ 
    -output $OUTPUT_PATH \
    -mapper "python map.py" \
    -reducer "python red.py" \
    -file ./map.py \     #将map.py文件进行分发
    -file ./red.py       #将red.py文件进行分发

-input :指定作业的输入文件的HDFS路径,支持使用*通配符,支持指定多个文件或目录,可多次使用,即可以有多个输入。

-output:指定作业的输出文件的HDFS路径,路径必须不存在,并且具备执行作业用户有创建该目录的权限,只能使用一次。

-mapper:用户自己写的map程序,指定用Python的方式运行

-reducer:用户自己写的reduce程序

-file:文件进行分发到集群中,类似的配置还有-cacheFile, -cacheArchive分别用于向计算节点分发HDFS文件和HDFS压缩文件

-jobconf:提交作业的一些配置属性,常用的有如下:

mapred.job.name:作业名

mapred.job.priority:作业优先级

mapred.job.map.capacity:最多同时运行map任务数

mapred.job.reduce.capacity:最多同时运行reduce任务数

mapred.task.timeout:任务没有响应(输入输出)的最大时间

mapred.compress.map.output:map的输出是否压缩

mapred.map.output.compression.codec:map的输出压缩方式

mapred.output.compress:reduce的输出是否压缩

mapred.output.compression.codec:reduce的输出压缩方式

stream.map.output.field.separator:map输出分隔符

mapred.map.tasks:map task数目

mapred.reduce.tasks:reduce task数目

stream.num.map.output.key.fields:指定map task输出记录中key所占的域数目

num.key.fields.for.partition指定对key分出来的前几部分做partition而不是整个key

3.文件分发

1)file

file分发方式,将会把指定的文件发送到执行任务的机器上,执行完后就删除文件,一般用于分发小文件,例如用户自己编写的代码。

2)cacheFile

cachefile用于分发大的文件,执行任务的机器就可以直接从hdfs文件系统上获取该文件,如果大文件依然用file的方式分发,会导致耗时过长,因为文件只能逐个传给需要的节点。

-cacheFile "hdfs://master:9000/a.txt#ABC",表示将hdfs上的a.txt文件分发到执行任务的机器上,#ABC表示a.txt的别名。

3)cacheArchive

cacheArchive在需要分发文件存在复杂层级关系时使用,将这些文件压缩成一个文件,并且上传到hdfs系统中,当程序运行时将会自动将该文件进行解压,并获取到压缩包中的所有文件,如果用file分发,就需要处理层级关系,比较麻烦。

-cacheArchive "hdfs://master:9000/w.tar.gz#ABC",w.tar.gz将会自动解压,ABC代表的是解压后的目录,因此在代码中,需要通过Python的os.listdir(目录名称)这个函数,打开该目录,遍历该目录下的所有文件,如下代码。

#!/usr/bin/python
import os
import sys
import gzip
import time

def get_file_handler(f):
    file_in = open(f, 'r')
    return file_in

def get_cachefile_handlers(f):
    f_handlers_list = []
    if os.path.isdir(f):
        for fd in os.listdir(f):
            f_handlers_list.append(get_file_handler(f + '/' + fd))
    return f_handlers_list

def read_local_file_func(f):
    word_set = set()
    for cachefile in get_cachefile_handlers(f):
        for line in cachefile:
            word = line.strip()
            word_set.add(word)
    return word_set

def mapper_func(white_list_fd):
    word_set = read_local_file_func(white_list_fd)

    for line in sys.stdin:
	time.sleep(100)#让程序睡眠,便于从其他节点观察分发的文件
        ss = line.strip().split(' ')
        for s in ss:
            word = s.strip()
            if word != "" and (word in word_set):
                print "%s\t%s" % (s, 1)

if __name__ == "__main__":
    module = sys.modules[__name__]
    func = getattr(module, sys.argv[1])
    args = None
    if len(sys.argv) > 1:
        args = sys.argv[2:]
    func(*args)

4.输出数据压缩

• 输出数据量较大时,可以使用Hadoop提供的压缩机制对数据进行压缩,减少网络传输带宽和存储的消耗。

• 可以指定对map的输出也就是中间结果进行压缩

• 可以指定对reduce的输出也就是最终输出进行压缩

• 可以指定是否压缩以及采用哪种压缩方式。

• 对map输出进行压缩主要是为了减少shuffle过程中网络传输数据量

• 对reduce输出进行压缩主要是减少输出结果占用的HDFS存储。

HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"

INPUT_FILE_PATH_1="/The_Man_of_Property.txt"
OUTPUT_PATH="/output_cachearchive_broadcast"

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH

# Step 1.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \
    -output $OUTPUT_PATH \
    -mapper "python map.py mapper_func WH.gz" \
    -reducer "python red.py reduer_func" \
    -jobconf "mapred.reduce.tasks=10" \
    -jobconf  "mapred.job.name=cachefile_demo" \
    -jobconf  "mapred.compress.map.output=true" \
    -jobconf "mapred.map.output.compression.codec= org.apache.hadoop.io. compress. GzipCodec" \
    -jobconf  "mapred.output.compress=true" \
    -jobconf  "mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec" \
    -cacheArchive "hdfs://master:9000/w.tar.gz#WH.gz" \
    -file "./map.py" \
-file "./red.py"

5.全局排序

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值