基础架构
HDFS Client
文件切分:文件上传 HDFS的时候,Client 将文件切分成一个个Block(数据块),然后进行存储。
与 NameNode 交互:获取文件真实的位置信息。
与 DataNode 交互:读取或写入数据。
Client 提供一些命令来管理 和访问HDFS,比如启动或者关闭HDFS。
NameNode和DataNode
管理 HDFS 的名称空间;
管理数据块(Block)映射信息;
配置副本策略;
处理客户端读写请求;
周期性的接收心跳和块的状态信息报告;
存储实际的数据块;
执行数据块的读/写操作;
周期性向NameNode汇报心跳信息;
周期性向NameNode汇报数据块信息;
周期性向NameNode汇报缓存数据块信息;
fsimage和edits
存放了客户端最近一段时间的操作日志;
客户端对 HDFS 进行写文件时的操作会先被记录在 edits 文件中;
edits 修改时元数据也会更新;
NameNode 中关于元数据的镜像,一般称为检查点,fsimage 存放了一份比较完整的元数据信息;
因为 fsimage 是 NameNode 的完整的镜像, 如果每次都加载到内存生成树状拓扑结构,这是非常耗内存和CPU, 所以一般开始时对NameNode 的操作都放在 edits 中;
fsimage 内包含了 NameNode 管理下的所有 DataNode 文件和文件 block 以及 block所在的 DataNode 的元数据信息;
随着 edits 内容增大,就需要在一定策略下和 fsimage 合并;
SecondaryNameNode
定期合并 fsimage和edits,并推送给NameNode,把 edits 控制在一个范围内。
在紧急情况下,可辅助恢复 NameNode。
<!-- 多久记录一次 HDFS 镜像, 默认 1小时 --><property><name>fs.checkpoint.period</name><value>3600</value></property><!-- 一次记录多大, 默认 64M --><property><name>fs.checkpoint.size</name><value>67108864</value></property>
SNN(SecondaryNameNode)通知NN(NameNode)暂时切换将日志写到edits.new内;
SNN通过GET请求将NN中的edits和fsimage文件加载到内存中;
SNN将内存中的edits和fsimage合并生成新的fsimage.ckpt文件;
SNN通过POST请求将生成的fsimage.ckpt文件传给NN;
NN将收到的fsimage.ckpt替换旧的fsimage文件;
NN将edits.new替换旧的edits文件;
高可用架构
JournalNode
ZKFailoverController:是基于Zookeeper的故障转移控制器,它负责控制NameNode的主备切换,ZKFailoverController会监测NameNode的健康状态,当发现Active NameNode出现异常时会通过Zookeeper进行一次新的选举,完成Active和Standby状态的切换;
HealthMonitor:周期性调用NameNode的HAServiceProtocol RPC接口(monitorHealth 和 getServiceStatus),监控NameNode的健康状态并向ZKFailoverController反馈;
ActiveStandbyElector:接收ZKFC的选举请求,通过Zookeeper自动完成主备选举,选举完成后回调ZKFailoverController的主备切换方法对NameNode进行Active和Standby状态的切换;
DataNode:NameNode包含了HDFS的元数据信息和数据块信息(blockmap),为了确保快速切换,Standby 状态的NameNode有必要知道集群中所有数据块的位置。为了做到这点,所有的DataNode必须配置两个NameNode的地址,同时发送数据块位置信息和心跳给他们两个。
共享存储系统(JournalNode):共享存储系统负责存储HDFS的元数据(EditsLog),Active NameNode(写入)和 Standby NameNode(读取)通过共享存储系统实现元数据同步,在主备切换过程中,新的Active NameNode必须确保元数据同步完成才能对外提供服务;对于HA集群而言,确保同一时刻只有一个NameNode处于active状态是至关重要的。否则,两个NameNode的数据状态就会产生分歧,可能丢失数据,或者产生错误的结果。为了保证这点,JNs必须确保同一时刻只有一个NameNode可以向自己写数据。
联邦机制
副本机制
一个文件有可能大于集群中任意一个磁盘,引入块机制,可以很好的解决这个问题;
使用块作为文件存储的逻辑单位可以简化存储子系统;
块非常适合用于数据备份,提供数据容错能力;
<!-- 块大小,单位为字节 --><property><name>dfs.block.size</name><value>134217728</value></property><!-- 副本数量,默认3个副本 --><property><name>dfs.replication</name><value>3</value></property>
当文件大于配置的块大小时会被拆分,如130M的文件会被拆分为两个块,一个128M另一个2M。
当文件小于配置的块大小时不会拆分,如100M的文件不会拆分,只有一个100M的块。
机架感知
第一个block副本放在客户端所在的数据节点里(如果客户端不在集群范围内,则从整个集群中随机选择一个合适的数据节点来存放);
第二个副本放置在与第一个副本所在节点不同的机架内的数据节点上(随机选择);
第三个副本放置在不同机架的节点上;
如果还有其他副本,则随机放在其他节点上;
当本地数据损坏时,节点可以从同一机架内的相邻节点拿到数据,速度肯定比从跨机架节点上拿数据要快;
当整个机架的网络出现异常,也能保证在其它机架的节点上找到数据;
distance(/D1/R1/H1,/D1/R1/H1)=0 相同的datanodedistance(/D1/R1/H1,/D1/R1/H2)=2 同一rack下的不同datanodedistance(/D1/R1/H1,/D1/R1/H4)=4 同一IDC下的不同datanodedistance(/D1/R1/H1,/D2/R3/H7)=6 不同IDC下的datanode
安全模式
当最小副本条件满足时,即:一定比例的数据块都到达最小副本数,系统会在30s后退出安全模式。
当最小的副本条件未达到要求时,就会对副本数不足的数据块安排DataNode进行复制,直到达到最小的副本数。
hdfs dfsadmin -safemode get #查看安全模式状态hdfs dfsadmin -safemode enter #进入安全模式hdfs dfsadmin -safemode leave #离开安全模式
<!-- 指定退出条件,需要达到最小副本数的数据块比例,默认是 0.999 --><property><name>dfs.namenode.safemode.threshold-pct</name><value>0.999f</value></property><!-- 指定系统退出安全模式时需要的延迟时间,单位为毫秒,默认为 30s --><property><name>dfs.namenode.safemode.extension</name><value>30000</value></property>
平衡策略
#开始数据平衡:#用默认的10%的阈值启动balancerstart-balancer.sh#或指定3%的阈值启动balancerhdfs dfs balancer -threshold 3start-balancer.sh -threshold 3#停止数据平衡:stop-balancer.shhdfs balancer[-threshold <threshold>][-policy <policy>][-exclude [-f <hosts-file> | <comma-separated list of hosts>]][-include [-f <hosts-file> | <comma-separated list of hosts>]][-idleiterations <idleiterations>]-threshold 10 #集群平衡的条件,datanode间磁盘使用率相差阈值,区间选择:0~100。Threshold参数为集群是否处于均衡状态设置了一个目标-policy datanode #默认为datanode,datanode级别的平衡策略-exclude -f /tmp/ip1.txt #默认为空,指定该部分ip不参与balance, -f:指定输入为文件-include -f /tmp/ip2.txt #默认为空,只允许该部分ip参与balance,-f:指定输入为文件-idleiterations 5 #最大迭代次数,默认为 5
#并行移动的block数量,默认5dfs.datanode.balance.max.concurrent.moves#Balance工具所占用的带宽,默认1048576(1MB)dfs.datanode.balance.bandwidthPerSec#用于执行block移动的线程池大小,默认1000dfs.balancer.moverThreads#每次balance进行迭代的过程最大移动数据量,默认10737418240(10GB)dfs.balancer.max-size-to-move#获取block的数量,默认2147483648(2GB)dfs.balancer.getBlocks.size#用来平衡的最小block大小,默认10485760(10MB)dfs.balancer.getBlocks.minblock-size
过度使用--> 过度闲置
过度使用-->平均值下
平均值上-->过度闲置
源DataNode的存储类型和目的DataNode的存储类型一致;
该block的副本未被安排;
目的DataNode不包含同样的副本;
移动之后该机架上的block不会减少;
与NameNode交互,获取DataNode磁盘使用情况;
根据数据分布情况对DataNode进行角色划分并配对;
根据移动策略从源DataNode移动block到目标DataNode,并删除源DataNode上的block;
获取移动结果,并继续移动其他数据块,直到没有数据可以移动或者HDFS集群以及达到了平衡的标准为止,然后向NameNode提交更新后的DataNode信息;
集群已达到均衡状态;
没有block能被移动;
连续5次(参数:idleiterations) 迭代移动没有任何一个block被移动;
当与NameNode交互时出现了IOException;
另一个Balancer进程在运行中。
读写原理
读原理
客户端向NN提交读请求;
NN进行权限检查、获取文件块列表:{blk_a_1: dn1,dn2,dn4 ;blk_a_2: dn2,dn3,dn4}
NN根据机架感知将距离客户端最近的文件块所在的DN列表返回给客户端:{blk_a_1:dn1, blk_a_2:dn2}
客户端和每个block所在的DN建立管道;
客户端读取数据,以数据包packet(64k)进行传输;
客户端将接收到的block合并为一个完整的文件;
写原理
客户端向NameNode提交写请求;
NN进行权限检查、判断是否满足写条件;
NN返回信息:可以上传;并将写操作记录在edits日志内。
客户端根据HDFS的块策略将文件切分为n个block文件块;
请求上传第一个文件块blk_a_1;
根据DN上的block信息和机架感知,选出可以上传的DN列表:(dn1,dn2,dn4);
NN返回可上传的DN列表(dn1,dn2,dn4);
客户端和DN建立数据传输管道,上传的DN之间也建立管道;
客户端向DN以数据包packet(64k)传递数据,dn1收到一个packet会传给dn2,dn2收到会传给dn4,每传一个packet会放入一个应答队列等待应答;
DN将收到的packet数据包进行缓存;
在管道的反方向上, DN逐个发送 ack(命令正确应答), 最终由管道中第一个DN节点dn1将ack发送给客户端;
当文件块block的packet传输完成则将缓存的临时文件放置在指定的HDFS上传路径;
继续上传其余的block文件块;
所有block上传完毕后返回完成信号给NN;
删除恢复机制
#时间单位是秒<property><name>fs.trash.interval</name><value>1440</value></property>














