在上一篇博客中,我们已经搭建了一个Hadoop集群,这里开始学习HDFS的基本原理。
HDFS的原理
hadoop的三大组件
hadoop有三大组件:
- HDFS文件系统;
- MapReduce编程框架;
- Yarn资源管理系统(2.x版本后);
这篇文章将针对HDFS进行学习。
什么是HDFS
HDFS即Hadoop分布式文件系统(Hadoop Distributed Filesystem),是为了处理大文件而设计的一种文件系统。传统的文件系统是单机的,能够存储的文件是有限的,而HDFS是分布式的系统,是可以扩展的,可以横跨很多机器的,所以能够处理大数据。HDFS的设计思想来自于Google在2013年10月发表的GFS论文。
HFDS采用的是主从架构,一个HDFS集群主要包括两种节点,一种叫做NameNode,一种叫做DataNode。其实还有一个SecondaryNameNode,是用来辅助NameNode的。
NameNode:管理文件系统元数据的节点,相应客户端需求(写文件,读文件、创建、重命名等)的节点。所谓的元数据,就是这个文件存储在哪些机器上,文件大小,创建时间,叫什么名字,对应的权限。NameNode不存储文件实际数据,所以读写文件的时候,只是告诉客户端具体的文件位置,客户端获取这个位置信息后,就直接和DataNode交互了。
DataNode:存储实际的数据。一个集群通常有很多个DataNode。
SecondaryNameNode:辅助NameNode来进行元数据的管理,后续会具体介绍。
HDFS基本架构
图中绘制了两个机架(Rack),HDFS在存储文件时,可以选择机架感知策略。
上图的架构里面出现了NameNode,DateNode和Client。Client需要读写文件会与NameNode进行交互,获取文件信息。然后去DataNode里面读写相应文件。
HDFS存储机制
每一个文件存储在HDFS里,会被分成文件块。写入HDFS的文件会被分割成64M或者128M的文件块(Block),每个文件块存储在几个节点上,称之为副本。一般来说,HDFS默认是存储3个副本。在上图里面,Client上传的文件T,按照128M一个块,被分成了4个块,最后一个块是小于128M的,但是也作为一个块存储。每个文件快,可以看到都有3个副本。
像文件块1,在DataNode_rack0_0,DataNode_rack0_1,DataNode_rack1_0这3个节点里面都有副本。而关于这个文件的元数据信息,则存储在NameNode节点里面。
机架感知
一般一个集群里,一个文件块会设置3个副本。
第一个副本块存本机
第二个副本块存不同机架的一个服务器节点上
第三个副本块存跟本机同机架内的其他服务器节点
机架感知策略的好处在于:
如果本机数据损坏或者丢失,那么客户端可以从同机架的相邻节点获取数据,速度肯定要比跨机架获取数据要快。
如果本机所在的机架出现问题,那么之前在存储的时候没有把所有副本都放在一个机架内,这就能保证数据的安全性,此种情况出现,就能保证客户端也能取到数据
在机架感知下,文件的读取也有一定的规则:
如果在本机有数据,那么直接读取
如果在跟本机同机架的服务器节点中有该数据块,则直接读取
如果该HDFS集群跨多个数据中心,那么客户端也一定会优先读取本数据中心的数据
HDFS读文件流程
- Clinet向NameNode发起请求,Namenode会视情况返回文件的部分或者全部block列表, 对于每个block, Namenode都会返回有该block拷贝的DataNode地址;
- 根据NameNode返回的信息,Clinet选取离客户端最近的DataNode来读取block。
- 读取完当前block的数据后, 关闭当前的DataNode链接, 并为读取下一个block寻找最佳的DataNode;
- 当读完列表block后, 且文件读取还没有结束, 客户端会继续向Namenode获取下一批的block列表;
- 读取完一个block都会进行checksum验证, 如果读取datanode时出现错误, 客户端会通知Namenode, 然后再从下一个拥有该block拷贝的datanode继续读。
HDFS写文件流程
Clinet首先将要存储的数据切分成若干块,Clinet向NameNode发起请求;Namenode会检查要创建的文件是否已经存在, 创建者是否有权限进行操作, 成功则会为文件创建一个记录, 否则会让客户端抛出异常;NameNode返回给客户端第一个block可以存储的DataNode列表。
根据NameNode返回的DataNode列表,选择最近的DataNode,创建socket连接,发送第一个block
第一个DataNode接收到数据,发送到第二个DataNode,第二个DataNode将接收到的数据发送到第三个DataNode,把它们排成一个pipeline管道。只要写入了dfs.replication.min的复本数(默认为1),写操作就会成功,
第三个副本完成后,向第二个DataNode返回成功的信息(ack),收到第三个副本所在的DataNode返回的信息,第二个DataNode向第一个DataNode返回成功信息
第一个副本所在的DataNode向客户端返回成功的信息。开始下一个数据的传输。
如果传输过程中, 有某个Datanode出现了故障, 那么当前的pipeline会被关闭, 出现故障的Datanode会从当前的pipeline中移除, 剩余的block会继续剩下的Datanode中继续以pipeline的形式传输, 同时Namenode会分配一个新的Datanode, 保持副本设定的数量。
NameNode工作机制详解
在NameNode节点里,元数据首先是存在内存里面的。此外,NameNode还会有一个fsimage文件,是最新的元数据检查点文件。为了避免数据丢失,也会把内存里新增的数据保存到硬盘里,如果写入fsimage里,因为会有修改之前的记录,所以性能较差。所以写入到edits文件里,因为是顺序增加,所以性能很快。当系统运行到一段时间之后,edits文件会比较大,需要合并edits到fsimage里去。这时候就是SecondaryNameNode在发挥作用。如果在NameNode上面合并,会影响NameNode的运行。具体的操作流程,如下图。
HDFS的一些策略
心跳机制
主节点和从节点之间的通信是通过心跳机制实现的,如NameNode与DataNode之间,JobTracker和TaskTracker之间。所谓“心跳”是一种形象化描述,指的是持续的按照一定频率在运行,类似于心脏在永无休止的跳动。
在Hadoop中,NameNode如何知道可以使用哪一个DataNode来存储?哪一个datanode的有多少可用空间?这里就用到了心跳机制,DataNode会定时的向NameNode发送报告 目的告诉NameNode自己的存活状况以及可用空间。这个时间默认是3s。
当一个DataNode长时间没有发送心跳报告,NameNode就会判断这个节点是宕机了,这时,NameNode就会设置这个节点为“dead node”,并且寻找这个节点上数据的副本,复制到其他可用的节点上。当然了,如果一次心跳报告没有收到,就判断为宕机,肯定是不合理的。而且,仅仅是没收到就判断宕机,也是有些不合理,还需要NameNode主动去确认一下。DataNode每隔3秒向NameNode发送一次心跳报告,只有NameNode收到了一次,就会重新计数。当NameNode连续10次没有收到DataNode的心跳报告,会觉得DataNode可能宕机了,此时NameNode向DataNode主动发送一次检查,发送一次检查的时间默认5分钟。如果一次检查没有返回信息,这时候NameNode会再进行一次检查,如果还是没有读取不到DataNode的信息,此时判定死亡。也就是说NameNode最终判断DataNode死亡需要$103s+25min=630s$,也就是说NameNode在连续630s中没有得到DataNode的信息才认为当前的DataNode宕机。
所以心跳机制主要要两个用处:
- 向NameNode汇报空间信息。
- NameNode判断DataNode是否宕机。
安全模式
NameNode启动后会进入一个称为安全模式的特殊状态。处于安全模式的NameNode是不会进行数据块的复制的。NameNode从所有的 DataNode接收心跳信号和块状态报告。块状态报告包括了某个DataNode所有的数据块列表。每个数据块都有一个指定的最小副本数。当NameNode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated)的;在一定百分比(这个参数可配置)的数据块被NameNode检测确认是安全之后(加上一个额外的30秒等待时间),Namenode将退出安全模式状态。接下来它会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他DataNode上。
前面已经了解了NameNode的工作机制和SecondaryNameNode的工作机制。在集群启动的时候,NameNode需要加载的元数据包括:1、抽象目录树 2、数据和块的映射关系 3、数据块存储的位置信息。
我们知道元数据即在磁盘存储又在内存存储,但是磁盘里面并没有存储数据库存储的位置信息。这是因为当集群第一次启动的时候,将磁盘元数据加载到内存中,如果磁盘元数据过大,会造成加载到内存的时间过长,所以磁盘只存储抽象目录树和数据和块的映射关系两部分。至于数据块存储的位置信息是通过DataNode的心跳报告获取的,集群在启动的时候NameNode,会接收DataNode的心跳报告,心跳报告中还包含数据块的存储位置信息。这时候NameNode就可以获取DataNode的数据块的存储状况。
1 | 内存存储:1、抽象目录树 2、数据和块的映射关系 3、数据块存储的位置信息 |
安全模式下可以查看元数据,例如ls,get,cat命令,但是不能修改元数据(上传文件,创建目录,追加文件,修改文件名)。
在集群维护的时候,也可以手动进入安全模式:
1 | hdfs dfsadmin -safemode enter进入安全模式 |
负载均衡
HDFS很容易出现负载不均衡的现象,也就是DataNode的磁盘利用率不均衡。例如集群新增节点,集群删除节点。当负载不均衡时,Map任务可能会分配到没有存储数据的机器,这将导致网络带宽的消耗,也无法很好的进行本地计算。当HDFS的负载不均衡时,就需要对HDFS的负载进行调整,让数据均匀的分布在各个DataNode上。其步骤如下:4
- 数据均衡服务(Rebalancing Server)首先要求 NameNode 生成 DataNode 数据分布分析报告,获取每个DataNode磁盘使用情况;
- Rebalancing Server汇总需要移动的数据分布情况,计算具体数据块迁移路线图。数据块迁移路线图,确保网络内最短路径;
- 开始数据块迁移任务,源 DataNode复制一块需要移动数据块;
- 将复制的数据块复制到目标DataNode上;
- 删除原始数据块;
- 目标DataNode向源 DataNode确认该数据块迁移完成;
- 源Data Node向Rebalancing Server确认本次数据块迁移完成。然后继续执行这个过程,直至集群达到数据均衡标准。
在第2步中,HDFS会把当前的DataNode节点,根据阈值的设定情况划分到Over、Above、Below、Under四个组中。在移动数据块的时候,Over组、Above组中的块向Below组、Under组移动。四个组定义如下:
Over:在设定的上限阈值之上
Above:在设定的阈值和集群平均值之间
Below:在集群平均值和设置的下限阈值之间
Under:低于下限阈值