参考:《Hadoop入门教程》https://blog.csdn.net/yuan_xw/article/details/50003197
《Hadoop架构介绍——HDFS的体系结构》https://blog.csdn.net/u013063153/article/details/53114678
《HDFS运行原理》https://www.cnblogs.com/laov/p/3434917.html
《Hadoop工作流程》https://blog.csdn.net/hongchenlingtian/article/details/53524705
一、概念
Hadoop是Apache开源组织的一个分布式计算开源框架,实现在大量计算机组成的集群中对海量数据进行分布式计算。Hadoop的核心是HDFS(分布式文件系统)和MapReduce。
分布式系统:网络互连的若干计算机。
分布式计算:分布式系统上执行的计算,将任务分解,然后合并计算结果。
二、HDFS
特点:1. 海量数据存储;2.文件分块存储到不同计算机上;3.高容错性(每个数据块都有备份),1T的数据要用3T的空间存储。
HDFS是 master/slave 架构,一个HDFS集群是有一个NameNode 和多个DataNoe节点,NameNode负责管理文件的命名空间、客户端对文件的访问,NameNode执行文件系统的namespace操作,例如打开、关闭、重命名文件和目录,同时决定block到具体Datanode节点的映射。DataNode负责管理节点上的存储、读写,Datanode在Namenode的指挥下进行block的创建、删除和复制。
2.1 HDFS写流程
有一个文件FileA,100M大小。Client将FileA写入到HDFS上。
HDFS按默认配置。
HDFS分布在三个机架上Rack1,Rack2,Rack3。
a. Client将FileA按64M分块。分成两块,block1和Block2;
b. Client向nameNode发送写数据请求,如图蓝色虚线①------>。
c. NameNode节点,记录block信息。并返回可用的DataNode,如粉色虚线②--------->。
Block1: host2,host1,host3
Block2: host7,host8,host4
原理:
NameNode具有RackAware机架感知功能,这个可以配置。
若client为DataNode节点,那存储block时,规则为:副本1,同client的节点上;副本2,不同机架节点上;副本3,同第二个副本机架的另一个节点上;其他副本随机挑选。
若client不为DataNode节点,那存储block时,规则为:副本1,随机选择一个节点上;副本2,不同副本1,机架上;副本3,同副本2相同的另一个节点上;其他副本随机挑选。
d. client向DataNode发送block1;发送过程是以流式写入。
流式写入过程,
1>将64M的block1按64k的package划分;
2>然后将第一个package发送给host2;
3>host2接收完后,将第一个package发送给host1,同时client想host2发送第二个package;
4>host1接收完第一个package后,发送给host3,同时接收host2发来的第二个package。
5>以此类推,如图红线实线所示,直到将block1发送完毕。
6>host2,host1,host3向NameNode,host2向Client发送通知,说“消息发送完了”。如图粉红颜色实线所示。
7>client收到host2发来的消息后,向namenode发送消息,说我写完了。这样就真完成了。如图黄色粗实线
8>发送完block1后,再向host7,host8,host4发送block2,如图蓝色实线所示。
9>发送完block2后,host7,host8,host4向NameNode,host7向Client发送通知,如图浅绿色实线所示。
10>client向NameNode发送消息,说我写完了,如图黄色粗实线。。。这样就完毕了。
分析,通过写过程,我们可以了解到:
①写1T文件,我们需要3T的存储,3T的网络流量贷款。
②在执行读或写的过程中,NameNode和DataNode通过HeartBeat进行保存通信,确定DataNode活着。如果发现DataNode死掉了,就将死掉的DataNode上的数据,放到其他节点去。读取时,要读其他节点去。
③挂掉一个节点,没关系,还有其他节点可以备份;甚至,挂掉某一个机架,也没关系;其他机架上,也有备份。
参考:https://www.cnblogs.com/laov/p/3434917.html
2.2 HDFS读流程
读操作就简单一些了,如图所示,client要从datanode上,读取FileA。而FileA由block1和block2组成。
那么,读操作流程为:
a. client向namenode发送读请求。
b. namenode查看Metadata信息,返回fileA的block的位置。
block1:host2,host1,host3
block2:host7,host8,host4
c. block的位置是有先后顺序的,先读block1,再读block2。而且block1去host2上读取;然后block2,去host7上读取;
2.3 Hadoop作业提交
参考:https://blog.csdn.net/hongchenlingtian/article/details/53524705
RM是Resource Manager,任务调度处理中心,NM是NodeManager,maptask 和 reducetask 实施中心。
1.Client中,客户端提交一个mr的jar包给JobClient(提交方式:hadoop jar ...)
2.JobClient持有ResourceManager的一个代理对象,它向ResourceManager发送一个RPC请求,告诉ResourceManager作业开始,
然后ResourceManager返回一个JobID和一个存放jar包的路径给Client
3.Client将得到的jar包的路径作为前缀,JobID作为后缀(path = hdfs上的地址 + jobId) 拼接成一个新的hdfs的路径,然后Client通过FileSystem向hdfs中存放jar包,默认存放10份
(NameNode和DateNode等操作)
4.开始提交任务,Client将作业的描述信息(JobID和拼接后的存放jar包的路径等)RPC返回给ResourceManager
5.ResourceManager进行初始化任务,然后放到一个调度器中
6.ResourceManager读取HDFS上的要处理的文件,开始计算输入分片,每一个分片对应一个MapperTask,根据数据量确定起多少个mapper,多少个reducer
7.NodeManager 通过心跳机制向ResourceManager领取任务(任务的描述信息)
8.领取到任务的NodeManager去Hdfs上下载jar包,配置文件等
9.NodeManager启动相应的子进程yarnchild,运行mapreduce,运行maptask或者reducetask
10.map从hdfs中读取数据,然后传给reduce,reduce将输出的数据给回hdfs
2.4 Hadoop常用Linux命令
http://hadoop.apache.org/docs/r1.0.4/cn/hdfs_shell.html
例如:Hadoop fs -cat 文件路径
注意:Hadoop命令需要安装,默认指向hadoop工具包
hadoop命令:https://www.cnblogs.com/feong/p/5148361.html
查看指定目录下内容:hadoop fs -ls [/production/ecom/adslog/(文件目录)]
将本地文件夹存储至hadoop上:hadoop fs -put [本地目录] [hadoop目录]
在hadoop指定目录内创建新目录:hadoop fs -mkdir [目录地址]
在hadoop指定目录下新建一个空文件,touchz命令:hadoop fs -touchz /test_put_dir/test_new_file.txt
打开某个已存在文件:hadoop fs -cat [file_path]
将hadoop上某个文件重命名:hadoop fs -mv [旧文件名] [新文件名]
将hadoop上某个文件down至本地已有目录下:hadoop fs -get [文件目录] [本地目录]
删除hadoop上指定文件:hadoop fs -rm [文件地址]
删除hadoop上指定文件夹(包含子目录等):hadoop fs -rm -r [目录地址]
将hadoop指定目录下所有内容保存为一个文件,同时down至本地:hadoop dfs -getmerge /user /home/t
将正在运行的hadoop作业kill掉:hadoop job -kill [job-id]
三、MapReduce
参考:https://www.cnblogs.com/firstsheng618/p/9022879.html
MapReduce是一种变成模型,用于大规模数据集的并行运算。Map和Reduce都是按照键值对来处理的,有一个shuffle过程,将Map的输出按键排序,放到合适的区间,再交给Reduce处理。
MapReduce计算模型主要由三个阶段构成:Map、Shuffle、Reduce。Map是映射,负责数据的过滤分类,将原始数据转化为键值对;Reduce是合并,将具有相同key值的value进行处理后再输出新的键值对作为最终结果;为了让Reduce可以并行处理Map的结果,必须对Map的输出进行一定的排序与分割,然后再交给对应的Reduce,这个过程就是Shuffle。Shuffle过程包含Map Shuffle和Reduce Shuffle。
3.1 Map Shuffle
在Map端的shuffle过程就是对Map的结果进行分区、排序、分割,然后将属于同一个分区的输出合并在一起并写在磁盘上,最终得到一个分区有序的文件。分区有序的含义是Map输出的键值对按分区进行排列,具有相同partition值的键值对存储在一起,每个分区里面的键值对又按key值进行升序排序(默认),大致流程如下:
3.2 Reduce Shuffle
- Map --> Task Tracker --> Job Tracker:map任务完成
- Reduce --> Job Tracker --> 开启reduce任务: 获取Map的输出位置
Reduce任务通过HTTP向各个Map任务拖取它所需要的数据。Map任务成功完成后,会通知父TaskTracker状态已经更新,TaskTracker进而通知JobTracker(这些通知在心跳机制中进行)。所以,对于指定作业来说,JobTracker能记录Map输出和TaskTracker的映射关系。Reduce会定期向JobTracker获取Map的输出位置,一旦拿到输出位置,Reduce任务就会从此输出对应的TaskTracker上复制输出到本地,而不会等到所有的Map任务结束。
- Map结果 --> Reduce --> 内存 --> 内存达到限度 --> 内存Merge --> 磁盘文件
Copy过来的数据会先放入内存缓冲区中,如果内存缓冲区中能放得下这次数据的话就直接把数据写到内存中,即内存到内存merge。Reduce要向每个Map去拖取数据,在内存中每个Map对应一块数据,当内存缓存区中存储的Map数据占用空间达到一定程度的时候,开始启动内存中merge,把内存中的数据merge输出到磁盘上一个文件中,即内存到磁盘merge。在将buffer中多个map输出合并写入磁盘之前,如果设置了Combiner,则会化简压缩合并的map输出。Reduce的内存缓冲区可通过mapred.job.shuffle.input.buffer.percent配置,默认是JVM的heap size的70%。内存到磁盘merge的启动门限可以通过mapred.job.shuffle.merge.percent配置,默认是66%。
- map输出全部完成 --> Reduce生成多个文件 --> 文件合并
当属于该reducer的map输出全部拷贝完成,则会在reducer上生成多个文件(如果拖取的所有map数据总量都没有内存缓冲区,则数据就只存在于内存中),这时开始执行合并操作,即磁盘到磁盘merge,Map的输出数据已经是有序的,Merge进行一次合并排序,所谓Reduce端的sort过程就是这个合并的过程。一般Reduce是一边copy一边sort,即copy和sort两个阶段是重叠而不是完全分开的。最终Reduce shuffle过程会输出一个整体有序的数据块。
3.3 MapReduce中的分组排序 GroupBy
itertools.groupby() 方法
https://docs.python.org/2/library/itertools.html#itertools.groupby
itertools提供操作迭代对象的函数,主要用到groupby()方法操作迭代器。
参数:iterable,可迭代的对象,比如文件、列表
key=keyfunc,分组函数, 默认为 lambda x:x,可以用itemgetter()指明按第几列分组
使用:文件按列分号,并按照某A列排序,读取文件并按A分组,再处理A分组里的内容
import itertools
from operator import itemgetter
def read_file():
for line in sys.stdin:
fields = line.strip().split('\t')
yield fields
def mapper():
readin = read_file()
for key, group in itertools.groupby(readin, itemgetter(2)):
print key
for g in group:
print g