目录
概念:
Block的拆分标准:
数据块Block介绍:
拆分的数据块需要等大:
HDFS存储注意事项:
Block数据安全:
NameNode:
DataNode:
汇报:
日志机制:
拍摄快照:
SNN的解决方案:
安全模式:
机架感知:
第一个节点:
第二个节点:
第三个节点:
第N个节点:
HDFS写流程(宏观):
HDFS写流程(微观):
HDFS读流程:
HA:
Active NameNode(ANN):
工作:
存储介质:
Standby NameNode(SNN):
工作:
存储介质:
合并日志文件和镜像:
DataNode:
Quorum JournalNode Manger(QJM):
Failover Controller(故障转移控制器):
主备节点正常切换:
zookeeper(管理型软件):
脑裂:
脑裂的场景有:
脑裂的解决方案是隔离:
Hadoop-Federation:
块池Block Pool:
命名空间卷NameSpace Volume:
额外补充点:
概念:
FS:File System
文件系统是基于硬盘之上的一个文件管理工具。以便于和硬盘解耦。
DFS:Distributed File System
分布式文件系统,它将数据存放在多台电脑上,HDFS是一种分布式文件系统,它是mapreduce计算的基础。
Block的拆分标准: 数据块Block介绍:
数据被切分后的一个整体称之为块,Hadoop版本1时默认每64M一个块,版本2时改为了128M,可以自定义设计,一般为2的n次方数,实际场景中应该根据文件大小和集群节点的数据量综合考虑块的大小。
不同文件块的大小可以不一致,同一个文件每个块(除了最后一个节点外)的大小一致。
拆分的数据块需要等大:
数据计算时,简化问题的复杂度。
如果不等大,进行分布式算法设计的时候,数据不同意而导致算法很难设计。
数据拉取的时间相对一致。
通过偏移量就直到这个块的位置。
相同文件分成的数据块大小应该相等。
HDFS存储注意事项:
hdfs上存储的文件可以追加但是不能修改,修改会导致:
影响偏移量。
数据倾斜。
蝴蝶效应等。
如果要使用追加功能,需要修改大概hdfs的追加设置。
块的大小一旦文件上传之后就不允许修改了。
Block数据安全:
每个数据块都会有3个(默认是3个,可修改)备份,也就是副本。相同的副本不会放到相同的节点,所以备份数量要小于等于节点的数量。这样使用数时还可以就近获取数据。
NameNode:
NN的效率较高,因为它所有的操作都是在内存中完成的,不会和磁盘进行任何的数据交互,但是这可能会导致数据的丢失。
1、接收客户端的读写服务(NameNode知道文件与DataNode的对应关系):
2、保存文件时保存文件的元数据信息:
元数据信息包括:文件的归属、文件权限、文件大小与时间、Block信息(block的位置信息不会持久化,需要每次开启集群的时候DN上报)
3、收集Block的位置信息:
系统启动:
NN关机时不会存储任何Block与DN的映射信息,DN启动时会将自己节点上存储的Block信息汇报给NN,NN接收请求之后重新生成映射关系(File--->Block,Block--->DN),如果某个数据块的副本数小于设置数,那么NN会将这个副本拷贝到其他节点。
集群运行中:
NN与DN保持心跳机制,三秒发送一次,如果客户端需要读取或者上传数据的时候,NN可以知道DN的健康情况,可以让客户端读取存活的DN节点,如果DN超过三秒没有心跳,就会认为DN出现了异常,就不会让新的数据读写到此DN上,如果DN超过10分钟没有心跳,那么NN会将当前DN存储的数据转存到其他节点。
DataNode:
存放文件的数据信息,它会将数据具体存放到磁盘上,当客户端读取数据的时候,首先去NN查询file与block与DN的映射,然后直接与DN建立连接,读取数据。
汇报:
启动时:
汇报之前会验证Block文件是否损坏。
向NN汇报当前的DN上block的信息。
运行中:
向NN保持心跳。
SecondaryNameNode:
传统的内存持久化方案:
日志机制:
任何操作之前都先记录日志,再数据改变之前先记录对应的日志,NN停止了,下次再启动时只需要重新按照以前的日志“重做”一遍即可,绝对不会丢失数据 。但是它的log文件大小会不可控,随着时间的发展,集群启动的时间会越来越长。而且日志中可能存在很多无效日志。
拍摄快照:
将内存中的数据序列化写到磁盘上,再启动时将磁盘中的数据反序列化写回内存,但是这样可能回导致关机时间长,如果异常关机,数据就丢失了,而且如果写出频率过高,会导致内存使用率低。
SNN的解决方案:
日志大小可控,快照定时保存,日志+快照。
当启动一个集群的时候,产生四个文件(edits...,fsimage...,seen_txid,VERSION),我们每次操作都会记录日志--->edits_inprogress-000000001,当这个日志文件大小或时间达到阈值(64M或3600秒),会申城新的日志文件,edits_inprogress-000000001--->edits_00000001,新创建edits_inprogress-000000002。
安全模式:
安全模式是指集群启动时的一个状态,NameNode启动时,首先将镜像文件fsimage载入内存,并执行编辑日志edits各项操作,一旦在内存中成功建立文件系统元数据映像,则创建一个新的镜像文件和一个空的编辑日志,此时NN开始监听DN的请求,NN运行在安全模式。
此时的NN的文件系统对于客户端来说是只读的,它是为了保护数据的安全。
安全模式下,DN向NN发送最新的块列表信息,NN收集到的Block信息达到最少副本数时就会脱离安全模式,因为NN了解到足够多的块位置信息后才可高效运行文件系统。如果NN收集到的Block信息没有达到最少副本数,就会将缺失的副本从已有的DN上拷贝到其他的DN。拷贝的过程中系统还是处于安全模式。
机架感知: 第一个节点:
集群内部:
优先考虑和客户端相同节点作为第一个节点。
集群外部:
选择资源丰富且不繁忙的节点作为第一个节点。
第二个节点:
选择和第一个节点不通机架的其他节点。
第三个节点:
与第二个节点相同机架的其他节点。
第N个节点:
与前面节点不重复的其他节点。
HDFS写流程(宏观):
客户端向HDFS发送写数据请求
hdfs通过rpc调用NN的create方法
NN首先检查是否有足够的空间权限等条件创建这个文件,如果有NN针对这个文件创建一个空的Entry对象,并返回成功状态给hdfs。如果没有直接抛出异常给客户端。
DFS如果收到成功的状态,会创建一个对象FSDataOutputStream的对象给客户端使用。
客户端需要向NN询问第一个Block存放的位置(NN通过机架感知策略得到的)。
需要将客户端和DN节点创建连接
pipeline(管道)
客户端将文件按照block切分数据,但是按照packet发送数据,默认一个packet大小未64k。
客户端通过pipeline管道开始使用FSDataOutputStream对象将数据输出
客户端首先将一个packet发送给node1,同时给予node1一个ack状态。
node1接收数据后会将数据继续床底个node2,同时给予node2一个ack状态。
node2将这个packet接收完成后响应这个ack给node1未true,同理node1响应给客户端。
客户端接收到成功的状态,就认为某个packet发送成功了,直到当前块所有的packet都发送完成。
客户端会将这个消息给NN,NN确认传输完成
NN会将block的信息记录到Entry。
HDFS写流程(微观):
首先,客户端从自己的硬盘以流的方式读取数据文件到自己的缓存中,然后将缓存中的数据以chunk(512B)和checksum(4B)的方式放入到packet(64K)中。
check:checksum = 128:1
checksum:在数据处理和数据通信领域中,用于校验目的一组数据项的和
packet分两类,1是实际数据包,另一类是header包,一个packet数据包的组成结构:
当packet满的时候添加到dataqueue,datastreamer开始从dataqueue队列上去除一个packet,荣国FSDataOPS发送得到Pipleline,是一种典型的生产者消费者模式,datastreamer也会将发送出去的packet添加到ackqueue上,客户端发送一个packet数据包后开始接收ack,会有一个用来接收ack的ResponseProcessor线程,如果收到成功的ack就从ackqueue中删除掉这个packet,如果是false,那么会将ackqueue中所有的packet重新挂载到发送队列重新发送。
最终DFS保存的数据格式:
HDFS读流程:
首先客户端发送请求到DFS,申请读取某一个文件,DFS去NN查找这个文件的信息(权限、文件是否存在等),DFS创建FSDataInputStream对象,客户端通过这个对象读取数据,客户端获取文件的第一个Block信息,返回DN1,DN2...,客户端直接就近选择某一个DN对应的数据。
依次读取其他块的信息,知道最后一个块,最后将Block合成一个文件。
HA:
Active NameNode(ANN):
功能与NN的功能是一样的。接收客户端请求,查询数据块DN信息,存储数据的元数据信息(数据文件:Block即DN的映射关系)。
工作:
启动时接收DN的Block汇报,运行时和DN保持心跳(3S,10M)。
存储介质:
完全基于内存,数据处理效率高,但是数据的持久化久。
Standby NameNode(SNN):
NN的备用节点,与主节点做同样的工作,但是它不会发出任何指令,存储数据的元数据信息(数据文件Block即DN的映射关系),它的内存数据和主节点内存数据几乎是一致的。
工作:
启动时接收DN的Block汇报,运行时和DN保持心跳(3S,10M)。
存储介质:
完全基于内存,数据处理效率高,但是数据的持久化久。
合并日志文件和镜像:
当搭建好集群的时候,格式化主备节点的时候,ANN和SNN都会默认创建镜像文件fsimage_00...,当我们操作HDFS的时候ANN会产生日志信息edits_inprogress_000...,主节点会将日志文件中新增的数据同步到ournalNode集群上,所以只需要SNN有操作的日志信息,就会合并fsimage与edits信息,SNN将合并好的Fsimage发送给ANN,ANN验证无误后存放到自己的目录中。
DataNode:
在硬盘上存储的是文件的Block信息,它启动时同时向两个NN汇报Block信息,运行中会同时和两个NN节点保持心跳机制。
Quorum JournalNode Manger(QJM):
共享存储系统,NameNode通过共享存储系统实现日志数据同步。JournalNode时一个独立的小集群,它的实现原理和ZK一致(Paxos)。
ANN产生日志文件的时候,同时发送到JournalNode的集群中每个节点上,JournalNode不要求所有的jn节点都接收日志,只要有半数以上的节点接收到日志,那么本条日志就生效。
SNN每个一段时间都会QJN上取回最新的日志。
Failover Controller(故障转移控制器):
对NN的主备切换进行总体控制,及时检测到NN的健康状况,在主NN故障时借助ZK实现自动的主备选举和切换。为了放置因为NN的GC失败导致心跳影响,ZKFC作为一个deamon进程从NN中分离出来。
启动时:
在集群启动时,主备节点的概念时模糊的,当ZKFC只检查到一个节点时将其设置为主节点,当ZKFC将查到两个NN节点时健康状态,发起投票机制,选出一个主,一个备,并修改主备节点的状态。
运行时:
由ZKFailoverController、HealthMonitor和ActiveStandbyElector这三个组件协同实现主备切换。
ZKFailoverController启动时创建HealthMonitor和ActiveStandbyElector这两个主要的内部组件。
HealthMonitor主要负责检测NN的健康状态。
当Active NN出现失败或者连接超时的情况,监控程序会将ZK上对应的临时Znode进行删除,znode的删除事件会主动出发到下一次的Active NameNode的选择。
ActiveStandbyElector主要负责完成自动的主备选举,内部封装了zk的处理逻辑。
主备节点正常切换:
NN在选举成功后,ActiveStandbyElector会在zk上创建要给ActiveStandbyElectorLock临时节点,而没有选举成功的备NN中的ActiveStandbyElector会监控这个节点。
如果Active NameNode对应的HealthMonitor检测到NN的状态异常,ZKFailoverController会主动删除当前在ZK上建立的临时节点ActiveStandbyElectorLock。
处于Standby状态的NN的ActiveStandbyElector注册的监听器就会收到这个节点的NodeDeleted事件,并创建ActiveStandbyElectorLock临时节点,本来处于Standby状态的NN会选举为主NN,并随后开始切换为Active状态。
如果Active状态的NN所在的整个机器宕掉的话,那个跟ZK连接的客户端线程也挂了,会话结束,那么根据zk的临时节点特性,ActiveStandbyElectorLock节点会被自动删除,从而也会自动进行一次主备切换。
zookeeper(管理型软件):
为主备切换控制器提供主备选举支持,辅助投票,和ZKFC保持心跳机制,确定ZKFC的存活。
脑裂:
脑裂时Hadoop2.X版本之后出现的全新问题,实际运行过程中很有可能出现两个NN同时服务于整个集群的情况,这种情况就是脑裂。
它出现的原因在于主从NN切换时,由于ActiveNameNode的网络延迟、设备故障等问题,另一个NN会认为活跃的NN称为失效状态,此时StandbyNameNode会转换为活跃状态,此时集群中将会出现两个活跃的NameNode,因此可能出现脑裂的的因素有网络延迟、心跳故障、设备故障。
脑裂的场景有:
NN可能出现这种情况,NN在垃圾回收时,可能会在长时间内整个系统无响应。
ZKFC客户端也就无法向ZK写入心跳信息吗,这样的话可能会导致临时节点掉线,备NN会切换到Active状态。
这种情况可能会导致整个集群会同时有两个NN。
脑裂的解决方案是隔离:
1、第三方存储,任一时刻,只有一个NN可以写入。
2、DataNode,需要保证只有一个NN发出与管理数据副本有关的删除命令。
3、Client需要保证同一时刻只有一个NN能够对Client的请求发出正确的响应。
①每个NN改变状态的时候,向DN发送自己的状态和一个序列号。
②DN在运行过程中维护此序列号,当failover时,新NN在返回DN心跳时会返回自己的active状态和一个更大的序列号,DN在接收到这个返回是认为该NN为新的active。
③如果这是原来的active(GC)恢复,返回给DN的心跳信息包含active状态和原来的序列号,这时DN就会拒接这个NN的命令。
解决方案:
ActiveStandbuElector为了实现fencing,当NN称为了ANN之后创建ZK临时节点ActiveStandbyElectorLock,创建ActiveBreadCrumb的持久节点,这个节点里保存了这个Active NameNode的地址信息。
ActiveNameNode的ActiveStandbuElector在正常的状态下关系ZK Session的时候会一起删除这个持久节点。
但如果ActiveStandbuElector在异常的状态下关闭,那么由于/hadoop-ha/$(dfs.nameservices)/ActiveBreadCrumb是持久化节点,会一直保留下来,后面当另一个NN选主成功后,会注意到上一个ANN遗留下来的节点,从儿回调ZKFailoverController的方法对就旧的Active NameNode进行fencing。
首先尝试调用这个旧的ANN的HAServiceProtocol PRC接口的transitionToStandby方法,看能否将它转成Standby状态。如果方法调用失败,执行Hadoop配置文件中预定义的隔离措施。
①sshfence:通过ssh登录到目标主机上,执行命令fuser将对应进程杀死。
②ssellfence:执行一个用户自定义的shell脚本来将对应进程进行隔离。
在成功地完成fencing之后,选主成功地ActiveStandbyElector才会回调ZKFailoverController地becomeActive方法,将对应地NN装换位Active状态,开始对外进行服务。
Hadoop-Federation:
使HDFS支持多个命名空间,并且允许在HDFS中同时存在多个NN。
块池Block Pool:
Block pool就是属于当个命名空间地一组block管理区域。
每个DataNode为所有的block pool存储。
DataNode是一个物理概念,而每个block pool是要给重新将block划分的逻辑概念。
一个NN失效不影响其下的DN为其他的NN服务。
DN与NN建立联系开始会话后自动建立Block pool。
命名空间卷NameSpace Volume:
一个Namespace和它的Block pool合在一起称作Namespace Volume。
Namespace Volume是一个独立完整的管理单元,当一个NN或NameSpace被删除,与之独赢的Block Pool也就被删除。
通过多个NN或NameSpace把元数据的存储和管理分散到多个节点中,降低单个节点数据压力和计算压力。
NameNode和NameSpace可以通过增加机器来进行水平扩展,这样可以让更多i的节点参与到运算,NameSpace命名空间,通过这种凡是确定要处理数据的路径。
NameNode和NameSpace组合使用:所有的NN共享DN,但是没有给Namespace会单独管理自己的块,会创建一个管理块的机制,即blocks pool。
额外补充点: HDFS读流程简述:
1、Client向NameNode发送RPC请求。请求文件block的位置;
2、NameNode收到请求之后会检查用户权限以及是否有这个文件,如果都符合,则会视情况返回部分或全部的block列表,对于每个block,NameNode都会返回含有该block副本的DataNode地址;这些返回的DataNode地址,会按照集群拓扑结构得出DataNode与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离 Client 近的排靠前;心跳机制中超时汇报的DataNode状态为STALE,这样的排靠后;
3、Client选取排序靠前的DataNode来读取block,如果客户端本身就是DataNode,那么将从本地直接获取数据(短路读取特性);
4、底层上本质是建立Socket Stream(FSDataInputStream),重复的调用父类DataInputStream的read方法,直到这个块上的数据读取完毕;
5、当读完列表的block后,若文件读取还没有结束,客户端会继续向NameNode 获取下一批的block列表;
6、读取完一个block都会进行checksum验证,如果读取DataNode时出现错误,客户端会通知NameNode,然后再从下一个拥有该block副本的DataNode 继续读;
7、read方法是并行的读取block信息,不是一块一块的读取;NameNode只是返回Client请求包含块的DataNode地址,并不是返回请求块的数据;
8、最终将读取来所有的block合并成一个完整的最终文件;
HDFS写流程简述:1、Client客户端发送上传请求,通过RPC与NameNode建立通信,NameNode检查该用户是否有上传权限,以及上传的文件是否在HDFS对应的目录下重名,如果这两者有任意一个不满足,则直接报错,如果两者都满足,则返回给客户端一个可以上传的信息;
2、Client根据文件的大小进行切分,默认128M一块,切分完成之后给NameNode发送请求第一个Block块上传到哪些服务器上;
3、NameNode收到请求之后,根据网络拓扑和机架感知以及副本机制进行文件分配,返回可用的DataNode的地址;Hadoop在设计时考虑到数据的安全与高效, 数据文件默认在HDFS上存放三份, 存储策略为本地一份,同机架内其它某一节点上一份, 不同机架的某一节点上一份。
4、客户端收到地址之后与服务器地址列表中的一个节点如A进行通信,本质上就是RPC调用,建立pipeline,A收到请求后会继续调用B,B在调用C,将整个pipeline建立完成,逐级返回Client;
5、Client开始向A上发送第一个block(先从磁盘读取数据然后放到本地内存缓存),以packet(数据包,64kb)为单位,A收到一个packet就会发送给B,然后B发送给C,A每传完一个packet就会放入一个应答队列等待应答;
6、数据被分割成一个个的packet数据包在pipeline上依次传输,在pipeline反向传输中,逐个发送ack(命令正确应答),最终由pipeline中第一个DataNode节点A将pipelineack发送给Client;
7、当一个block传输完成之后, Client再次请求NameNode上传第二个block,NameNode重新选择三台DataNode给Client。
HDFS在读取文件时,其中一个块突然损坏了后HDFS的做法:客户端读取完DataNode上的块之后会进行checksum验证,也就是把客户端读取到本地的块与HDFS上的原始块进行校验,如果发现校验结果不一致,客户端会通知NameNode,然后再从下一个拥有该block副本的DataNode继续读。
HDFS在上传文件时,其中一个DN突然挂掉后HDFS的处理方法:客户端上传文件时与DataNode建立pipeline管道,管道的正方向是客户端向DataNode发送的数据包,管道反向是DataNode向客户端发送ack确认,也就是正确接收到数据包之后发送一个已确认接收到的应答。
当DataNode突然挂掉了,客户端接收不到这个DataNode发送的ack确认,客户端会通知NameNode,NameNode检查该块的副本与规定的不符,NameNode会通知DataNode去复制副本,并将挂掉的DataNode作下线处理,不再让它参与文件上传与下载。
NameNode在启动的时候会做的操作:NameNode数据存储在内存和本地磁盘,本地磁盘数据存储在fsimage镜像文件和edits编辑日志文件。
首次启动NameNode:格式化文件系统,为了生成fsimage镜像文件;
启动NameNode:
读取fsimage文件,将文件内容加载进内存
等待DataNade注册与发送block report
启动DataNode:
向NameNode注册
发送block report
检查fsimage中记录的块的数量和block report中的块的总数是否相同
对文件系统进行操作(创建目录,上传文件,删除文件等):
此时内存中已经有文件系统改变的信息,但是磁盘中没有文件系统改变的信息,此时会将这些改变信息写入edits文件中,edits文件中存储的是文件系统元数据改变的信息。
第二次启动NameNode:读取fsimage和edits文件;
将fsimage和edits文件合并成新的fsimage文件;
创建新的edits文件,内容开始为空;
启动DataNode。
Secondary NameNode的工作机制:Secondary NameNode是合并NameNode的edit logs到fsimage文件中;
它的具体工作机制:
Secondary NameNode询问NameNode是否需要checkpoint。直接带回NameNode是否检查结果;
Secondary NameNode请求执行checkpoint;
NameNode滚动正在写的edits日志;
将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode;
Secondary NameNode加载编辑日志和镜像文件到内存,并合并;
生成新的镜像文件fsimage.chkpoint;
拷贝fsimage.chkpoint到NameNode;
NameNode将fsimage.chkpoint重新命名成fsimage;
所以如果NameNode中的元数据丢失,是可以从Secondary NameNode恢复一部分元数据信息的,但不是全部,因为NameNode正在写的edits日志还没有拷贝到Secondary NameNode,这部分恢复不了。
HDFS中小文件过多的危害以及避免方法:Hadoop上大量HDFS元数据信息存储在NameNode内存中,因此过多的小文件必定会压垮NameNode的内存。
每个元数据对象约占150byte,所以如果有1千万个小文件,每个文件占用一个block,则NameNode大约需要2G空间。如果存储1亿个文件,则NameNode需要20G空间。解决这个问题的方法就是合并小文件,可以选择在客户端上传时执行一定的策略先合并,或者是使用Hadoop的
CombineFileInputFormat
实现小文件的合并。