转载自:
https://cloud.tencent.com/developer/article/1031641
https://my.oschina.net/freelili/blog/1853668
HDFS优缺点
- 优点
-
1.1 高容错性
- 可以由数百或数千个服务器机器组成,每个服务器机器存储文件系统数据的一部分;
- 数据自动保存多个副本;
- 副本丢失后检测故障快速,自动恢复。
-
1.2 适合批处理
- 移动计算而非数据;
- 数据位置暴露给计算框架;
- 数据访问的高吞吐量;
- 运行的应用程序对其数据集进行流式访问。
-
1.3 适合大数据处理
- 典型文件大小为千兆字节到太字节;
- 支持单个实例中的数千万个文件;
- 10K+节点。
-
1.4 可构建在廉价的机器上
- 通过多副本提高可靠性;
- 提供了容错与恢复机制。
-
1.5 跨异构硬件和软件平台的可移植性强
- 轻松地从一个平台移植到另一个平台。
-
1.6 简单一致性模型
- 应用程序需要一次写入多次读取文件的访问模型;
- 除了追加和截断之外,不需要更改已创建,写入和关闭的文件;
- 简化了数据一致性问题,并实现了高吞吐量数据访问;
- 高度可配置,具有非常适合于许多安装的默认配置。大多数时候,只需要为非常大的集群调整配置。
- 缺点
- 2.1 不适合低延迟的数据访问
- HDFS设计更多的是批处理,而不是用户交互使用。重点在于数据访问的高吞吐量,而不是数据访问的低延迟。
- 2.2 不适合小文件存取
- 占用NameNode大量内存;
- 寻道时间超过读取时间。
- 2.3 无法并发写入、文件随即修改
- 一个文件只能有一个写者;
- 仅支持追加和截断。
基本组成
- Namenode
接受客户端的读写服务
执行文件系统命名空间操作,如打开,关闭和重命名文件和目录。管理文件系统命名空间
记录对文件系统命名空间或其属性的任何更改。存储元数据信息—metadata
Metadata是存储在Namenode上的元数据信息,
它存储到磁盘的文件名为:fsimage。
并且有个叫edits的文件记录对metadata的操作日志。
总体来说,fsimage与edits文件记录了Metadata中的权限信息和文件系统目录树、
文件包含哪些块、
确定块到DataNode的映射、
Block存放在哪些DataNode上(由DataNode启动时上报)。
NameNode将这些信息加载到内存并进行拼装,
就成为了一个完整的元数据信息。
Namenode在内存中保存着整个文件系统的命名空间和文件数据块映射(Blockmap)的映像。
这个关键的元数据结构设计得很紧凑,
因而一个有4G内存的Namenode足够支撑大量的文件和目录。
当Namenode启动时,它从硬盘中读取Edits和FsImage,
将所有Edits中的事务作用在内存中的FsImage上,
并将这个新版本的FsImage从内存中保存到本地磁盘上,
然后删除旧的Edits,
因为这个旧的Edits的事务都已经作用在FsImage上了。
这个过程称为一个检查点(checkpoint)。
Datanode将HDFS数据以文件的形式存储在本地的文件系统中,
它并不知道有关HDFS文件的信息。
它把每个HDFS数据块存储在本地文件系统的一个单独的文件中。
Datanode并不在同一个目录创建所有的文件,
实际上,它用试探的方法来确定每个目录的最佳文件数目,
并且在适当的时候创建子目录。
在同一个目录中创建所有的本地文件并不是最优的选择,
这是因为本地文件系统可能无法高效地在单个目录中支持大量的文件。
当一个Datanode启动时,它会扫描本地文件系统,
产生一个这些本地文件对应的所有HDFS数据块的列表,
然后作为报告发送到Namenode,这个报告就是块状态报告。管理文件系统命名空间
HDFS支持传统的分层文件组织。
用户或应用程序可以在这些目录中创建目录和存储文件。
文件系统命名空间层次结构与大多数其他现有文件系统类似:
可以创建和删除文件,
将文件从一个目录移动到另一个目录,
或重命名文件。
HDFS支持用户配额和访问权限。但不支持硬链接或软链接。
NameNode维护文件系统命名空间。
对文件系统命名空间或其属性的任何更改由NameNode记录。
应用程序可以指定应由HDFS维护的文件的副本数。
文件的副本数称为该文件的复制因子。
此信息由NameNode存储。
- SecondaryNameNode
它不是NameNode的备份,但可以作为NameNode的备份,
当因为断电或服务器损坏的情况,
可以用SecondNameNode中已合并的fsimage文件作为备份文件恢复到NameNode上,
但是很有可能丢失掉在合并过程中新生成的edits信息。
因此不是完全的备份。
由于NameNode仅在启动期间合并fsimage和edits文件,
因此在繁忙的群集上,
edits日志文件可能会随时间变得非常大。
较大编辑文件的另一个副作用是下一次重新启动NameNode需要更长时间。
SecondNameNode的主要功能是帮助NameNode合并edits和fsimage文件,
从而减少NameNode启动时间。
SNN执行合并时机
根据配置文件配置的时间间隔fs.checkpoint.period
默认1小时;
dfs.namenode.checkpoint.txns
,默认设置为1百万,
也就是Edits中的事务条数达到1百万就会触发一次合并,
即使未达到检查点期间。-
SNN合并流程
- 首先生成一个名叫edits.new的文件用于记录合并过程中产生的日志信息;
- 当触发到某一时机时(时间间隔达到1小时或Edits中的事务条数达到1百万)时,
SecondaryNamenode将edits文件、与fsimage文件,
从NameNode上读取到SecondNamenode上; - 将edits文件与fsimage进行合并操作,合并成一个fsimage.ckpt文件;
- 将生成的合并后的文件fsimage.ckpt文件转换到NameNode上;
- 将fsimage.ckpt在NameNode上变成fsimage文件替换NameNode上原有的fsimage文件,
并将edits.new文件上变成edits文件替换NameNode上原有的edits文件。 - SNN在hadoop2.x及以上版本在非高可用状态时还存在,
但是在hadoop2.x及以上版本高可用状态下SNN就不存在了,
在hadoop2.x及以上版本在高可用状态下,处于standby状态的NameNode来做合并操作。
-
DataNode
管理附加到它们运行的节点的存储,
并允许用户数据存储在文件中;
在内部,文件被分割成一个或多个块(Block),
并且这些块被存储在一组DataNode中;
负责提供来自文件系统客户端的读取和写入请求;
执行 块创建,删除;启动DN进程的时候会向NN汇报Block信息;
通过向NN发送心跳保持与其联系(3秒一次),
如果NN10分钟没有收到DN的心跳,
则认为DN已经丢失,
并且复制其上的Block到其他的DN上。
-
HDFS存储单元(block)
文件被切分成固定大小的数据块
默认数据块大小为64MB(hadoop1.x)、
128MB(hadoop2.x)、
256MB(hadoop3.x),可配置;
若文件大小不到一个块大小,则单独存成一个block,
block块是一个逻辑意义上的概念。
文件大小是多少,就占多少空间。一个文件存储方式
按大小被切分成不同的block,存储到不同的节点上;
默认情况下,每个block都有3个副本;
block大小与副本数通过client端上传文件时设置,
文件上传成功后副本数可以变更,block size不可变更。
将大文件拆分成256MB的block块,
每个block块分别随机存放在不同的节点上,
从而避免了数据倾斜的问题.
-
数据复制
-
数据复制概述
HDFS被设计成能够在一个大集群中跨机器可靠地存储超大文件。
它将每个文件存储成一系列的数据块,
除了最后一个,所有的数据块都是同样大小的。
为了容错,文件的所有数据块都会有副本。
每个文件的数据块大小和副本系数都是可配置的。
应用程序可以指定某个文件的副本数目。
副本系数可以在文件创建的时候指定,也可以在之后改变。
HDFS中的文件都是一次性写入的,并且严格要求在任何时候只能有一个写入者。Namenode全权管理数据块的复制,
它周期性地从集群中的每个Datanode接收心跳信号和块状态报告(Blockreport)。
接收到心跳信号意味着该Datanode节点工作正常。
块状态报告包含了一个该Datanode上所有数据块的列表。 Block的副本放置策略
副本的存放是HDFS可靠性和性能的关键。
在大多数情况下,副本系数是3,
HDFS的存放策略是将一个副本存放在本地机架的节点上,
一个副本放在同一机架的另一个节点上,
最后一个副本放在不同机架的节点上。
这种策略减少了机架间的数据传输,这就提高了写操作的效率。
机架的错误远远比节点的错误少,
所以这个策略不会影响到数据的可靠性和可用性。
于此同时,因为数据块只放在两个不同的机架上,
所以此策略减少了读取数据时需要的网络传输总带宽。
在这种策略下,副本并不是均匀分布在不同的机架上。
三分之一的副本在一个节点上,
三分之二的副本在一个机架上,
其他副本均匀分布在剩下的机架中,
这一策略在不损害数据可靠性和读取性能的情况下改进了写的性能。
-
-
副本选择
为了降低整体的带宽消耗和读取延时,HDFS会尽量让读取程序读取离它最近的副本。
其计算方式大致如下:- 相同节点 = 0
- 相同机架不同节点 = 2
- 相同数据中心不同机架 = 4
- 不同数据中心 = 6
如果在读取程序的同一个机架上有一个副本,
那么就读取该副本。
如果一个HDFS集群跨越多个数据中心,
那么客户端也将首先读本地数据中心的副本。 -
安全模式
NameNode在启动的时候会进入一个称为安全模式的特殊状态,
它首先将映像文件(fsimage)载入内存,
并执行编辑日志(edits)中的各项操作;
一旦在内存中成功建立文件系统元数据映射,
则创建一个新的fsimage文件(这个操作不需要SecondNameNode来做)与一个空的编辑日志;此刻namenode运行在安全模式,
即namenode的文件系统对于客户端来说是只读的,
显示目录、显示文件内容等,
写、删除、重命名都会失败;在此阶段namenode搜集各个datanode的报告,
当数据块达到最小副本数以上时,
会被认为是“安全”的,
在一定比例的数据块被认为是安全的以后(可设置),
再过若干时间,安全模式结束;当检测到副本数不足数据块时,该块会被复制,
直到达到最小副本数,系统中数据块的位置并不是由namenode维护的,
而是以块列表形式存储在datanode中。特别的--当namenode节点的磁盘满了,HDFS也会进入安全模式,并且此时无法退出安全模式
-
数据组织
数据块
HDFS被设计成支持大文件,适用HDFS的是那些需要处理大规模的数据集的应用。
这些应用都是只写入数据一次,但却读取一次或多次,
并且读取速度应能满足流式读取的需要。
HDFS支持文件的“一次写入多次读取”语义。
一个典型的数据块大小是256MB。
因而,HDFS中的文件总是按照256M被切分成不同的块,
每个块尽可能地存储于不同的Datanode中。-
分段
详细可以查看:https://juejin.im/post/5bec278c5188253e64332c76
客户端创建文件的请求其实并没有立即发送给Namenode,
事实上,在刚开始阶段HDFS客户端会先将文件数据缓存到本地的一个临时文件。
应用程序的写操作被透明地重定向到这个临时文件。
当这个临时文件累积的数据量超过一个数据块的大小,
客户端才会联系Namenode。
Namenode将文件名插入文件系统的层次结构中,并且分配一个数据块给它。
然后返回Datanode的标识符和目标数据块给客户端。
接着客户端将这块数据从本地临时文件上传到指定的Datanode上。
当文件关闭时,在临时文件中剩余的没有上传的数据也会传输到指定的Datanode上。
然后客户端告诉Namenode文件已经关闭。
此时Namenode才将文件创建操作提交到日志里进行存储。
如果Namenode在文件关闭前宕机了,则该文件将丢失。上述方法是对在HDFS上运行的目标应用进行认真考虑后得到的结果。
这些应用需要进行文件的流式写入。
如果不采用客户端缓存,由于网络速度和网络堵塞会对吞估量造成比较大的影响。
这种方法并不是没有先例的,
早期的文件系统,比如AFS,就用客户端缓存来提高性能。
为了达到更高的数据上传效率,已经放松了POSIX标准的要求。 -
管道复制
当客户端向HDFS文件写入数据的时候,
一开始是写到本地临时文件中。
假设该文件的副本系数设置为3,
当本地临时文件累积到一个数据块的大小时,
客户端会从Namenode获取一个Datanode列表用于存放副本。
然后客户端开始向第一个Datanode传输数据,
第一个Datanode一小部分一小部分(4 KB)地接收数据,
将每一部分写入本地仓库,
并同时传输该部分到列表中第二个Datanode节点。
第二个Datanode也是这样,
一小部分一小部分地接收数据,写入本地仓库,
并同时传给第三个Datanode。
最后,第三个Datanode接收数据并存储在本地。
因此,Datanode能流水线式地从前一个节点接收数据,
并在同时转发给下一个节点,
数据以流水线的方式从前一个Datanode复制到下一个。如果复制过程中某个DataNode出现问题,
并不会导致该次写入过程失败,
问题DataNode将排出这个管道,
其他节点正常写入,
只要有(dfs.namenode.repliction.min
默认为1),个节点写入成功,
那么本次写入过程就是成功的。
后续 NameNode 在检查文件副本数的时候,会帮助恢复正常
读写流程
- 读数据流程
使用HDFS提供的客户端Client,
通过 DistributedFileSystem 向远程的Namenode发起RPC请求;Namenode会视情况返回文件的部分或者全部block列表,
对于每个block, Namenode都会返回有该block副本的DataNode地址;
并按照就近原则进行排序;客户端Client会选取离客户端最近的DataNode来读取block;
如果客户端本身就是DataNode, 那么将从本地直接获取数据;
该过程是并行的,也就是说多个block块的数据可以一起读取。读取完当前block的数据后, 关闭当前的DataNode链接,
并为读取下一个block寻找最佳的DataNode;当读完列表block后, 且文件读取还没有结束,
客户端会继续向Namenode获取下一批的block列表;以上这些步骤对于客户端来说都是透明的。
客户端只需通过 DistributedFileSystem 返回的 FSDataInputStream 读取数据即可特别的--如果客户端和所连接的DataNode在读取时出现故障,
那么它就会去尝试连接存储这个块的下一个最近的DataNode,
同时它会记录这个节点的故障,以免后面再次连接该节点。
客户端还会验证从DataNode传送过来的数据校验和。
如果发现一个损坏块,那么客户端将再尝试从别的DataNode读取数据块,
并且会告诉NameNode 这个信息,
NameNode也会更新保存的文件信息,进行数据修复。-
写数据流程
客户端调用create来新建文件。
DistributedFileSystem 通过RPC调用,
在 NameNode 的文件系统命名空间中创建一个后缀是.copy
新文件,
此时还没有相关的DataNode与之相关。NameNode 会通过多种验证保证新的文件不存在文件系统中,
并且确保请求客户端拥有创建文件的权限。
当所有验证通过时,NameNode 会创建一个新文件的记录,
如果创建失败,则抛出一个IOException异常;
如果成功, namenode 掌握着集群DataNode整体状况,
将第一批 block 块分配数据块后,
连同 DataNode 列表信息返回给客户端;当客户端写入数据时,DFSOutputStream 会将文件分割成数据包(64k),
然后放入一个内部队列,我们称为“数据队列(data queue)”。
DataStreamer会将这些小的文件包放入数据流中,
DataStreamer的作用是请求NameNode为新的文件包分配合适的DataNode存放副本。
返回的DataNode列表形成一个“管道”,
假设这里的副本数是3,
那么这个管道中就会有3个DataNode。
DataStreamer将文件包以流的方式传送给队列中的第一个DataNode。
第一个DataNode会存储这个包,
然后将它推送到第二个DataNode中,
随后照这样进行,直到管道中的最后一个DataNode,
这种 pipeline 的方式加快了写入过程,
并隐藏了副本数对客户端的影响,
即 对客户端来说,副本数是透明的。
副本的放置遵循 Block的副本放置策略DFSOutputStream同时也会保存一个包的内部队列,
用来等待管道中的DataNode返回确认信息,
这个队列被称为确认队列(ask queue)。
只有当所有的管道中的DataNode都返回了写入成功的信息文件包,
才会从确认队列中删除。客户端完成数据写入后,对数据流调用close方法。
该操作将剩余的所有数据写入dataNode管线,
当DataNode 向 NameNode 心跳汇报的时候,
新写入的 block 信息被更新到 NameNode.
第一批block 块的写入完成重复以上过程,客户端继续第二批 block 块的写入,直至最后一批写入完成结束!
-
特别的--当出现写入某个DataNode失败时,HDFS会作出以下反应:
首先管道会被关闭,任何在 确认队列 中的文件包都会被添加到数据队列的前端,以确保故障节点下游的datanode不会漏掉任何一个数据包。为存储在另一个正常的datanode的当前数据块指定一个新的标识,并将标识传送给nameNode,当dataNode 在恢复后,会发现其原来的标识是过时的,于是就可以删除存储的不完整的那部分数据块。
从管线中删除故障datanode,基于两个正常的datanode构建新的管线。余下的数据库写入管线中正常的datanode。
namenode在注意到副本不足时,会在另一个节点上创建一个新的副本。后续的数据块继续正常的接受处理。
如果有多个节点的写入失败了,如果满足了最小备份数的设置(
dfs.namenode.repliction.min
),写入也将会成功 写入一致性
新建一个文件后,它能够在文件系统命名空间中立即可见
写入文件的内容不保证立即可见(即逝数据流已经调用flush()方法刷新并存储)
当前正在写入的块对其他reader不可见。
通过hflush()方法后,数据被写入datanode的内存中。可保证对所有reader可见
通过hsync()方法后,数据被写入到磁盘上。
如果没有调用hflush或者hsync()方法。客户端在故障的情况下就会存在数据块丢失
后记
本文主要是记录了个人在复习过程看到的一些知识点,可能有点东拼西凑的感觉,但是作为复习来看看还是不错的。当然关于 HDFS 肯定不止这么一点东西,不过作为开发,运维相关的其实很少用到,不过还是有一些需要补充的,比如:高可用,HDFS shell相关。。。诶,其实前面有几篇文章说后续会补上,但是因为各种原因,还在那里欠着,还好没人计较,也就是自己在自娱自乐。。。哈哈