1、前言
本文写于2018年02月份,以当前HDFS版本2.9.0为主,主要参考为官方文档,其中加入了一些自己的理解,如有不对之处,还请多多指教,感谢!
2、介绍
HDFS是一个分布式的文件存储系统,它起源于Apache Nutch项目,当时作为其基础设施的一部分。经过发展,现在属于Apache Hadoop Core项目,网址:http://hadoop.apache.org/。HDFS设计于运行在通用的商用硬件上。它和其他的分布式文件系统有很多相似之处,但也有很多不同之处。
以下是HDFS的特点:
- 支持高容错,它可以部署在低成本的硬件上。
- 支持高吞吐量的访问,尤其当程序需要读取大的数据集合时
- 支持流式访问,因此并没有严格的遵守POSIX协议
3、设想和目标
硬件故障
硬件故障已经成为一个常态而非例外。一个HDFS实例包含了成千上万个服务器,每个服务器存储一部份数据。在真实的场景中,有大量的组件,每个组件都会有出错的概率,这意味着,HDFS的某些组件始终不可用。因此,HDFS的核心架构目标是,检测故障并快速的自动恢复。
流数据访问
应用程序需要通过流式的方式,去访问它们在HDFS上的数据。它们一般运行在文件系统上。HDFS的设计更多的是用于批处理上,而不是用户的交互使用。HDFS设计的重点是数据访问的高吞吐量(high throughput),而不是数据访问的低延迟(low latency)。POSIX协议强加了很多硬件需要,而这些,对于访问HDFS的应用程序来说是不需要的。POSIX语义在一些关键的地方,用来增加数据访问的吞吐率(throughput rates)。
大数据集
通常跑在HDFS上的程序是需要访问一个大的数据集。在HDFS中,一个典型的文件大小为G和TB的级别的。在这种场景下,HDFS被调整为重点支持大文件。在它设计之初,就是为了支持单个集群(a single cluster)中有数百个节点,所组成了大的数据集合和带宽。它也应该支持单个节点(a single instance)实例中,支持数千万个文件。
简单的一致性模型
HDFS应用程序的访问模型是,通常需要一次写入、多次读取。一个文件除了创建(created)、写入(written)、关闭(closed)之后,就不需要再进行修改了,除了追加(appends)和截断(truncates)之外。在追加操作中,支持将内容追加到文件的末尾,但不能在任意节点进行更新。这种设定,简化了数据一致性问题,并实现了高吞吐量的数据访问。一个MapReduce应用程序或者是一个Web爬虫程序,是完全符合这个模型的。
移动计算的成本低于移动数据
如果一个计算程序,离所需要的数据越近,则它请求的效率会高很多。尤其是当数据集很大的时候。这种方式,可以最大限度的减少网络拥堵并提高系统整体的吞吐量。这种设定,是因为通常来说,将计算任务移动到更离数据越近的地方越好,而不是把数据移动到程序所在的位置。HDFS提供了接口,让应用程序在离它们需要的数据更近的地方,进行运行和计算。
异构的硬件和软件平台可移植
HDFS很容易从一个平台移植到另一个平台,这种设计有利于HDFS进行自由的选择,从而运行在大量的平台上。
4、NameNode 和 DataNodes
HDFS是主/从(master/slave)架构。一个HDFS集群包含一个NameNode,作为管理文件系统名称空间(file system namespace)和管理客户端访问HDFS的主服务器。此外,还有一组DataNode节点,通常群集中的每个节点都是一个DataNode,用于管理自己节点上的存储。HDFS开放文件系统名称空间,并允许用户把数据存储在文件中。
NameNode负责:
- 执行文件系统命名空间的相关操作,如打开,关闭和重命名文件和目录。
- 决定数据块(blocks)到DataNode的映射。
DataNode负责:
- 在HDFS内部,一个文件会被分成一个或多个块,这些块存储在DataNode中。
- 负责提供来自文件系统客户端的读取和写入请求。
- 根据来自NameNode的指令,执行数据块创建,删除和复制操作。
NameNode和DataNode设计于运行在商用机器上,这些机器的操作系统通常是GNU/Linux。HDFS使用Java语言构建,任何支持Java的机器都可以运行NameNode和DataNode软件。
使用高度可移植的Java语言,意味着HDFS可以部署在各种机器上。典型的部署模式是:集群中有一台只运行NameNode的专用机器,其他的每台机器上运行着一个DataNode实例。不排除在同一台计算机上运行多个DataNode实例,但在实际部署中很少出现这种情况。
集群中单个NameNode的设计,极大地简化了系统的体系结构。NameNode存储着所有HDFS的元数据。这种设计方式是,用户的数据永远不会流经NameNode。
5、文件系统Namespace
HDFS支持传统的分层文件组织。用户或应用程序可以在这些目录内创建目录并存储文件。文件系统名称空间层次与大多数其他现有文件系统类似;可以创建和删除文件,将文件从一个目录移动到另一个目录,或者重命名文件。HDFS支持用户配额(user quotas)和访问权限(access permissions)。HDFS目前不支持硬链接或软链接,并不排除后续支持这些特性的可能。
6、数据副本
HDFS设计宗旨是可靠的存储着超大型文件,运行在大规模的集群机器上。它将每个文件存储为一系列的块(a sequence of blocks)。文件的块被复制,是用来实现容错。块大小(block size )和复制因子(replication factor)可以针对每个文件进行配置。
一个文件中,除了最后一个数据块(blocks)之外,其他所有的数据块都具有相同的大小。当用户可以在将可变长度块的配置加到append和hsync后,可以在不填写最后一个块的情况下,写入到新的数据块中。
应用程序可以在文件创建时指定文件的副本数量,也可以在后面进行修改。HDFS中的文件是一次性写入的(追加和截断除外),并且在任何时候都严格的限定一个文件只能有一个写入线程。
NameNode决定着数据块的复制。它定期从集群中的每个DataNode接收心跳(Heartbeat)和数据块报告(Blockreport)。能接收到Heartbeat意味着DataNode运行正常。 而Blockreport包含DataNode上所有块的列表信息。使用这种策略的短期目标是在生产系统上对其进行验证,更多地了解其行为,并为测试和研究更复杂的策略奠定基础。
副本的位置
副本的位置对于HDFS的可靠性和性能至关重要。优化副本的位置可以将HDFS与大多数其他分布式文件系统区分开来。这个功能是需要大量调优和经验。机架感知副本位置(rack-aware replica placement policy )策略的目的是提高数据可靠性、可用性和网络带宽利用率。当前实现的副本位置的策略就是使用的机架感知。
大规模的HDFS实例,通常分布在多个机架上的算机上。不同机架中两个节点之间的通信必须通过交换机。在大多数情况下,不同机架之间网络带宽总和是不一样的。
NameNode通过Hadoop Rack Awareness(注:Hadoop机架感知功能组件)确定每个DataNode所属的机架标识。一个简单但非最佳的策略是将副本放在另外一个的机架上。这样可以防止当前整个机架出现故障时数据丢失,并允许在读取数据时从多个机架进行读取,从而充分利用带宽。这种策略在集群中均匀分配副本,可以轻松平衡组件发生故障时的负载。但是,这样会增加写入成本,因为写入需要将数据块传输到多个机架。
常见情况下,当复制因子为3时,HDFS的放置策略是:第一个副本,在本地机器上放置一个副本(如果写入器位于数据节点上),否则放置在随机数据节点上。第二个副本,在一个不同或者远程机架的一个节点上。最后一个副本在第二个机架上的不同节点上。这种策略可以减少机架间写入流量,通常会提高写入性能。
机架故障的概率远远小于节点故障;这种策略不会影响数据可靠性和可用性。但是,它也降低了读取数据时使用的总体网络带宽,因为块仅放置在两个机架中,而不是三个。文件的副本不会均匀分布在机架上。第一个副本位于当前机架一个节点上,第二个副本位于另外一个机架的节点上,第三个副本均匀分布在其余机架的节点上。这种方法可提高写入性能,而不会影响数据可靠性或读取性能。
如果复制因子(replication factor)大于3,则随机确定第4个副本和后续副本的位置,同时将每个机架的副本数保持在上限以下(基于公式(副本-1)/机架+2)
由于NameNode不允许DataNode具有同一个块的太多副本,因此创建的最大副本数是当时的DataNode个数总和。
在对HDFS添加了对存储类型和存储策略的支持后,除了上述的机架感知功能之外,NameNode还将副本放置的策略考虑在内。NameNode首先选择基于机架感知的节点,然后检查候选节点是否具有与该文件关联的策略所需的存储空间。如果候选节点不匹配,NameNode会查找另一个节点。如果在第一个路径中找不到足够的节点来放置副本,则NameNode将在第二个路径中查找具有回退存储类型(fallback storage types)的节点。
默认副本放置策略仍在完善和开发中。
副本的选择
为了最大限度地降低全局带宽消耗和读取延迟,当有读请求时,HDFS会尝试将离请求最近的一个副本进行返回会。如果在读取器节点的同一机架上存在副本,则该副本优先满足读取请求。如果HDFS群集跨越多个数据中心,则在本地数据中心的副本优先于任何远程副本。
安全模式(Safemode)
在启动时,NameNode进入一个称为Safemode的特殊状态。当NameNode处于安全模式状态时,不会发生数据块的复制。NameNode接收来自DataNode的Heartbeat和Blockreport消息。Blockreport包含DataNode托管的数据块列表。每个块都有指定的最小数量的副本(参数:dfs.namenode.replication.min
,默认为1)。当该数据块的最小副本数已与NameNode中所记录的一致时,该块被认为是安全复制的。
副本数达到最小要求的block占系统总block数的百分比(参数:dfs.namenode.safemode.threshold-pct
,当实际比例超过该配置后,才能离开安全模式(但是还需要其他条件也满足)。默认为0.999f,也就是说符合最小副本数要求的block占比超过99.9%时,并且其他条件也满足才能离开安全模式。如果为小于等于0,则不会等待任何副本达到要求即可离开。如果大于1,则永远处于安全模式。)再加上30秒后,NameNode退出安全模式状态。如果依然存在数据块的列表(如果有的话)小于指定的副本数量,NameNode会将这些块复制到其他DataNode。
7、文件系统元数据持久化
HDFS命名空间由NameNode节点进行存储和管理。 NameNode使用名为EditLog的事务日志来持久化记录文件系统元数据发生的所有更改。例如,在HDFS中创建一个新文件,NameNode向EditLog中插入一条记录,来记录这个操作。同样,更改文件的复制因子会产生一条记录,将新记录插入到EditLog中。NameNode使用其本地主机OS文件系统中的文件来存储EditLog。HDFS整个文件系统名称空间(包括块到文件和文件系统属性的映射)存储在名为FsImage的文件中。FsImage也作为文件存储在NameNode的本地文件系统中。
NameNode将整个文件系统命名空间和文件Blockmap映射保存在内存中。当NameNode启动,或检查点(checkpoint)达到配置的阈值时,NameNode从磁盘读取FsImage和EditLog,并将EditLog中的所有事务加载到内存中,并以此为新版本刷新为磁盘上的FsImage。
当检查点触发时,它会截断旧的EditLog,因为事务已经被持久化到了FsImage中。检查点的目的是通过快照的方式,将系统元数据保存到FsImage中,确保HDFS上的文件和元数据的一致性。虽然读取FsImage效率高,但直接对FsImage进行增量编辑效率并不高。HDFS并没有为每个操作而去修改FsImage,而是将修改的记录保存在Editlog中。在检查点触发时,Editlog中的修改记录将应用于FsImage上。这个频率以秒为单位,或者在给定数量的文件系统事务已经累积(参数:dfs.namenode.checkpoint.txns
),或者是到指定的时间间隔(参数:dfs.namenode.checkpoint.period
)时,会触发检查点。如果这两个属性都都进行了设置,则要达到的第一个阈值才会触发检查点。
8、通信协议
所有HDFS通信协议都在TCP/IP协议之上进行分层。客户端通过可配置TCP端口的与NameNode所在的机器进行连接,具体使用的为ClientProtocol接口。DataNode使用DataNode Protocol与NameNode进行通信。远程过程调用(RPC)抽象包装了客户端协议和数据节点协议。通过设计,NameNode永远不会发起RPC,它只响应DataNode或客户端发出的RPC请求。
9、健壮性
HDFS的主要目标是,即使出现故障时也能可靠地存储数据。三种常见类型的故障是NameNode故障,DataNode故障和网络分区。
数据磁盘故障,心跳和重新复制
每个DataNode定期向NameNode发送Heartbeat消息。NameNode通过是否收到Heartbeat消息来检测这种情况。 网络分区可能导致一些DataNode失去与NameNode的连接。NameNode将最近没有Heartbeats的DataNode标记为死亡,并且不会向它们转发任何新的IO请求。那么,对于HDFS而言,在死亡DataNode节点上的数据都将不可用。 这样导致某些块的复制因子(replication factor)降至其指定值以下。NameNode会不断去跟踪哪些块需要复制,并在需要时启动复制操作。 重新复制(re-replication)的动作可能由于许多原因而产生:DataNode变得不可用,副本损坏,DataNode上的硬盘出行故障,或者文件的复制因子增加。
标记DataNode不可用的超时时间过于保守(默认情况下超过10分钟),这样做是避免当DataNode不稳时,会产生的复制风暴(replication storm)。 在对读取 和/或 写入性能敏感的场景下,用户可以设置较短的时间间隔。
群集重新平衡
HDFS架构与数据重新平衡方案兼容。如果DataNode上的可用空间低于某个阈值,则数据重新平衡方案可能会自动将数据从一个DataNode移动到另一个DataNode。目前还没有实现的是,针对特定文件突然需要实现某些高需求(读、写)的情况下,动态创建额外的副本并重新平衡群集中的其他数据。
数据的完整性
由于存储设备故障,网络故障或软件错误,从DataNode获取的数据块可能会损坏。HDFS客户端软件对HDFS文件的内容执行校验和检查。 当客户端创建HDFS文件时,它会计算文件每个块的校验和,并将这些校验值和,存储在同一个HDFS命名空间中的一个单独隐藏文件里。当客户端读取文件内容时,会去验证从每个DataNode收到的数据的校验和,与文件中的校验和是否相匹配。 如果不是,那么客户端可以选择从另一个具有该块的副本的DataNode中去获取该数据块。
元数据磁盘故障
FsImage和EditLog是HDFS的中心数据结构。这些文件的损坏可能会导致HDFS实例不可用。基于上叙原因,NameNode可以配置多个FsImage和EditLog副本。任何的FsImage或EditLog的更新都会同步到其他的副本上。这种同步更新的操作可能会降低NameNode每秒可支持的命名空间事务处理速度。但是,这种牺牲是可以接受的,因为即使HDFS上是数据密集型应用,它们也不会是元数据密集型的。当NameNode重新启动时,它会去选择最新的一致的FsImage和EditLog来使用。
另外还有以下方法防止发生故障:使用多个NameNode启用高可用性、使用NFS作为共享存储、使用分布式日志(称为Journal,官方推荐的方法)。
快照
快照(Snapshots)支持在特定时刻存储数据副本。当HDFS出现损坏时,可以使用快照将损坏的HDFS实例回滚到之前的快照点上。
10、数据组织
数据块
HDFS旨在支持非常大的文件。与HDFS兼容的应用程序应该是处理大型数据集的应用程序。这些应用程序只会写入一次数据,但他们后续会读取一次或者多次,并且要求可以使用流的方式进行读取。HDFS支持在文件上一次写入多次读取(write-once-read-many)的语义。 HDFS使用的块大小通常为128 MB。因此,一个HDFS文件会被分成128 MB大小的块,每个块将驻留在不同的DataNode上。
副本写入流程
当HDFS副本数为3时,在客户端进行数据写入时,NameNode会使用算法(replication target choosing algorithm)检索去获取DataNode列表。这个列表包含了需要写入该块的副本的DataNode节点。客户端会首先写入到第一个DataNode,当第一个DataNode开始接收到了部分数据,它会将其写入到本地存储库中,并将这些数据传输到列表中的第二个DataNode。当第二个DataNode开始接收数据块的每个部分时,会将该这些数据写入其本地存储库,然后将该数据块刷新到第三个DataNode。最后,第三个DataNode将数据写入其本地存储库。这样写入的过程就像一个流水线一样,DataNode可以从流水线中的前一个接收数据,并且同时将数据转发到流水线中的下一个节点。
11、数据访问
HDFS支持多种方式去获取数据。HDFS为应用程序提供了FileSystem Java API。使用C语言包装器也可用Java API和REST API。
FS Shell
HDFS允许将用户数据以文件和目录的形式进行组织。它提供了一个名为FS shell的命令行界面,可让用户与HDFS中的数据进行交互。这个命令集的语法和于大家已经熟悉的shell(例如bash,csh)类似。以下是部分命令示例:
操作 | 命令 |
---|---|
创建一个名为 /foodir 的目录 | bin/hadoop dfs -mkdir /foodir |
删除 /foodir 目录 | bin/hadoop fs -rm -R /foodir |
查看文件 /foodir/myfile.txt 中的内容 | bin/hadoop dfs -cat /foodir/myfile.txt |
DFSAdmin
DFSAdmin命令集是用于管理HDFS集群,仅由HDFS管理员使用。以下是部分命令示例:
操作 | 命令 |
---|---|
使集群进入安全模式 | bin/hdfs dfsadmin -safemode enter |
显示 DataNode 列表 | bin/hdfs dfsadmin -report |
动态刷新配置文件,以便增加或停用DataNode | bin/hdfs dfsadmin -refreshNodes |
浏览器接口
标准的HDFS安装时,通过可配置的TCP端口安装Web服务。用户可以使用Web浏览器浏览HDFS的相关信息,并查看其数据文件的内容。
12、空间回收
如果配置回收站为打开,那么通过FS Shell命令执行删除的操作后,文件并不会立即从HDFS中删除。HDFS会将这个被删除的文件,移动到回收站目录下(每个用户拥有自己的回收站目录,在/user//.Trash 下)。只要文件保留在回收站中,文件就进行可以快速恢复。
最近删除的文件被移到回收站目录(/user//.Trash/Current),并且在通过可配置的时间间隔,HDFS会为当前回收站目录中的文件创建检查点,在(/user//.Trash/)目录下,并且会在检查点过期后删除它们。具体可见:expunge command of FS shell 更多关于回收站检查点点信息
在回收站达到失效时间后,NameNode将从HDFS中删除该文件。 删除文件会导致与文件关联的块空间被释放。 注意,用户删除文件的时间,与HDFS中相应增加可用空间的时间,两者之间可能存在明显的延迟。
以下示例演示使用FS Shell命令,对HDFS上的delete目录下的文件test1、test2执行创建和删除操作。
$ hadoop fs -mkdir -p delete/test1
$ hadoop fs -mkdir -p delete/test2
$ hadoop fs -ls delete/
Found 2 items
drwxr-xr-x - hadoop hadoop 0 2015-05-08 12:39 delete/test1
drwxr-xr-x - hadoop hadoop 0 2015-05-08 12:40 delete/test2
下面的命令演示删除delete目录下,test1文件。返回的信息显示,文件被移动到回收站目录。
$ hadoop fs -rm -r delete/test1
Moved: hdfs://localhost:8020/user/hadoop/delete/test1 to trash at: hdfs:// localhost:8020/user/hadoop/.Trash/Current
现在使用skipTrash参数,进行文件删除操作。使用该参数,可以直接删除文件,跳过回收站。
$ hadoop fs -rm -r -skipTrash delete/test2
Deleted delete/test2
可以看到,回收站目录下只有test1文件。而test2文件,被彻底删除了。
$ hadoop fs -ls .Trash/Current/user/hadoop/delete/
Found 1 items\
drwxr-xr-x - hadoop hadoop
减少副本数
当我们减少副本数时,NameNode会选择将多余的副本删除掉。通过Heartbeat将此信息传输到对应的DataNode上。 DataNode删除相应的块,并在群集中释放空间。需要注意的是,setReplication API调用完成后,可能存在时间延迟,需要等待一会儿才该空间才会在群集中释放出来。
13、参考引用
Hadoop JavaDoc API
HDFS source code: http://hadoop.apache.org/version_control.html