Hadoop三大组件:HDFS/MR/Yarn,前面已经详述了计算模型MR的全过程,都说Hadoop的思想是移动计算而不移动数据,这一切基于hadoop的分布式文件系统HDFS。这两节详述hdfs的的工作过程/原理和注意事项。
首先看下HDFS的构成如下图
Client:客户端。
NameNode:master,它是一个主管、管理者,存储元数据。
DataNode:slave,NameNode 下达命令,DataNode 执行操作并存储实际数据。DataNode会定期心跳汇报节点状态和Block信息。
SecondaryNameNode:NameNode配合管理元数据。
写入时,需要先登记登记元信息,再具体写入,如下
客户端create一个新的文件;NameNode会做各种校验,比如文件是否已经存在,客户端是否有权限等。
如果校验通过,客户端开始写数据到DN(DataNode),文件会按照block大小进行切块,默认128M(可配置)。具体配置参数为hdfs-site.xml中如下参数:
<property>
<name>dfs.blocksizename>
<value>128mvalue>
property>
每个DataNode写完一个块后,返回确认信息。
写完数据,关闭文件
读取时,需要先从NameNode读取对应文件的元信息,对应具体的Block分布,然后客户端直接从DataNode读取对应数据,如下
客户端调用open方法,打开一个文件。访问NN,获取block的location,即block所在的DN,NN(NameNode)会根据拓扑结构返回距离客户端最近的DN。
客户端直接访问DN读取block数据并计算校验和,整个数据流不经过NN。
所有block读取完成,关闭文件
可以看到,元数据节点是非常重要的部分,那么具体元数据是如何保存和管理的呢。考虑如下两点:
具体过程示意如下:
当元数据较大时,合并会耗时,而且为了保证元数据节点本身的性能,需要一个备份节点来完成合并工作,这就是常说的SecondaryNamenode的工作,示意如下:
Secondary namenode工作流程:
具体的元数据存储目录一般在dfs.namenode.name.dir中,如下
具体来说,可以分别使用如下命令导出对应fsimage和edieslog数据
dfs oiv -p XML -i fsimage_0000000000000000660 -o fsimage.xml
hdfs oev -i edits_inprogress_0000000000000000666 -o edit.xml
fsimage结构如下,保存的时对应的结构和文件块信息块信息
<inode>
<id>16388id>
<type>DIRECTORYtype>
<name>outputname>
<mtime>1557901778514mtime>
<permission>wenzhou:supergroup:rwxr-xr-xpermission>
<nsquota>-1nsquota>
<dsquota>-1dsquota>
inode>
<inode>
<id>16389id>
<type>DIRECTORYtype>
<name>dataname>
<mtime>1567214197491mtime>
<permission>wenzhou:supergroup:rwxr-xr-xpermission>
<nsquota>-1nsquota>
<dsquota>-1dsquota>
inode>
<inode>
<id>16419id>
<type>DIRECTORYtype>
<name>aname>
<mtime>1557901778861mtime>
<permission>wenzhou:supergroup:rwxr-xr-xpermission>
<nsquota>-1nsquota>
<dsquota>-1dsquota>
inode>
<inode>
<id>16424id>
<type>FILEtype>
<name>part-00000name>
<replication>3replication>
<mtime>1557901778851mtime>
<atime>1557901778803atime>
<perferredBlockSize>134217728perferredBlockSize>
<permission>wenzhou:supergroup:rw-r--r--permission>
<blocks>
<block>
<id>1073741831id>
<genstamp>1007genstamp>
<numBytes>8numBytes>
block>
blocks>
inode>
editlog结构如下,保存的是具体操作和对应的信息
<RECORD>
<OPCODE>OP_START_LOG_SEGMENTOPCODE>
<DATA>
<TXID>666TXID>
DATA>
RECORD>
<RECORD>
<OPCODE>OP_MKDIROPCODE>
<DATA>
<TXID>667TXID>
<LENGTH>0LENGTH>
<INODEID>16484INODEID>
<PATH>/test3PATH>
<TIMESTAMP>1569642205798TIMESTAMP>
<PERMISSION_STATUS>
<USERNAME>wenzhouUSERNAME>
<GROUPNAME>supergroupGROUPNAME>
<MODE>493MODE>
PERMISSION_STATUS>
DATA>
RECORD>
<RECORD>
<OPCODE>OP_ADDOPCODE>
<DATA>
<TXID>668TXID>
<LENGTH>0LENGTH>
<INODEID>16485INODEID>
<PATH>/test3/edit.xml._COPYING_PATH>
<REPLICATION>1REPLICATION>
<MTIME>1569642220389MTIME>
<ATIME>1569642220389ATIME>
<BLOCKSIZE>134217728BLOCKSIZE>
<CLIENT_NAME>DFSClient_NONMAPREDUCE_759388144_1CLIENT_NAME>
<CLIENT_MACHINE>127.0.0.1CLIENT_MACHINE>
<OVERWRITE>trueOVERWRITE>
<PERMISSION_STATUS>
<USERNAME>wenzhouUSERNAME>
<GROUPNAME>supergroupGROUPNAME>
<MODE>420MODE>
PERMISSION_STATUS>
<RPC_CLIENTID>7e15e3a5-a0f1-4cd6-9cb2-969812945d5bRPC_CLIENTID>
<RPC_CALLID>3RPC_CALLID>
DATA>
RECORD>
值得注意的是,上述文件并没有保存Block和对应DN节点的映射,实际上这个也不是namenode元数据保存的,namenode只负责保存文件结构和block信息,具体的block和DN是由DN自己上报的。
如下,实际上,namenode刚开始启动时,加载fsimage合并edit这一过程中始终处于安全模式,也就是hdfs对于客户端是只读的。
具体DN向NN(NameNode)汇报,此过程为z之前说的BlockReport,通过blockReport构建BlocksMap的结构如下,此时内存中维护block->DN的映射。
可以通过hdfs dfsadmin -safemode查看和进入进出安全模式状态。
通过元数据的WAL方式可以保证数据的可靠性,但是没法保证NN挂掉时的可用性,为了解决这个问题,hadoop 2.X引入HA机制,简单来说就是维护active和standby两个NN节点,当active挂掉时立即用standby替换。
示意图如下
根据上述原理,具体配置包括
具体配置参考https://www.cnblogs.com/dflmg/p/10167879.html和https://www.cnblogs.com/jifengblog/p/9307702.html
实际大型hadoop集群都是由很多机器组成的,具体对应到机房不同机架的机器,通常单个机架30-40共享一个交换机(10-40G),各机架又通过上行链路与一个核心交换机或路由器互联,典型结构如下。同一机架内部的带宽远高于机架间的,因此我们期望hdfs在存取文件时优先选用同一机架的。
默认hdfs是随机存取的,为了让hdfs感知到机架结构,必须我们告诉它对应的网络拓扑结构才行。
<property>
<name>net.topology.node.switch.mapping.implname>
<value>实现接口的类的全路径value>
property>
<property>
<name>topology.script.file.namename>
<value>/path/to/RackAware.pyvalue>
此脚本接受一个参数,输出一个值。接受的参数通常为某台datanode机器的ip地址,而输出的值通常为该ip地址对应的datanode所在的rack,例如”/rack1”。Namenode启动时,会判断该配置选项是否为空,如果非空,则表示已经用机架感知的配置,此时namenode会根据配置寻找该脚本,并在接收到每一个datanode的heartbeat时,将该datanode的ip地址作为参数传给该脚本运行,并将得到的输出作为该datanode所属的机架,保存到内存的一个map中。
目前使用的机架存储方案如下,这样保证本客户端再次读取新写的数据时,直接从本地读取,这样延迟最小,读取速度最快。