1. 概述
HDFS集群分为两大角色:NameNode、DataNode(Secondary NameNode)
NameNode负责管理整个文件系统的元数据,记录存放在哪些datanode中,以及存放路径
dataNode 负责管理用户的文件数据块
文件会按照固定大小(blocksize)来切分成块后分布式存储在若干台datanode上
每一个文件快可以有多个副本,并存放在不同的datanode上
datanode 会定期向namenode汇报自身所保存的文件block信息,而namenode则会负责保持文件的副本数量(当减少datanode的时候,namenode才知道当前副本状态,从而进行副本维持)
HDFS的内部工作机制对客户端保持透明,客户端请求访问HDFS都是通过向namenode申请来进行
2. 写数据流程
比较简单,看图即懂
3. 读数据流程
比较简单,看图即懂
4. nameNode、secondNameNode管理元数据机制
4.1 机制
每次更新元数据,namenode都需要记录下来这些更新信息,方便之后查询更新过的元数据。
假如每次记录在磁盘上,几千万上亿条元数据写入速度会非常慢,导致磁盘IO使用率过高,效率低;读数据的时候再上亿条数据里面搜索,查询的效率也非常低。
因此需要将更新记录放在内存中(new一个元数据对象),如果仅仅使用内存,亿级的数据又会使nameNode死机或者意外断电,那么内存中的记录全部丢失。
所以需要把内存中的亿级元数据定时写入fsimage中。由于一条元数据平均大小为150Bytes,定时时间太长的话中途断电就丢失了,定时时间太短又会导致磁盘IO占用过高的问题。
这样,secondNameNode就引入了,用于fsimage镜像同步管理。
1. 用户更新元数据时,将更新记录写入namenode内存
2. 在第1步的同时,在namenode中,只将内存中更新的记录追加到日志文件(edits文件)中,这样,即便是断电了,nameNode也能根据edits操作日志就能恢复元数据的记录信息。
但是这样又会引入一个问题,edits文件变得越来越大,每次重启的时候加载edits日志又会严重耗时,影响nameNode启动速度。
这样我们就需要一个元数据对象(在内存中)的一个镜像文件fsimage,那么edits文件就不用记录每条元数据的完整信息,只用记录对单条元数据的操作记录,且定时将edits和fsimage镜像文件合并。这样断电重启时,只需要将fsimage与edits中未同步的少量操作记录计算合并后的元数据完整信息加载到内存中。
但是定时合并edits和fsimage也是一个很耗费资源的操作,且合并的同时,查询操作就无法保证效率和正确性,从而引入了secondNameNode,将耗费资源的操作放到另一台节点中。
3. 为了在定时合并edits和fsimage镜像时区分合并了和未合并的操作记录,这里需要拆分edits文件,未合并过的新操作记录用edits_inprogress来区分,默认1分钟拆分一次。
合并在hadoop namenode中专业术语是checkpoint,由secondNameNode发起询问namenode是否需要checkpoint,如果达到限值namenode回给secondNameNode可以checkpoint,secondNameNode才发起checkpoint请求。
其实,定时触发合并edits_inprogress记录和fsimage镜像的说法并不严格,实际上checkpiont触发条件有2个:a。secondNameNode定时触发询问namenode是否需要出发checkpoint(默认是1小时一次);b。edits的size达到默认值:64MB,namenode才同意触发checkpoint。
当secondNameNode请求触发checkpoint时,namenode需要将edits_inprogress_idx改名为edits_idx,且新建edits_inprogress_idx+1用来记录新增的操作。这样secondNameNode可以没有顾虑的做edits_idx和fsimage的checkpoint
4. 第一次做checkpoint时:secondNameNode将namenode的edits_idx和fsimage下载过来;之后再做checkpoint:secondNameNode只需要下载edits_idx+1,因为上一次的fsimage.checkpoint就是当先namenode的fsimage
5. 将下载过来的edits_idx和fsImage(fsimage.checkpoint)加载到内存中进行运算
6. 将运算的结果dump到文件(覆盖)fsimage.checkpoint
7. secondNameNode将fsimage.checkpoint上传到namenode中
8. namenode 将fsimage.checkpoint替换旧的fsimage
默认edits文件1分钟拆分一次,checkpoint询问30分钟一次
如果没有新的edits记录,则不会做checkpoint
当达到亿级数据时,一定要考虑内存的大小,以阿里云目前服务器最大支持512G来看,每条元数据平均150bytes,能存储36亿条元数据。每个元数据是一个文件,那么可以自行估算企业的实际情况。另外,元数据所指向的上传的文件肯定是越大越好,且不要频繁改动,适合存储大文件(小文件要合并存放,例如每个小时的文件合并成一天的文件来存放到HDFS)?
namenode如果死机,secondNameNode不具备能够替代的功能,因为secondNameNode只是用于做checkpoint的,所以一旦namenode死机,hadoop集群就不能运作了。
4.2 集群功能优化配置参数
4.2.1 dfs.namenode.name.dir
如果namenode的硬盘损坏,元数据是可以恢复绝大部分的(没有做checkpoint的edits_inprogress数据不能恢复),我们可以做个实验来验证:
1. #hadoop fs -ls /testdir
2. 立即删除namenode的工作目录 #rm -rf /kluter/hdpTmpDir/dfs/name, 这是,hadoop fs -ls /是可以正常工作的, 因为元数据信息还在namenode进程中
3. kill namenode进程,重启namenode失败,因为启动时报错:namenode工作目录不存在了
4. 将secondNameNode的工作目录cp到namenode的工作目录,重启namenode成功,但是发现第1步创建的testdir没有恢复成功
5. 为了解决第4步的问题,我们在开始搭建的时候就必须规划好硬件资源,在namenode上,需要挂载至少2块磁盘:即便一块磁盘挂了,另一块磁盘中还是有相同完整数据的。需要修改hdfs-site.xml配置:
<property>
<name>dfs.namenode.name.dir</name>
<value>/kluter/hdpTmpDir/dfs/name,/kluter/hdpTmpDir/dfs/nameBak</value>
</property>
4.2.2 dfs.datanode.data.dir
同理datanode可以通过hdfs-site.xml中的dfs.datanode.data.dir来配置多个工作目录到相同主机的不同硬盘,但并不是block在相同主机不同磁盘的复制(因为复制是在不同主机namenode之间)。
这个配置有2个作用:
1. 可以应对多个hadoop客户端同时传文件到HDFS的情况,不同的客户端将文件放在不同的磁盘,使用不同磁盘的IO并发进行,以减轻一块磁盘IO的压力。
2. 可以达到不增加namenode的情况下进行HDFS空间扩容,节约主机成本,只有磁盘费用的增加
4.2.3 dfs.namenode.secondary.http-address
这个参数是设置secondaryNameNode的节点IP和端口,默认是和namenode在同一台主机
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>10.10.77.193:50090</value>
</property>
4.2.4 dfs.namenode.checkpoint.period
checkpoint超时时间,默认值3600s=1小时,做实验可以改成几分钟试试效果
4.2.5 dfs.namenode.checkpoint.dir
secondaryNameNode的工作目录,当snn和nn在同一台时有效,如果不在同一台主机,nn会根据这个参数建立一个空的namesecondary目录
4.3 其他
4.3.1 VERSION 文件
路径:/kluter/hdpTmpDir/dfs/name/current/, namenode、datanode路径相同
# cat VERSION
#Fri Jun 22 14:10:26 CST 2018
namespaceID=126653939
clusterID=CID-530c66ce-ec4f-4bea-895f-6bdda27c48fa
cTime=1529647826935
storageType=NAME_NODE
blockpoolID=BP-590588951-10.10.77.194-1529647826935
layoutVersion=-63
1. namespaceID:文件系统的唯一标识符,在文件系统首次格式化之后生成的;有多个集群的情况下,不同集群拥有不同的
2. storageType:说明这个文件系统存储的是什么进程的数据结构信息
3. cTime表示nn存储时间的创建时间,如果nn更新升级,cTime将会记录更新时间戳
4. layoutVersion表示HDFS永久性数据结构的版本信息,只要数据结构变更版本号也改变,此时HDFS也需要升级,否则磁盘仍旧是旧的版本数据结构,会导致新版本的nn无法使用
5. clusterID是系统生成或手动指定的集群ID。当有多个集群时,需要格式化一个指定集群就需要填写对应的cluster_id
4.3.2 namenode的seen_txid文件
路径:/kluter/hdpTmpDir/dfs/name/current,只有namenode才有
作用是重启namenode时,值会增加,用于namenode对edits_inprogress_idx进行回滚,每次重启namenode时,nn就知道除了fsimage之外还需要将哪些edits文件进行加载该值等于inprogress的index,加载完fsimage和edits_inprogress文件后滚动新的edits_inprogress_idxNew
5. DataNode工作机制
1.
存储管理用户的文件块数据(block datas)
2.
定期向namenode汇报自身所持有的block信息(用于集群中某些block异常时,namenode知道如何去恢复初始副本数量),默认值21600000ms = 6hour,建议改成1小时=3600000
<property>
<name>dfs.blockreport.intervalMsec</name>
<value>3600000</value>
</property>
3. dataNode掉线timeout参数
如果dataNode因为各种原因死掉造成无法与namenode通信,namenode不会立即把该dn节点判定为掉线,要经过一段时限,HDFS默认timeout为10分钟+30秒,超时公式:timeout=2*heartbeat.recheck.interval + 10*dfs.heartbeat.interval
heartbeat.recheck.interval默认为300000ms=5分钟,heartbeat.interval默认为3s
假如手动kill一个datanode,也许你会觉得10分钟+30s后namenode才认定datanode挂掉是一个很长的时间(实验:kill zookeeper2这台datanode,在网页上发现datanode2的状态依然是active),但实际上有可能datanode只是需要重启一下,这样不用频繁的在网络内部发送消息,也不需要namenode立即去别的节点上同步数据来保证数据的copy数量