大纲
-
HDFS 基本概念及特性
-
NameNode 和 DataNode
-
数据冗余备份
-
数据副本存放策略
-
机架感知
-
FsImage 和 EditLog
-
SecondaryNameNode
-
HDFS 读写步骤
-
安全模式
-
几个问答题
什么是 HDFS
The Hadoop Distributed File System (HDFS) is a distributed file system designed to run on commodity hardware.从以上官方描述可以看出,HDFS 是 Hadoop 分布式文件系统,并且运行在普通硬件上。这就意味着 HDFS 不需要优秀的硬件资源、高昂的硬件成本,只需要简单的物理机组成分布式集群,HDFS 使用横向拓展(增加机器)来提高存储容量,而非纵向扩展(提高单个机器的配置)。 HDFS 并非唯一的分布式文件系统,还有 GFS、TFS 等,但 HDFS 是使用最多的开源分布式文件存储系统,具有高度容错(highly fault-tolerant)及低成本的特点。
HDFS 的使命
接受硬件故障
HDFS 可以运行在成百上千台廉价的物理机上,存储着海量的数据,机器出故障是常见的一件事, 作为一个优秀的文件存储系统,HDFS 能够接受机器故障,它会进行故障检测以及恢复故障文件。流式数据访问运行在 HDFS 上的应用程序能够访问数据流,HDFS 主要用于批处理。看到流数据访问时,卡了我很久,没明白流式数据、实时数据、流式计算、实时计算有什么区别,经过一番查阅,发现这些概念或许不应该放在一起作比较。 流式数据:理解为不是一次性加载完的数据,比如看电影,数据是一帧一帧过来的,动态的;
实时数据:实时产生的数据,和流式数据区别不大,有时候也会叫做实时流数据;实时计算:处理实时数据,区别与离线计算(处理历史数据);流式计算:这与实时计算不应该一起比较,实时计算强调的是数据的实时性,而流式计算强调的是计算方法,理解为 Java8 中的流式数据处理;
支持大数据集HDFS 存储的典型文件是 GB 或 TB 大小,一个磁盘无法存储大文件,HDFS 将文件切分成小块,分别存储在不同服务器的磁盘上,通过网络进行连接。简单的一致性模型(write-once-read-many) HDFS 通常是一次写入,多次读取,不支持随机写操作,可以在文件末尾追加。这种方式简化了数据一致性问题。移动计算比移动数据更划算针对海量数据的处理,如果需要将数据移动到计算程序所在的节点,受网络的限制,计算将变得非常缓慢。HDFS 提供接口将计算程序移动到数据所在的位置,移动应用程序比移动海量数据效率高得多。跨硬件和平台的可移植性HDFS 易于从一个平台移植到另一个平台,这有助于 HDFS 成为大量应用程序的首选。HDFS 由 Java 编写,只要支持 Java 语言的机器便可以运行 HDFS NameNode 或 DataNode,这也是利用了 Java 的跨平台特性。
NameNode 和 DataNode
HDFS 是 master/slave 架构,在分布式中,一主多从的架构很常见。NameNode 主要存储和管理数据的元信息以及接受客户端的请求,DataNode 主要存储文件,所有的元信息都存储在一个 NameNode 节点上,这样极大地简化了架构的复杂性。 文件以 block(块)的形式存储在 DataNode 上,一个大文件存储到 HDFS 上时,会被切分成很多 block,这个过程是由 HDFS 自己完成的。单个 block 默认大小 128M,每个 block 默认备份三份,存储在不同的节点,冗余备份保证了 HDFS 文件的可靠性。NameNode:- 接受用户请求;
- 维护文件系统的目录结构;
- 管理文件与 block 之间的关系以及 block 与 DataNode 之间的关系;
- 存储文件;
- 文件被分成 block 存储在磁盘上;
- 为保证数据安全,文件会有多个副本,默认是 3 份;
- 目录仅仅是元信息,没有冗余备份,文件才有备份;
- 一个物理节点可以作为一个 DataNode,也可以在一个节点上启动两个 DataNode,只是通常不会这么做;
- HDFS 不支持硬链接或软连接;
- HDFS 中的 block 大小以及备份的数量都是可配置的;
数据冗余备份
一个文件的元信息如下图所示,包含了文件名,备份的份数,对应的 block id。 以文件 part-0 为例,备份数是 2,block id 是 1 和 3,在 Datanodes 中可以找到,id 为 1 的 block 有两个,id 为 3 的 block 有两个,分别存储在不同 Datanode。两个不同 block id 组合起来就是一个完整的名称为 part-0 的文件。数据副本存放策略
大型 HDFS 实例通常分布运行在由许多机架组成的集群中,一个机房中有很多机架,一个机架上有多个服务器,不同机架的机器通信需要经过交换机,受带宽等因素的影响,需要更高的网络通信成本。所以默认 3 个副本的情况下,采用如下的放置策略:- 在机架 1 上放置第一个副本;
- 在另一个机架 2 上放置第二副本;
- 副本三与副本二放置在同一个机架上;
- 如果有更多的副本,则随机选择机架,每个机架的副本数量有个上限值,计算方式通常是:(replicas - 1) / racks + 2
- 避免一个机架出故障,导致所有数据丢失;
- 同一个机架上的节点通信网络会比不同机架节点通信更好,副本二与副本三放置在同一个机架能够节省带宽;
机架感知
了解 HDFS 默认三份备份后,会想到一个问题,NameNode 怎么知道 DataNode 在哪个机架呢?写文件时怎么能正确知道 DataNode 是否满足上面的备份策略呢? Hadoop 组件有机架感知(Rack Awareness)功能,默认是关闭的,可以通过配置文件开启,在core-site.xml
文件中有此配置项:net.topology.script.file.name
,以下是官方文档对该配置项的描述。net.topology.script.file.nameThe default implementation of the DNSToSwitchMapping. It invokes a script specified in net.topology.script.file.name to resolve node names. If the value for net.topology.script.file.name is not set, the default value of DEFAULT_RACK is returned for all node names.该配置项的值是一个脚本的路径,当没有配置时,默认值为 DEFAULT_RACK,DEFAULT_RACK 就是将所有 DataNode 认为是一个机架,物理上它们可能是在不同机架。此时 HDFS 并不知道每个 DataNode 对应的真实 rack,就会将副本随机写到 DataNode 上,不一定满足上面提到的副本放置策略。 开启机架感知后,指定的脚本接受一个入参,DataNode 的 ip,计算完返回一个结果,DataNode 所在的机架 id,格式如下:
/myrack/myhost
eg: /192.168.100.0/192.168.100.5
NameNode 在启动时,会判断该配置是否为空,如果不为空,说明开启了机架感知。NameNode 会根据配置找到该脚本,当接受到 DataNode 的心跳时,会运行该脚本,将其 ip 作为入参,将输出的结果作为该 DataNode 的 rack id,保存为一个 map 的形式存放在内存中。 开启机架感知后,NameNode 就能够正确识别每个 DataNode 所属的机架,能够轻松实现上述的副本存放策略。读取时如何选择副本
为了最大的减少带宽和延迟,HDFS 读取文件采用就近原则,如果与客户端在同一机架上的 DataNode 上存有副本,则直接读取该副本。如果 HDFS 是跨数据中心的,则优先选择同一数据中心的副本。FsImage 和 EditLog
对元数据的每一次更改都会记录在名为 EditLog 的文件中,该文件由 NameNode 维护,存储在 NameNode 节点的本地磁盘,比如在 HDFS 中新建一个文件、修改备份因子都会记录在 EditLog 文件中。整个文件系统的信息,包括文件与 block 的映射和文件系统的属性,存储在一个名为 FsImage 的文件中,该文件也存储在 NameNode 的本地磁盘。- EditLog:保存元数据更改记录,一个文件只记录一段时间的信息,该文件会在某些时刻合并到 FsImage,FsImage 中的信息要比 EditLog 记录的信息慢一步。
- FsImage:保存文件系统目录树以及文件和 block 的对应关系,理解为元数据镜像文件,某个时刻整个 HDFS 系统文件信息的快照;
SecondaryNameNode
SecondaryNameNode 不是 NameNode 的备份,不是为了做高可用(HA)的。checkpoint 是触发 FsImage 和 EditLog 文件进行合并的条件,形成新的 FsImage,也就是检查点。到达 checkpoint 时,会将 FsImage 和 EditLog 文件读取到内存,并通过 http 的方式发送给 SecondaryNamenode,由 SecondaryNamenode 完成合并,再发回给 NameNode。 checkpoint 的触发条件有两个:-
指定时间间隔,通过 dfs.namenode.checkpoint.period 进行配置,默认是一小时;
-
指定 EditLog 文件大小,通过 dfs.namenode.checkpoint.txns 进行配置,默认是 1 百万条事务记录;
FsImage 和 EditLog 合并过程
- 检查是否触发 checkpoint 条件;
- 触发 checkpoint,NameNode 停止向 edits 中写新的记录,另外生成一个 edits.new 文件,将新的事务记录在此文件中;
- SecondaryNameNode 通过 HTTP 请求,从 NameNode 下载 fsimage 和 edits 文件,合并生成 fsimage.chkpoint 文件;
- SecondaryNameNode 再将新生成的 fsimage.chkpoint 上传到 NameNode 并重命名为 fsimage,直接覆盖旧的 fsimage,实际上中间的过程还有一些 MD5 完整性校验,检查文件上传下载后是否完整;
- 将 edits.new 文件重命名为 edits 文件,旧的 edits 文件已经合并到 fsimage;
读写详细步骤
读操作(简略版)
- Client 向 NameNode 发起读请求;
- NameNode 将请求文件的元信息返回给 Client;
- Client 根据元信息去对应的 datanode 上取 block,并以追加的方式写文件,完成 block 的拼接工作;
- 最后组成完整的文件;
-
客户端发起写文件请求,会带上元数据信息;
-
NameNode 接受到请求后,会做一些校验工作,如文件是否存在、客户端是否有写权限等,并将写操作记录到 edits 文件中,如果写失败,比如断电了,edits 文件中还记录了上一次操作的信息,能够复原上一次操作;
-
NameNode 将返回每个 block 存放的 DataNode 列表;
-
客户端从 block 所属的 DataNode 列表中,假设备份 3 份,根据就近原则开始写操作,比如选择 DataNode1,在写的同时,DataNode1 会将文件信息传递给 DataNode2,DataNode2 接收到后再传递给 DataNode3,DataNode 接收到信息后,再依次返回确认信息,就像流水线一样,1 -> 2 -> 3,这个过程叫 Replication Pipelining。
-
DataNode 写完之后,会将结果返回给客户端,收到一个成功的结果,客户端就认为写操作已经完成了,剩余两个备份会异步进行。假设 2 -> 3 的过程中写失败了, 3 号机器宕机,2 号收不到成功确认 ack,则会告知 NameNode,NameNode 再重新指定一个 DataNode 进行写操作,1、2 随机选择一个作为写操作的发起端,保证最后是 3 份备份。
安全模式
NameNode 启动时,会从磁盘读取 FsImage 和 EditLog 文件至内存,然后等待 DataNode 发送 Blockreport,此时 NameNode 处于只读状态,这时不能进行写操作,这个过程 NameNode 处于安全模式。当 DataNode 将 block 的信息发送给 NameNode,大多数 block 处于可用状态时,NameNode 会自动退出安全模式。也可以通过 hdfs 命令行或 NameNode 页面对安全模式进行开关操作。问答形式
HDFS 中的文件写入只支持单个写入者,而且写操作总是以「只添加」方式在文件末尾写数据吗?- HDFS 的目标应用场景就是一次写入,多次读取 ;
- 如果要支持随机写,分布式数据的一致性就会受到挑战;随机写文件会破坏原文件元数据,元数据的改动会导致校验和的改动,而 hadoop 会依赖校验和等信息进行文件拆分,以及校验文件合法性,随机写会造成效率很低。
- 如果一定要保证实时的数据一致性,性能牺牲会很大,不适合大数据量少写多读的场景。
-
从磁盘传输的时间明显大于寻址时间,导致程序在处理这块数据时,变得非常慢;
-
MapReduce 中的 map 任务通常一次只处理一个 block 的数据,如果块过大,运行速度会变慢;
-
存放大量小文件会占用 NameNode 中大量内存来存储元数据,而 NameNode 的内存是有限的,这样不合理;
-
文件块过小,大量小文件导致寻址时间增长,程序花很多时间找 block 位置;
-END-