HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。
HDFS中文件在物理上是分块存储的(Block),块的大小可以通过配置参数(dfs.blocksize)来规定,默认大小在Hadoop2.x/3.x版本中是128M,1.x版本中是64M。
因为数据的分块存储,存储数据时有可能产生大量的小文件。
块设置的太小,会增加寻址时间,程序会一直在找块开始的位置。
块设置的太大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需要的时间。导致程序处理这个数据块的时间变长。
因此HDFS块大小的设置主要取决于磁盘传输速率的大小。
1.在 HDFS 中,每个数据块都会被分成若干个数据块副本。默认情况下,每个数据块会被复制到HDFS 集群中的三个不同的 DataNode 上。
2.HDFS 会将数据块的多个副本分别存储在不同的 DataNode 上,以提高数据的可靠性和容错性。如果某个 DataNode 发生故障,数据块的其他副本仍然可以被访问。
3.HDFS 会尽量将数据块的多个副本存储在不同的机架上,以避免机架故障导致数据不可用的情况。在 HDFS 中,每个机架都有一个主节点和多个从节点。HDFS 会将数据块的多个副本存储在不同的机架的从节点上。
机架感知:
这样既能增加网络IO的效率,也能通过相邻机架提高数据稳定性。
1.客户端向 NameNode 发送读取请求,请求包括要读取的文件名和文件块的起始位置。
2.NameNode 根据请求的文件名,查询文件所对应的块列表,并返回给客户端块列表以及每个块所在的 DataNode 列表。
3.客户端根据返回的块列表和 DataNode 列表,选择一个 DataNode 开始读取数据。如果选择的 DataNode 不可用,则会选择另外一个 DataNode。
3.客户端向选择的 DataNode 发送读取请求,请求包括要读取的块的起始位置和长度。
4.DataNode 接收到读取请求后,从本地磁盘读取数据块,并将数据块返回给客户端。
5.如果 DataNode 上的数据块副本不足,则会从其他 DataNode 上获取数据块副本。
客户端接收到数据块后,将数据块缓存在本地内存中,并通过缓存来提高读取性能。 如果要读取的数据超过了一个块的大小,则客户端需要多次向DataNode 发送读取请求,直到读取完所有的数据
思考:NameNode中的元数据是存储在哪里的?
NameNode中保存处理数的元数据信息,假设存储只在磁盘中,那么就无法高效率的应对客户的随机读写请求;如果存储只在内存中,一旦断电宕机,整个集群将无法使用。
因此: 引入Fsimage文件,磁盘文件,备份元数据信息。
思考:Fsimage为磁盘文件,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。
因此: 引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。
如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并。
Fsimage文件:HDFS文件系统元数据的一个永久性检查点,其中包含HDFS文件系统的所有目录和文件的inode序列号信息。
Edits文件:存放HDFS文件系统的所有更细操作的路径,文件系统客户端执行的所有写操作首先会被记录到该文件中
NameNode的每次启动都会将Fsimage文件读入内存,加载Edits文件里边的更新操作,保证内存中的数据是最新的,同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件合并了。
(1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在,客户端是否有写权限。
(2)NameNode返回是否可以上传。
(3)客户端请求第一个 Block上传到哪几个DataNode服务器上(数据的切分在客户端完成)。
(4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3(返回dn数据存储列表)。
(5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。(Pipline管道传输)
(6)dn1、dn2、dn3逐级应答客户端。
(7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答(ACK校验)。
(8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
1.如果数据块的副本数量不足,NameNode会选择其他可用的DataNode复制数据块,以满足数据块的副本数量要求。
2.如果DataNode发生故障,NameNode会将该DataNode上的数据块复制到其他可用的DataNode上,以保证数据的可靠性和容错性。
值得注意的是:
在HDFS的写流程中,edit文件记录了所有的NameNode操作,包括文件的创建、删除、修改等。当客户端向NameNode发送写请求时,NameNode会将操作记录到内存中的edit文件中。当内存中的edit文件达到一定大小时,NameNode会将其刷写到磁盘上的edit文件中。同时,SecondaryNameNode/JNS会帮助NameNode定期将内存中的edit文件刷写到磁盘中合并fsimage,以避免因为系统崩溃等原因导致内存中的数据丢失。
因此,edit文件的更改与HDFS的写流程是相互独立的。edit文件的更改只与NameNode的操作有关,而不与DataNode的写入操作有关。当NameNode接收到客户端的写请求时,会更新内存中的edit文件,而不是在DataNode写入数据时更新edit文件。因此,edit文件的更改和数据块的写入是两个独立的过程。
在Hadoop中,所有的写操作都首先被写入内存中的Edit文件,然后再由NameNode周期性地将这些操作同步到磁盘上的Edit文件中。这个过程被称为“刷写(flush)”。
在刷写之前,如果NameNode节点宕机了,那么内存中的Edit文件也会丢失,此时可能会造成数据的损失。为了避免这种情况,Hadoop使用了Write Ahead Logging(WAL)技术。WAL技术可以将写操作先写入一个磁盘上的日志文件(称为“先写日志”),然后再执行实际的写操作。这样,即使NameNode节点宕机了,也可以从日志文件中恢复出丢失的写操作。当然,这也会带来一定的性能开销,因为每次写操作都需要写入两次磁盘。
需要注意的是,Edit文件和WAL日志文件是不同的文件。Edit文件是Hadoop用来记录NameNode的元数据操作的文件,而WAL日志文件则是用来记录所有写操作的文件,WAL日志是只写文件。