1. 简介
1.1. 概述
HDFS是基于 流数据 访问模式的 分布式文件系统 ,其设计建立在 “一次写入、多次读取” 的基础上,提供高吞吐量、高容错性的数据访问,能很好地解决海量数据的存储问题。
流数据:是指数千个数据源 持续生成 的数据,可以理解为随时间延续而 无限增长 的动态数据集合。通俗点说,数据就是水龙头里流出的水。 特点:实时的,动态生成的,没有明确的开始和结束。 与流数据对应的是批数据:批数据是指以大批量形式存储和处理的数据。在批处理中,数据集是静态的,意味着数据在处理前已经被完全收集和存储。通俗点说,数据就是一个瓶子里流出的水。 特点:静态数据集,处理前数据已知。
1.2. 优点
大规模数据存储: HDFS能够处理PB级别的数据存储需求,通过将数据分割成块并在集群的多个节点上分布存储,实现了数据的水平扩展。
高吞吐量数据访问: 对于大数据分析任务,HDFS提供了高带宽的数据访问能力,允许用户快速读取大量数据进行处理。
高容错性: HDFS具有内置的冗余机制,通常每个数据块有多个副本,这确保了即使某些节点失败,数据仍然可用。这种高容错性对于大规模分布式环境至关重要。
成本低: 通过利用廉价的商用硬件构建大型集群,HDFS降低了大规模数据存储的成本。
支持流式数据访问: HDFS被设计用于流式数据访问模式,适合于批量处理和分析。
1.3. 缺点
不适合处理 低延迟 的数据访问。比如用户 要求时间比较短 的低延迟应用(主要处理高数据吞吐量的应用)。
不适合处理 大量的小文件。会造成寻址时间超过读取时间;会占用NameNode大量内存,因为NameNode把文件系统的元数据存放在内存中(文件系统的容量由NameNode的大小决定),小文件太多会消耗NameNode的内存。
不适合并发写入。一个文件只能有一个写入者,HDFS暂不支持多个用户对同一个文件的写操作。
不适合 任意修改 文件。仅支持append(附加),不支持在文件的任意位置进行修改。
2.组成和架构
HDFS的组成架构图及各部分功能如下所示:
(本文所有图片均来自网络)
2.1.NameNode节点(名称节点)
负责保存文件属性。
2.1.1.职责
是HDFS的管理者,负责保存和管理HDFS的元数据。
管理维护HDFS的命名空间:NameNode管理HDFS系统的命名空间,维护文件系统树以及文件系统树中所有文件的元数据。管理这些信息的的文件分别是 edits(操作日志文件) 和 fsimage(命名空间镜像文件) 。
editlog(操作日志):在NameNode启动的情况下,对HDFS进行的各种操作进行记录。(HDFS客户端执行的所有操作都会被记录到editlog文件中,这些文件由edits文件保存)。(自上次快照以来的所有更改) fsimage:包含HDFS中的元信息(比如修改时间、访问时间、数据块信息等)。(最新的元数据快照)
管理DataNode上的数据块:管理所有元数据信息,DataNode上数据块的均衡,维持副本数量。
接受客户端的请求:接受客户端文件上传、下载、创建目录等的请求。
2.2. DataNode节点(数据节点)
负责保存文件数据。
2.2.1.职责
负责存储数据,把数据块以Linux文件的形式保存在磁盘上,并根据Block标识和字节范围来读写块数据。
保存数据块:一个数据块会在多个DataNode进行冗余备份,但是在某一个DataNode最多只有一个备份。
负责客户端对数据块的IO请求:在客户端执行写操作时,DataNode之间会相互通信,保证写操作的一致性。
定期和NameNode进行心跳通信,接受NameNode的指令:如果NameNode十分钟没有收到DataNode的心跳信息,将会将其上的数据块复制到其他DataNode节点。
为什么DataNode把数据块设置的比较大?(正常数据块是64k,但是datanode的数据块是128M) 答:1.减少寻址开销:较大的数据块意味着对于相同的数据量,需要处理的块数量更少,这样可以减少磁盘的寻道时间。 2.提高传输效率:避免数据传输花费过多时间在于寻址上 3.减少NameNode的内存消耗:NameNode负责跟踪和管理HDFS中的所有文件和块的位置信息,如果块比较大可以减少NameNode需要维护的元数据量,进而减少内存消耗,有助于保持NameNode的可扩展性和响应速度。 但是块也不是越大越好,如果块过大,当需要数据恢复时,消耗的时间就会增加。同时,因为每个文件至少占用一个完整的块,所以对于一些小文件,可能会导致存储空间的浪费。
2.3.SecondaryNameNode节点(第二名称节点)
定期创建命名空间的检查点操作的节点,专门用于FsImage和Edits的合并。出于可靠性考虑,SecondaryNameNode节点与NameNode节点通常运行在不同的机器上
,且SecondaryNameNode节点与NameNode节点的内存要一样大
。
一般情况下,一个集群中的SecondaryNameNode节点也是只有一个的原因是什么? 答:因为SecondaryNameNode的合并操作并非频繁发生,而是按照预设的时间间隔进行,因此没有必要部署多个节点来并行执行这些任务。多个SecondaryNameNode反而会增加系统复杂度,引入不必要的同步和通信开销,可能会导致数据不一致或其他问题。
2.3.1.职责
防止edits过大:定期合并 fsimage 和 edits 文件,使 edits 大小保持在限制范围内。这样做减少了重新启动NameNode时合并 fsimage 和 edits 耗费的时间,从而减少了NameNode启动的时间。
做冷备份:对一定范围内数据做快照性备份,在NameNode失效时能恢复部分 fsimage。
2.3.2 工作流程
NameNode启动:当NameNode首次启动或格式化时,它会创建一个初始的
fsimage
文件,这个文件包含了文件系统的所有元数据信息。同时,NameNode开始记录所有的文件系统变更操作到编辑日志(Edit Log)中。编辑日志积累:随着时间推移,NameNode会持续记录文件系统的变更操作,这些操作被追加到一系列的编辑日志文件中。
触发检查点(Checkpoint):当编辑日志文件达到一定大小(默认64MB)或经过了一段时间(默认1小时),会触发一个检查点操作,SecondaryNameNode开始合并。
SecondaryNameNode参与:SecondaryNameNode会从NameNode拉取最新的
fsimage
文件和所有未合并的编辑日志文件。SecondaryNameNode将这些文件加载到内存中,并且将编辑日志中的所有变更应用到fsimage
中,形成一个更新后的fsimage
文件。合并操作:在内存中完成
fsimage
和编辑日志的合并后,SecondaryNameNode将更新后的fsimage
保存到自己的本地磁盘上。返回合并后的fsimage给NameNode:SecondaryNameNode将合并后的
fsimage
文件发送回NameNode。NameNode接收到更新后的fsimage
文件后,会用它来替换原有的fsimage
文件,并清空已经合并的编辑日志。NameNode继续运行:NameNode使用更新后的
fsimage
文件继续提供服务,并开始记录新的编辑日志。循环往复:这个过程会周期性地重复,以确保NameNode的元数据始终保持在一个合理大小,提高NameNode的性能和响应时间。
2.4. HttpFS节点
是一个HTTP网关,允许客户端通过HTTP协议访问HSFS,让那些非Java应用和那些不能直接使用HDFS Java客户端的应用程序能够访问HDFS。
2.4.1. 职责
客户端可以通过发送HTTP请求到HttpFS的URL来访问HDFS,这些请求会被转发到HDFS集群并返回相应的响应。
2.5. journalNode节点
日志节点。用于支持NameNode高可用性的关键组件之一。
2.5.1. 职责
日志记录:包含了所有对文件系统元数据所做的更改,比如文件的创建、删除、重命名等操作。这样当Active NameNode失败的情况下,Standby NameNode可以迅速的转换为Active状态,并且确保它的元数据和前一个Active NameNode的元数据完全一致。
故障恢复:当Active NameNode发生故障,Standby NameNode会读取JournalNode中的Edits log然后应用到自己的Fslmage上。
3.HDFS的读写流程
3.1.写数据流程
客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在,检查客户端权限是否可以写入文件。
NameNode返回是否可以上传。
客户端请求第一个 Block上传到哪几个DataNode服务器上。
NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
dn1、dn2、dn3逐级应答客户端。
客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。(虽然存储是存储在128MB的块中,但是写数据传输的数据包大小只是64KB)。
当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
NameNode给客户端返回几个DataNode节点由配置的冗余级别来决定。一般默认是三份。 第一个副本:放置在上传文件的数据节点(第一个副本如果是在 集群外 提交,则随机挑选一个 CPU 比较空闲 、 磁盘不太满 的节点); 第二个副本:放置在与 第一个副本 不同 的机架的节点上; 第三个副本:放在与 第二个副本 相同 的机架的其他节点上。
通信管道的作用: 1.**减少客户端的负载**:通信管道允许数据以流的方式从客户端传递到第一个DataNode然后依次通过后续的DataNode,直到达到所有必要的副本位置。所以通过建立通信管道,客户端只需要与第一个DataNode通信,之后的复制过程由DataNode之间完成,这极大地减轻了客户端的负担。 2.**数据块的同步复制**:通信管道确保了数据块可以同时在多个DataNode上进行复制,这有助于快速实现数据块的冗余存储,提高数据的可靠性和可用性。 3.**故障恢复**:如果在数据传输过程中任何一个DataNode发生故障,管道机制允许失败点之前的DataNode重新启动数据流,从而继续数据块的复制,确保数据完整性和一致性。 4.**减少NameNode的负担**:通过管道机制,数据块的复制过程主要由DataNode自主进行,减少了NameNode的直接参与,使得NameNode可以专注于文件系统的元数据管理。
3.2.读数据流程
客户端通过DistributedFileSystem向NameNode请求读取文件。
NameNode通过查询元数据,找到文件块所在的DataNode列表,返回给客户端。
客户端挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
客户端以Packet为单位接收,先在本地缓存,然后写入目标文件
4.高可用性
JournalNode和Standby NameNode和Failover Controller(故障转移)都是为了保障HDFS的高可用性。
4.1.备用NameNode (Standby NameNode)
备用NameNode并不直接处理客户端请求,但它的角色非常重要,因为它需要保持与活动NameNode相同的元数据状态。备用NameNode定期从活动NameNode那里同步最新的元数据信息,包括FsImage(文件系统镜像)和EditLog(编辑日志)文件。这样做是为了确保在活动NameNode发生故障时,备用NameNode可以立即接管其职责,而不会丢失任何元数据信息。
当活动NameNode不可用时,备用NameNode会通过一个称为“故障转移”(Failover)的过程被提升为活动状态。在故障转移后,新的活动NameNode会开始处理客户端请求,而原来的活动NameNode如果恢复,可以转变为新的备用NameNode。
4.2.JournalNode
JournalNode的主要作用是作为一个独立的日志存储系统,用来存储NameNode的事务日志(EditLogs)。在HDFS HA架构中,Active NameNode会将所有对文件系统元数据的更改写入到JournalNode集群中,确保这些更改被持久化并可被多个NameNode访问。这样做的目的是为了提高系统的容错能力,因为在Active NameNode失败的情况下,Standby NameNode可以从JournalNode集群中恢复最新的元数据状态,而无需依赖于Active NameNode本身。
在Hadoop的高可用性(HA)配置中,如果配置了JournalNode,那么在Active NameNode发生故障时,Standby NameNode并不会直接从Active NameNode读取日志信息,而是从JournalNode集群读取必要的日志信息。 同时,Active NameNode会将所有对文件系统元数据的更改写入到JournalNode集群中,确保这些更改被持久化并可被多个NameNode访问。
4.3.Failover Controller
故障转移是指从备用NameNode切换到活动状态的过程。这个过程可以是手动的,也可以是自动的,取决于具体的配置。Hadoop提供了一个Zookeeper Failover Controller(ZKFC)组件,它可以监控NameNode的状态,并在检测到活动NameNode故障时自动触发故障转移,将备用NameNode转换为活动状态。