5. HDFS的工作机制(重点)

5.1 开机启动Namenode过程

namenode启动(根据客户端的请求记录fsimage和edits,在内存中进行增删改查)

  1. 第一次启动namenode格式化后,创建HDFS镜像文件fsimage和编辑日志文件edits。如果不是第一次启动,直接加载镜像文件到内存,再加载编辑日志。(HDFS的镜像文件FsImage包含着集群所有文件的元数据信息; 编辑日志edits类似“账本”记录数据操作)
    Namenode在启动时,会将Fsimage的内容加载到内存当中,然后执行EditLog文件中的各项操作,使得内存中的元数据保持最新。这个操作完成以后,就会生成一个新的Fsimage文件(最新Fsimage取代旧的)和一个空的EditLog文件
    名称节点启动成功并进入正常运行状态以后,HDFS中的更新操作都被写到EditLog,而不是直接写入 FsImage,这是因为对于分布式文件系统而言,FsImage文件通常都很庞大,如果所有的更新操作都直接往 FsImage文件中添加,那么系统就会变得非常缓慢。相对而言,EditLog通常都要远远小于FsImage,一个 EditLog文件最大64M,更新操作写入到EditLog是非常高效的。
    名称节点在启动的过程中处于“安全模式”,只能对外提供读操作,无法提供写操作。在启动结束后,系统就会 退出安全模式,进入正常运行状态,对外提供写操作。
  2. 客户端对元数据进行增删改的请求,namenode记录操作日志,当到达条件时更新滚动日志(“记账”)到 edits.002、edits.inprogress为接下来用的edits
  3. namenode在内存中对数据进行增删改查

小知识:

  1. 滚动编辑日志(前提必须启动集群)
  1. 可以强制滚动
    [bigdata@hadoop102 current]$ hdfs dfsadmin -rollEdits
  2. 可以等到edits.inprogress满64m生成edits文件
  3. 可以等操作数量达到100万次
  4. 时间到了,默认1小时
    注意:在2,3,4时发生滚动,会进行checkpoint
  1. 镜像文件什么时候产生
    可以在namenode启动时加载镜像文件和编辑日志
    也可以在secondarynamenode生成的fsimage.chkpoint文件重新替换namenode原来的fsimage文件时
5.2 安全模式介绍

Namenode启动时,首先要加载fsimage文件到内存,并逐条执行editlog文件里的事务操作,在这个期间一旦在内存中成功建立文件系统元数据的映像,就会新创建一个fsimage文件(该操作不需要 SecondaryNamenode)和一个空的editlog文件。在这个过程中,namenode是运行在安全模式下的, Namenode的文件系统对于客户端来说是只读的,文件修改操作如写,删除,重命名,修改权限等均会失败。

系统中的数据块的位置并不是由namenode维护的,而是以块列表的形式存储在datanode中。在系统的正常操作期间,namenode会在内存中保留所有块位置的映射信息。在安全模式下,各个datanode会向namenode发送最新的块列表信息,如果满足“最小副本条件”,namenode会在30秒钟之后就退出安全模式,开始高效运行文件系统.所谓的最小副本条件指的是在整个文件系统中95.9%的块满足最小副本级别(默认值:dfs.replication.min=1)。

PS:启动一个刚刚格式化完的集群时,HDFS还没有任何操作呢,因此Namenode不会进入安全模式。

5.2.1 查看namenode是否处于安全模式:
[root@hostname01 current]# hdfs dfsadmin -safemode get 
Safe mode is ON
5.2.2 管理员可以随时让Namenode进入或离开安全模式,这项功能在维护和升级集群时非常关键
[root@hostname01 current]# hdfs dfsadmin -safemode enter 
Safe mode is ON 
[root@hostname01 current]# hdfs dfsadmin -safemode leave 
Safe mode is OFF
5.2.3 将下面的属性的值设置为大于1,将永远不会离开安全模式
<property> 
    <name>dfs.namenode.safemode.threshold-pct</name> 
    <value>0.999f</value> 
</property>
5.2.4 有时,在安全模式下,让安全模式进入等待状态,此时当前连接失效
[root@hostname01 current]# hdfs dfsadmin -safemode wait
5.3 DataNode与NameNode通信(心跳机制)
  1. hdfs是hostname01/slave结构,hostname01包括namenode和resourcemanager,slave包括 datanode和nodemanager
  2. hostname01启动时会开启一个IPC服务,等待slave连接
  3. **slave启动后,会主动连接IPC服务,并且每隔3秒链接一次,这个时间是可以调整的,设置 heartbeat,这个每隔一段时间连接一次的机制,称为心跳机制。**Slave通过心跳给hostname01汇报自己信息,hostname01通过心跳下达命令。
  4. Namenode通过心跳得知datanode状态。Resourcemanager通过心跳得知nodemanager状态
  5. 当hostname01长时间没有收到slave信息时,就认为slave挂掉了。

注意:超长时间计算结果:默认为10分钟30秒

属性:dfs.namenode.heartbeat.recheck-interval 的默认值为300000,单位为毫秒(五分钟)

属性:dfs.heartbeat.interval 的默认值时3,单位为秒

计算公式:2 * recheck+10 * heartbeat

5.4 SecondayNamenode的工作机制(检查点机制)

SecondaryNamenode,是HDFS集群中的重要组成部分,它可以辅助Namenode进行fsimage和editlog的合并工作,减小fsimage文件大小,以便缩短下次Namenode的重启时间,能尽快退出安全模式。

两个文件的合并周期,称之为检查点机制(checkpoint),是可以通过hdfs-default.xml配置文件进行修改的:

<property> 
    <name>dfs.namenode.checkpoint.period</name> 
    <value>3600</value> 
    <description>两次检查点间隔的秒数,默认是1个小时</description> 
</property> 
<property> 
    <name>dfs.namenode.checkpoint.txns</name> 
    <value>1000000</value> 
    <description>txid执行的次数达到100w次,也执行checkpoint</description> 
</property> 
<property> 
    <name>dfs.namenode.checkpoint.check.period</name> 
    <value>60</value> 
    <description>60秒一检查txid的执行次数</description> 
</property>

通过上图,可以总结如下:

  1. SecondaryNamenode请求Namenode停止使用正在编辑的editlog文件,Namenode会创建新的 editlog文件(小了吧),同时更新seed_txid文件。
  2. SecondaryNamenode通过HTTP协议获取Namenode上的fsimage和editlog文件。
  3. SecondaryNamenode将fsimage读进内存当中,并逐步分析editlog文件里的数据,进行合并操作, 然后写入新文件fsimage_x.ckpt文件中。
  4. SecondaryNamenode将新文件fsimage_x.ckpt通过HTTP协议发送回Namenode。
  5. Namenode再进行更名操作。
  6. 触发条件:
  1. txid每增加一百万次
  2. 离上次检查的已过一小时
5.5 网络拓扑

在进行副本冗余策略时,我们是否应该考虑网络带宽问题呢?答案是肯定的,网络带宽是很稀缺的资源。(希望5G时代能解决这个问题,嘎嘎)。因此在副本冗余时,这些副本存储在哪个工作节点上,存储在哪个机架上?HDFS又怎么知道哪个机架远,哪个机架近呢。

衡量不同节点之间的带宽,实际上很难实现,HDFS采用了一个较为简单的方式:把网络看成一颗树,两个节点之间的距离是他们到最近共同祖先的距离总和。

距离有这四种情况:

Distance(/d1/r1/n1, /d1/r1/n1)=0(同一节点上的进程) 
Distance(/d1/r1/n1, /d1/r1/n2)=2(同一机架上的不同节点) 
Distance(/d1/r1/n1, /d1/r2/n3)=4(同一数据中心不同机架上的节点) 
Distance(/d1/r1/n1, /d2/r3/n4)=6(不同数据中心的节点) 
带宽依次递减。
5.6 机架感知

如何想让HDFS知道自己的网络拓扑情况,那就是另外一个配置策略了,即机架感知。默认情况下,机架感知策略是关闭的。 需要进行对net.topology.script.fifile.name进行设置。如果不设置,namenode就会将datanode注册属于/default-rack机架。

使用了机架感知策略的副本存放:

Hadoop2.4.2版本以后的策略:
第一个副本在client所处的节点上。如果客户端在集群外,随机选一个。 
第二个副本与第一个副本不相同机架,随机一个节点进行存储 
第三个副本与第二个副本相同机架,不同节点。

补充:不存在节点上存在多个副本

5.7 节点动态上线
5.7.1 属性说明
在hdfs-site.xml里有一个属性,用于指定所有在线主机名和要上线的主机名的文件 
<property> 
    <name>dfs.hosts</name> 
    <value></value> 
    <description>命名包含允许连接到namenode的主机列表的文件。必须指定文件的完整路径名。如果值为空,则允许所有主机</description> 
</property>
5.7.2 动态上线步骤:
准备工作:准备一台配置了jdk和hadoop的新主机slave3。如果克隆的话,别忘记删除 ${hadoop.tmp.dir}/dfs/目录和logs目录

第一步:修改dfs.hosts属性,指定文件include的全路径(首次修改需要自己添加)

<property> 
    <name>dfs.hosts</name> 
    <value>/usr/local/hadoop/etc/hadoop/include</value> 
</property> 
注意:include这个文件名,可以自定义。

第二步:将待上线的主机名(slave3)添加到dfs.hosts指定的文件内

[root@hostname01 hadoop]# touch include #创建文件 
[root@hostname01 hadoop]# vi include 
hostname01 
hostname02 
hostname03 
slave3 #将要上线的主机名 
注意:dfs.hosts指定的文件中必须存储所有的在线主机名

第三步:在namenode上刷新节点:

[root@hostname01 hadoop]# hadoop dfsadmin -refreshNodes

第四步:在要上线的节点上重启 datanode

[root@hostname01 hadoop]# hadoop-daemon.sh start datanode

第四步:通过hadoop dfsadmin -report 或者web界面,可以看到, 节点已经上线。

第五步:最后修改slaves,添加上新主机名。以便集群重启时,带上此节点。

[root@hostname01 hadoop]# vi slaves 
hostname01 
hostname02 
hostname03 
slave3 #添加此节点

动态添加节点脚本

addnode.sh

#!/bin/sh
#获取节点
nodename=$1
#如果在黑名单,则从黑名单除名
sed -i '/'${nodename}'/d' /usr/local/hadoop/etc/hadoop/exclude
#添加至白名单
echo -e "$nodename" >> /usr/local/hadoop/etc/hadoop/include
#刷新
hadoop dfsadmin -refreshNodes
#切换主机
ssh $nodename > /dev/null 2>&1 << eeooff
#开始节点
hadoop-daemon.sh start datanode
#切换回来
exit
eeooff
#刷新奴隶名单
sed -i '/'${nodename}'/d' /usr/local/hadoop/etc/hadoop/slaves
echo -e "$nodename" >> /usr/local/hadoop/etc/hadoop/slaves
5.8 节点动态下线
5.8.1 属性说明
在hdfs-site.xml里有一个属性,用于指定存储将要退役的主机名的文件 
<property> 
    <name>dfs.hosts.exclude</name> 
    <value></value> 
    <description>命名包含不允许连接到namenode的主机列表的文件。必须指定文件的完整路径名。如果该值为空,则不排除任何主机</description> 
</property>
5.8.2 下线步骤

第一步:修改dfs.hosts.exclude属性,指定文件exclude的全路径

<property> 
    <name>dfs.hosts.exclude</name> 
    <value>/usr/local/hadoop/etc/hadoop/exclude</value> 
</property> 
注意:dexclude这个文件名,可以自定义。

第二步:将待下线的主机名(hostname01)加入到dfs.hosts.exclude指定的文件中

[root@hostname01 hadoop]# touch exclude #创建exclude文件 
[root@hostname01 hadoop]# vi exclude 
hostname01

第三步:在namenode节点上刷新节点

[root@hostname01 hadoop]# hadoop dfsadmin -refreshNodes

第四步:通过hadoop dfsadmin -report或者web界面,进行查看

此时,该datanode状态转化为Decommission In Progress。表示该datanode正在退役中,进行数据块 
的转移。 
因此会花上一段时间。需要等待...... 
当decommission进程完成数据移动, datanode状态会转变为Decommissioned, 然后datanode会自 
动停止datanode进程。 然后你可以看见dead nodes下多了一个你想要下线的节点。

第五步:切换至下线主机停止节点运行

]# hadoop-daemon.sh stop datanode

以下两步将会杀死该节点

第六步:退回namenode主机,删除include 和 exclude 中该节点的hosts, 重新刷新hadoop dfsadmin -refreshNodes。

第七步:删除slaves中该节点的配置, 防止下次集群重启时,namenode傻乎乎的启动这个退役的节点。

动态下线脚本

waitnode.sh

#!/bin/sh
nodename=$1
#加入黑名单
echo -e "$nodename" >> /usr/local/hadoop/etc/hadoop/exclude
hadoop dfsadmin -refreshNodes
ssh $nodename > /dev/null 2>&1 << eeooff
#切换主机并停止该节点工作
hadoop-daemon.sh stop datanode
exit
eeooff
#再次刷新
hadoop dfsadmin -refreshNodes

动态删除节点脚本

delnode.sh

#!/bin/sh
nodename=$1
#进入黑名单
echo -e "$nodename" >> /usr/local/hadoop/etc/hadoop/exclude
ssh $nodename > /dev/null 2>&1 << eeooff
hadoop dfsadmin -refreshNodes
exit
eeooff
#从白名单除名
sed -i '/'${nodename}'/d' /usr/local/hadoop/etc/hadoop/include
#从黑名单除名
sed -i '/'${nodename}'/d' /usr/local/hadoop/etc/hadoop/exclude
#刷新节点
hadoop dfsadmin -refreshNodes
#从奴隶名单除名
sed -i '/'${nodename}'/d' /usr/local/hadoop/etc/hadoop/slaves

注意:** 当你下线一个datanode节点, 有可能该节点长时间处于Decommission In Progress 状态, 一直不能转变成Decommissioned。 请你用hadoop fsck / 检查下是否有些块少于指定的块数, 特别注意那些mapreduce的临时文件。 将这些删除, 并且从垃圾箱移除, 该节点就可以顺利下线, 这是我想到的解决办法。