这一篇博文是【大数据技术●降龙十八掌】系列文章的其中一篇,点击查看目录:大数据技术●降龙十八掌
一、 HDFS简介
1、 Hadoop中的HDFS
HDFS是Hadoop Distributed File System(Hadoop 分布式文件系统)的缩写,但是HDFS不是Hadoop里的唯一的分布式文件系统,因为Hadoop只是定义了文件系统的抽象,HDFS只是其中的一个实现,另外,Hadoop中支持的文件系统如下:
文件系统 | URI方案 | Java实现 | 定义 |
---|---|---|---|
Local | file | fs.LocalFileSystem | 支持有客户端校验和的本地文件系统 |
HDFS | hdfs | hdfs.DistributedFileSystem | Hadoop的分布式文件系统 |
HFTP | hftp | hdfs.HftpFilesSystem | 支持通过Http方式以只读的方式访问HDFS,distcp经常用在不同的HDFS集群间复制数据 |
HSFTP | hsftp | hdfs.HsftpFilesSystem | 支持通过Https方式以只读的方式访问HDFS |
HAR | har | fs.HarFileSystem | 构建在其他文件系统上进行归档文件的文件系统,Hadoop归档文件主要用来减少NameNode的内存使用。 |
KFS | kfs | fs.kfs.KosmosFilesSystem | |
FTP | ftp | Fs.ftp.FtpFileSystem | 由FTP服务器支持的文件系统 |
S3(本地) | s3n | fs.s3native.NativeS3FileSystem | 基于AmazonS3的文件系统 |
S3(基于块) | s3 | fs.s3.NativeS3FileSystem | 基于AmazonS3的文件系统,以块格式存储解决了S3的5GB文件大小的限制 |
2、 HDFS的特点
HDFS是基于流数据模式访问和处理超大文件的需求而开发的,HDFS的主要特点
(1) 超大文件数据集群
HDFS可以存储和管理GB、TB、甚至PB级别的大文件。
(2) 流式数据访问方式读取文件
HDFS的设计是基于“一次写入、多次读取”任务,对文件的读取,是采用顺序全部扫描的方式。
(3) 对硬件要求并不是特别高,有很好的容错机制。
因为HDFS在设计上考虑了数据的可靠性、安全性、高可用性,所以HDFS对硬件的要求并不是太高,可以运行在廉价的商用硬件集群上。
(4) 数据访问有一定的延迟
HDFS适合大型数据集的分析人物,HDFS优化的是数据吞吐量,是要以提高延迟为代价的。
(5) HDFS无法高效存储大量小文件
因为HDFS上所有文件的元数据都是存储在NameNode上的,而NameNode数据是存在内存里的,所以NameNode的内存大小限制了文件个数。
(6) HDFS不支持多个写入者,也不支持随机写。
HDFS的一个文件只有一个写入者,并且操作只能在文件末尾追加,所以不支持随机写。
二、 HDFS体系结构
1、 体系结构图
2、 体系结构介绍
(1) HDFS由Client、NameNode、DataNode、SecondaryNameNode组成。
(2) Client提供了文件系统的调用接口。
(3)NameNode由fsimage(HDFS元数据镜像文件)和editlog(HDFS文件改动
日志)组成,NameNode在内存中保存着每个文件和数据块的引用关系。
NameNode中的引用关系不存在硬盘中,每次都是HDFS启动时重新构造
出来的。
(4) SecondaryNameNode的任务有两个:
定期合并fsimage和editlog,并传输给NameNode。
为NameNode提供热备份。
(5) 一般是一个机器上安装一个DataNode,一个DataNode上又分为很多
很多数据块(block)。
(6) DataNode会通过心跳定时向NameNode发送所存储的文件块信息。
(7) HDFS的副本存放规则
默认的副本系数是3,一个副本存在本地机架的本机器上,第二个副本存
储在本地机架的其他机器上,第三个副本存在其他机架的一个节点上。
这样减少了写操作的网络数据传输,提高了写操作的效率;另一方面,机
架的错误率远比节点的错误率低,所以不影响数据的可靠性。
3、 核心概念
(1) 块
数据块是HDFS中最小的寻址单位,Hadoop2.5中默认一个块的大小为
128M,不像其他的文件系统,HDFS中块是个逻辑概念,少于一个块大小
的文件不会占用一整块的空间。
设置块比较大的原因是减少寻址开销,但是块设置的也不能过大,因为一
个Map任务一般是处理一个块的数据,如果块设置的太大,Map任务处理的数据
量就会过大,会导致效率并不高
HDFS中块是逻辑概念的好处:
-
可以存储任意大的文件而不受单个节点磁盘大小的限制,HDFS可以将超
大文件分割为多个块,分别存储在不同的机器上。 -
简化存储子系统的设计,权限信息和块分开存储,利于管理。
-
块的设计利于数据备份,进而提供容错能力和提高可用性。
(2) NameNode
NameNode管理集群中的文件读写执行调度,并存储文件的元数据,NameNode的
数据存储在命名空间镜像(Namespace image)和编辑日志(Edit log)
中,NameNode启动时,会将所有数据读取到内存中
(3) DataNode
DataNode负责任务的执行,并存储数据,和客户端进行交互,DataNode
会通过心跳定时向NameNode发送它所存储的块信息。
(4) HDFS联盟
HDFS集群中只有一个NameNode是有很多局限性的,为了解决单个
NameNode的局限,设计了HDFS联盟,HDFS 联盟是可以在Hadoop集群
中设置多个NameNode,联盟中的多个NameNode是不同的,可以理解为
将一个NameNode切分为了多个NameNode,每一个NameNode只负责管
理一部分数据,HDFS Federation中的多个NameNode共用DataNode。
单个NameNode节点的局限性:
- 命名空间的限制
NameNode上存储着整个HDFS上的文件的元数据,NameNode是部署在
一台机器上的,因为单个机器硬件的限制,必然会限制NameNode所能管
理的文件个数,制约了数据量的增长。
- 数据隔离问题
整个HDFS上的文件都由一个NameNode管理,所以一个程序很有可能会
影响到整个HDFS上的程序,并且权限控制比较复杂。
- 性能瓶颈
单个NameNode时HDFS文件系统的吞吐量受限于单个NameNode的吞吐
量。因为NameNode是个JVM进程,JVM进程所占用的内存很大时,性能
会下降很多。
HDFS联盟架构:
(5) HDFS HA
单NameNode的缺陷存在单点故障的问题,如果NameNode不可用,则会
导致整个HDFS文件系统不可用。所以需要设计高可用的HDFS(Hadoop
HA)来解决NameNode单点故障的问题。解决的方法是在HDFS集群中设
置多个NameNode节点。但是一旦引入多个NameNode,就有一些问题需
要解决。
HDFS HA需要保证的四个问题:
-
保证NameNode内存中元数据数据一致,并保证编辑日志文件的安全
性。 -
多个NameNode如何协作
-
客户端如何能正确地访问到可用的那个NameNode。
-
怎么保证任意时刻只能有一个NameNode处于对外服务状态。
解决方法:
-
对于保证NameNode元数据的一致性和编辑日志的安全性,采用
Zookeeper来存储编辑日志文件。 -
两个NameNode一个是Active状态的,一个是Standby状态的,一个时间
点只能有一个Active状态的NameNode提供服务,两个NameNode上存储的元数据是实时同步的,当Active的NameNode出现问题时,通过
Zookeeper实时切换到Standby的NameNode上,并将Standby改为Active
状态。 -
客户端通过连接一个Zookeeper的代理来确定当时哪个NameNode处于服
务状态
HDFS HA架构图:
-
HDFS HA架构中有两台NameNode节点,一台是处于活动状态
(Active)为客户端提供服务,另外一台处于热备份状态(Standby) -
元数据文件有两个文件:fsimage和edits,备份元数据就是备份这两个文
件。JournalNode用来实时从Active NameNode上拷贝edits文件,
JournalNode有三台也是为了实现高可用 -
Standby NameNode不对外提供元数据的访问,它从Active NameNode
上拷贝fsimage文件,从JournalNode上拷贝edits文件,然后负责合并
fsimage和edits文件,相当于SecondaryNameNode的作用。最终目的是保
证Standby NameNode上的元数据信息和Active NameNode上的元数据信
息一致,以实现热备份。 -
Zookeeper来保证在Active NameNode失效时及时将Standby NameNode
修改为Active状态。 -
ZKFC(失效检测控制)是Hadoop里的一个Zookeeper客户端,在每一
个NameNode节点上都启动一个ZKFC进程,来监控NameNode的状态,
并把NameNode的状态信息汇报给Zookeeper集群,其实就是在
Zookeeper上创建了一个Znode节点,节点里保存了NameNode状态信
息。当NameNode失效后,ZKFC检测到报告给Zookeeper,Zookeeper把
对应的Znode删除掉,Standby ZKFC发现没有Active状态的NameNode
时,就会用shell命令将自己监控的NameNode改为Active状态,并修改
Znode上的数据。
Znode是个临时的节点,临时节点特征是客户端的连接断了后就会把znode删除,所以当ZKFC失效时,也会导致切换NameNode。
- DataNode会将心跳信息和Block汇报信息同时发给两台NameNode,DataNode只接受Active NameNode发来的文件读写操作指令。
三、 工作机制
1、副本存放和读取策略
HDFS中的文件是被分成块存放的,为了达到数据的高可用性,每一个块会保存N个副本,如果数据块损坏,就会读取副本来恢复数据块,而副本的存放策略是HDFS可靠性和性能的关键。
举个例子:HDFS默认副本数是3,HDFS存放的策略是将第一个副本存放在本地节点上,第二个副本存放在同一个机架的不同节点上,第三个副本存放在不同机架上另一个节点上,同一个机架上的两个节点同时失效的概率要比一个节点失效的概率低,而两个机架同时失效的概率要比一个机架上两个节点失效的概率低,这样尽最大可能地提高数据存储的可靠性;另外读取副本时,为了减少带宽,首先尝试读取同一个节点上的第一个副本,读取失败后再去读取同一个机架上另外一个节点上的第二个副本,如果还是读取失败,就读取不同机架上的第三个副本,这样尽可能地提高读取副本的性能。
2、机架感知
为了提高HDFS的数据存储可靠性和提高数据读写的性能,HDFS采用了一种称为机架感知的策略:
在一个交换机上的所有节点被称为在一个机架上,通常HDFS运行在跨越多个机架的集群上,通常同一个机架上的两个节点间的带宽比不同机架上的两个节点间的带宽要大。HDFS的机架感知策略就是将一个数据块的副本存放在不同机架上,这样防止整个机架的失效导致数据的丢失,从而提高数据的存储可靠性,同时读取副本时,优先读取同一个机架上的副本,从而提高副本读取的性能。配置dfs.network.script 参数来确定节点所处的机架。当这个脚本配置完毕,每个节点都会运行这个脚本来获取它的机架ID。默认的安装假定所有的节点属于同一个机架。
机架感知设置步骤:
(1) 编写机架感知的脚本
机架感知的脚本是用Python写的,输入一个域名或者IP,返回所在机架。
#!/usr/bin/python
#-*-coding:UTF-8 -*-
import sys
rack = {"hadoop-node-31":"rack1",
"hadoop-node-32":"rack1",
"hadoop-node-33":"rack1",
"hadoop-node-34":"rack1",
"192.168.1.50":"rack2",
"192.168.1.51":"rack2",
"192.168.1.52":"rack2",
"192.168.1.53":"rack2",
"192.168.1.54":"rack2",
}
if __name__=="__main__":
print "/" + rack.get(sys.argv[1],"rack0")
(2) 配置脚本路径
将脚本放在NameNode所在的机器上,然后在core-site.xml中配置
<property>
<name>topology.script.file.name</name>
<value>/opt/topology.py</value>
</property>
(3) 重启NameNode,使得机架感知生效。
3、安全模式
NameNode启动后会进入安全模式,安全模式下进行NameNode的初始化操作,不接受客户端的写入操作,只接受读取操作,并且不进行数据块的复制。安全模式下的NameNode做的工作如下:
(1) NameNode从硬盘加载fsimage
NameNode中存存储着fsimage开头、后面带一堆数字的文件,这些是NameNode元数据的序列化后的文件,编号最大的就是最新的文件,NameNode从硬盘读取编号最大的fsimage文件内容,将元数据信息加载到内存里。
(2) 回放edits
edits文件里是存储着还未写入fsimage的操作,尤其是上次NameNode没有正常停止时,为写入fsimage的操作只是写在edits文件里,所以NameNode启动时,加载完fsimage文件后,然后加载edits文件,并且回放edits里的操作,写入fsimage,保证不会丢失操作。
回放edits日志写入fsimage后,NameNode就会创建一个新的fsimage文件和一个空的edits文件。
(3) NameNode等待DataNode报告块信息
fsimage文件里并不记录文件块的信息,文件块的信息存在于DataNode中,所以接下来NameNode需要等待DataNode报告他们各自块的列表信息,DataNode通过心跳机制向NameNode报告。
这里也能看出来,NameNode内存里的元数据的组成结构为:fsimage+edits+datanode中block列表信息;
(4) 退出安全模式
每个数据块都有一个最小副本数,当HDFS中99.9%的块都达到各自的最小副本数时,再等待30秒,就退出安全模式。
这个99.9%可以在hdfs-site.xml中配置,属性名为dfs.namenode.safemode.threshold-pct,默认为0.999f。
这个30秒也是可以在hdfs-site.xml中配置的,属性名为dfs.namenode.safemode.extension,默认是30000。
(5) 复制缺少的副本块
退出安全模式后,检测哪些数据块还没有达到最小副本数,从其他副本块中复制数据块进行补充。
(6) 流程图
4、HDFS程序文件结构
(1) HDFS数据存储配置
${HADOOP_HOME}/etc/hadoop/core-site.xml配置文件中的hadoop.tmp.dir属性用来配置HDFS文件存储的目录路径,NameNode节点和DataNode节点都是读取这个属性。
-
NameNode存储路径配置属性为:dfs.namenode.name.dir,默认为:file://${hadoop.tmp.dir}/dfs/name
-
DataNode存储路径配置属性为:dfs.datanode.data.dir,默认为:file://${hadoop.tmp.dir}/dfs/data
比如hadoop.tmp.dir属性设置的是:/opt/data/tmp,下面来介绍下各自文件。
(2) NameNode节点文件
[hadoop@bigdata-senior01 tmp]$ ll /opt/data/tmp/dfs/name/current/
NameNode中有几类文件:
- fsimage文件:
NameNode在运行状态时,元数据是存储在内存里的,这样能保证数据的读写速度很快,但是NameNode会将元数据信息存储在硬盘里,fsimage文件就是存储元数据的映像文件,fsimage文件名称后面的编码最大的文件,就是当前正在使用的fsimage文件。
- fsimage_*****.md5文件
是校验文件,用于验证fsimage的完整性。
- edits文件
edits文件是NameNode元数据日志文件,当有元数据的写入需求时,先将日志写入edits文件,因为edits文件比fsimage文件要小很多,所以写入非常快。之后SencondaryNameNode会协助将edits日志写入fsimage文件。
- seen_txid文件
seen_txid文件中的数字是当前正在使用的edits文件的编号,namenode启动时用来判断回放seen_txid指定版本的日志。
- VERSION文件
[hadoop@bigdata-senior01 tmp]$ cat /opt/data/tmp/dfs/name/current/VERSION
#Tue Jul 05 09:00:19 CST 2016
namespaceID=2101579007
clusterID=CID-205277e6-493b-4601-8e33-c09d1d23ece4
cTime=0
storageType=NAME_NODE
blockpoolID=BP-1641019026-127.0.0.1-1467624350057
layoutVersion=-57
-
namespaceID是HDFS文件系统的唯一标示,在格式化的时候生成。
-
clusterID是集群ID,NameNode和DataNode的集群ID应该一致,格式化的时候可以指定一个集群ID,没有指定则会自动生成一个集群ID。
-
cTime是NameNode的Fsimage的创建时间,如果layoutVersion发生变化触发一次升级,就会更新。
-
storageType是说明这个文件存储的是什么进程的数据结构信息,NAME_NODE或者DATA_NODE。
-
blockpoolID是存储块池的Id。
-
layoutVersion表示 HDFS 永久性数据结构的版本信息, 只要数据结构变更,版本号也要递减,此时的 HDFS 也需要升级,否则磁盘仍旧是使用旧版本的数据结构,这会导致新版本的 NameNode 无法使用。
(3) DataNode节点文件
[hadoop@bigdata-senior01 tmp]$ ll /opt/data/tmp/dfs/data/current/BP-1641019026-127.0.0.1-1467624350057/current/finalized/
blk_开头的文件是块文件。
5、数据读取流程
(1) HDFS客户端调用DistributedFileSystem类的open()方法,通过RPC协议请求NameNode来确定所请求的文件所在位置,找出最近的DataNode节点的地址。
(2) DistributedFileSystem会返回一个FSDataInputStream输入流对象给客户端。
(3) 客户端会在FSDatatInputStream上调用read()函数,按照每个DataNode的距离从近到远依次读取。
(4) 读取完每个DataNode后,在FSDataInputStream上调用close()函数。
(5) 如果读取出现故障,就会读取数据块的副本,同时向NameNode报告这个消息。
6、数据写入流程
(1) 客户端调用DistributedFileSystem对象的create()方法,通过RPC协议调用NameNode,在命名空间创建一个新文件,此时还没有关联的DataNode与之关联。
(2) create()方法会返回一个FSDataOutputStream对象给客户端用来写入数据。
(3) 写入数据前,会将文件分割成包,放入一个“数据队列”中。
(4) NameNode为文件包分配合适的DateNode存放副本,返回一个DataNode的管道。
(5) 根据管道依次保存文件包在各个DataNode上。
(6) 各个DataNode保存好文件包后,会返回确认信息,确认消息保存在确认队列里,当管道中所有的DataNode都返回成功的的确认信息后,就会从确认队列里删除。
管道中所有的DataNode都保存完成后,调用FileSystem对象的close()关闭数据流。
这一篇博文是【大数据技术●降龙十八掌】系列文章的其中一篇,点击查看目录:大数据技术●降龙十八掌