HDFS内部原理和机制
网络拓扑-节点距离计算
在写文件操作的时候,客户端会优先选择离上传节点最近的DataNode执行上传操作。
节点距离的计算方式: 两个节点到达最近的公共祖先节点的距离总和。
比如 机器的节点有 公网 ,机房 ,机架。

举例说明:
- d1,r1,n-0 和d1,r1,n-0 之间的距离 为 0 ==》 祖先节点是自己都是0
- d1,r1,n-0 和d1,r1,n-2 之间的距离是2
- d1,r2,n-2 和d2,r4,n-0 之间的距离是 6
HDFS中会根据节点距离来计算如何写入数据。
当写入数据选择数据节点的时候基本原则是:
- 优先写入客户端所在数据节点。(客户端在集群内的情况下,如果客户端不在集群内就随机选择一个)
- 第二个副本在另一个机架的随机一个节点
- 第三方副本在第二个副本所在机架的随机节点
这样分配的好处:
优先写入客户端所在的数据节点,可以优先保证传输效率。
第二个副本在其他机架是为了支持数据容错性。
第三个副本和第二个副本在同一个机器是为了第二个副本和第三个副本的传输效率。
NameNode 和 SecondaryNameNode 工作机制
对应NameNode 是存储了文件的元数据信息。
为了提高对文件的计算性能,NameNode会把这些文件的数据加载到内存中去。这样当有请求获取的相关的信息的时候,性能非常好,同时为了数据的安全性,这些数据也是持久化到磁盘上的。
nameNode持久化到磁盘的文件有2种文件,一种是 fsimage 文件。fsimage 文件拥有着 某个时间点的全量数据。而有新的数据相关的操作后,并不会去修改fsimage,而是新增一个 edits 文件,edits 文件中,记录了做了那些文件修改操作,而内存中的数据 = fsimage 的结果 + edits 文件的修改;而edits 的数据也不会一直递增,会定期合并edits 修改 到fsimage 生成一个更新的镜像文件。
这样当服务重启的时候,可以快速将fsimage 的快照 和少量的edits 操作恢复到内存中。
- fsimage 文件是全量备份文件
- edits 文件是增量记录文件
- 2nn 会定期执行edits 文件合并到 fsimage 中
这种机制和 redis的持久化方式有点类似, redis 开启aof重写的方式

系统启动的时候NameNode 加载fsimage 和edits 文件到内存中.客户端的更改记录到edit_inprogress 文件中.
(inprogress文件表示当前正在使用滚动记录的文件)
2nn 定期触发 checkpoint 操作
- 配置文件的定时时间到了(默认1小时)
- 检查到有些文件的edits 中的数据超过了限定值(也可配置)
触发合并操作的时候,NameNode 滚动更换 edits ,换一个新的文件写入。
滚动前的fsimage 和edits 发送到2nn
2nn 执行合并操作 成一个文件fsimage.checkpoint
2nn 将文件fsimage,checkpoint 改成fsimage 并同步给NameNode 最终再同步到2nn中
查看Name Node 的 data/name/current 文件夹可以看到这些文件

可以看到 fsimage 文件和 edits 文件都是多个;
对于fsimage 文件来说,数序大的是最新的数据。
而edits 文件,inprogress 文件是当前正在写入的文件,序号大与inprogress 的文件是未合并的文件,而序号小于inprogress 的文件是已经合并过的文件。
同时 seen_txid 文件中记录了一个数字,这个数字就是最后一个edits 的数字。
fsimage 和 edit 文件内容格式
fsimage 文件信息查看
1
| hdfs oiv -p XML -i fsimage_0000000000000000032 -o ~/fsimage.xml
|
oiv 执行输出格式为xml格式输出到指定的目录下的文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
| <?xml version="1.0"?> <fsimage> <version> <layoutVersion>-64</layoutVersion> <onDiskVersion>1</onDiskVersion> <oivRevision>ba631c436b806728f8ec2f54ab1e289526c90579</oivRevision> </version> <NameSection> <namespaceId>238095190</namespaceId> <genstampV1>1000</genstampV1> <genstampV2>1003</genstampV2> <genstampV1Limit>0</genstampV1Limit> <lastAllocatedBlockId>1073741826</lastAllocatedBlockId> <txid>32</txid> </NameSection> <ErasureCodingSection> <erasureCodingPolicy> <policyId>1</policyId> <policyName>RS-6-3-1024k</policyName> <cellSize>1048576</cellSize> <policyState>DISABLED</policyState> <ecSchema> <codecName>rs</codecName> <dataUnits>6</dataUnits> <parityUnits>3</parityUnits> </ecSchema> </erasureCodingPolicy> <erasureCodingPolicy> <policyId>2</policyId> <policyName>RS-3-2-1024k</policyName> <cellSize>1048576</cellSize> <policyState>DISABLED</policyState> <ecSchema> <codecName>rs</codecName> <dataUnits>3</dataUnits> <parityUnits>2</parityUnits> </ecSchema> </erasureCodingPolicy> <erasureCodingPolicy> <policyId>3</policyId> <policyName>RS-LEGACY-6-3-1024k</policyName> <cellSize>1048576</cellSize> <policyState>DISABLED</policyState> <ecSchema> <codecName>rs-legacy</codecName> <dataUnits>6</dataUnits> <parityUnits>3</parityUnits> </ecSchema> </erasureCodingPolicy> <erasureCodingPolicy> <policyId>4</policyId> <policyName>XOR-2-1-1024k</policyName> <cellSize>1048576</cellSize> <policyState>DISABLED</policyState> <ecSchema> <codecName>xor</codecName> <dataUnits>2</dataUnits> <parityUnits>1</parityUnits> </ecSchema> </erasureCodingPolicy> <erasureCodingPolicy> <policyId>5</policyId> <policyName>RS-10-4-1024k</policyName> <cellSize>1048576</cellSize> <policyState>DISABLED</policyState> <ecSchema> <codecName>rs</codecName> <dataUnits>10</dataUnits> <parityUnits>4</parityUnits> </ecSchema> </erasureCodingPolicy> </ErasureCodingSection> <INodeSection> <lastInodeId>16394</lastInodeId> <numInodes>9</numInodes> <inode> <id>16385</id> <type>DIRECTORY</type> <name></name> <mtime>1649790885461</mtime> <permission>hadoop:supergroup:0755</permission> <nsquota>9223372036854775807</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16386</id> <type>DIRECTORY</type> <name>tmp</name> <mtime>1649786802940</mtime> <permission>hadoop:supergroup:0770</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16387</id> <type>DIRECTORY</type> <name>hadoop-yarn</name> <mtime>1649786802940</mtime> <permission>hadoop:supergroup:0770</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16388</id> <type>DIRECTORY</type> <name>staging</name> <mtime>1649786802940</mtime> <permission>hadoop:supergroup:0770</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16389</id> <type>DIRECTORY</type> <name>history</name> <mtime>1649786802996</mtime> <permission>hadoop:supergroup:0770</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16390</id> <type>DIRECTORY</type> <name>done</name> <mtime>1649786802940</mtime> <permission>hadoop:supergroup:0770</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16391</id> <type>DIRECTORY</type> <name>done_intermediate</name> <mtime>1649786802996</mtime> <permission>hadoop:supergroup:1777</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16392</id> <type>DIRECTORY</type> <name>folder1</name> <mtime>1649790916368</mtime> <permission>hadoop:supergroup:0755</permission> <nsquota>-1</nsquota> <dsquota>-1</dsquota> </inode> <inode> <id>16394</id> <type>FILE</type> <name>test</name> <replication>3</replication> <mtime>1649790958271</mtime> <atime>1649790916156</atime> <preferredBlockSize>134217728</preferredBlockSize> <permission>hadoop:supergroup:0644</permission> <blocks> <block> <id>1073741826</id> <genstamp>1003</genstamp> <numBytes>26</numBytes> </block> </blocks> <storagePolicyId>0</storagePolicyId> </inode> </INodeSection> <INodeReferenceSection></INodeReferenceSection> <SnapshotSection> <snapshotCounter>0</snapshotCounter> <numSnapshots>0</numSnapshots> </SnapshotSection> <INodeDirectorySection> <directory> <parent>16385</parent> <child>16392</child> <child>16386</child> </directory> <directory> <parent>16386</parent> <child>16387</child> </directory> <directory> <parent>16387</parent> <child>16388</child> </directory> <directory> <parent>16388</parent> <child>16389</child> </directory> <directory> <parent>16389</parent> <child>16390</child> <child>16391</child> </directory> <directory> <parent>16392</parent> <child>16394</child> </directory> </INodeDirectorySection> <FileUnderConstructionSection></FileUnderConstructionSection> <SecretManagerSection> <currentId>0</currentId> <tokenSequenceNumber>0</tokenSequenceNumber> <numDelegationKeys>0</numDelegationKeys> <numTokens>0</numTokens> </SecretManagerSection> <CacheManagerSection> <nextDirectiveId>1</nextDirectiveId> <numDirectives>0</numDirectives> <numPools>0</numPools> </CacheManagerSection> </fsimage>
|
通过此文件可以看到fsimage 通过xml的方式记录了文件目录的数据信息。
edit 文件的格式
1
| hdfs oev -p XML -i edits_0000000000000000013-0000000000000000032 -o ~/edits.xml
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <RECORD> <OPCODE>OP_ADD_BLOCK</OPCODE> <DATA> <TXID>18</TXID> <PATH>/floder1._COPYING_</PATH> <BLOCK> <BLOCK_ID>1073741825</BLOCK_ID> <NUM_BYTES>0</NUM_BYTES> <GENSTAMP>1001</GENSTAMP> </BLOCK> <RPC_CLIENTID/> <RPC_CALLID>-2</RPC_CALLID> </DATA> </RECORD> <RECORD> <OPCODE>OP_CLOSE</OPCODE> <DATA> <TXID>19</TXID> <LENGTH>0</LENGTH> <INODEID>0</INODEID> <PATH>/floder1._COPYING_</PATH> <REPLICATION>3</REPLICATION> <MTIME>1649790643914</MTIME> <ATIME>1649790641780</ATIME> <BLOCKSIZE>134217728</BLOCKSIZE> <CLIENT_NAME/> <CLIENT_MACHINE/> <OVERWRITE>false</OVERWRITE> <BLOCK> <BLOCK_ID>1073741825</BLOCK_ID> <NUM_BYTES>13</NUM_BYTES> <GENSTAMP>1001</GENSTAMP> </BLOCK> <PERMISSION_STATUS> <USERNAME>hadoop</USERNAME> <GROUPNAME>supergroup</GROUPNAME> <MODE>420</MODE> </PERMISSION_STATUS> </DATA> </RECORD>
|
可以看到通过xml的方式记录了对那些数据做了那些操作;
DataNode机制
在NameNode 的存储的信息中没有dataNode中的数据的块信息,数据的块信息是由每个对应的dataNode自己存储的,DataNode 会将自身的文件的块信息上报给NameNode.

一个数据块在DataNode上以文件的方式存储,一个是数据文件一个是元数据包括数据块的长度,块校验和,时间戳。

DataNode向NameNode注册后, 周期性(可配置)默认一小时向NameNode上报自身的所有块信息
DataNode 向NameNode的心跳是3秒一次,心跳的返回结果中带有NameNode给改DataNode的命令如复制块数据操作或删除数据块操作,如果超过10分钟NameNode没有收到DataNode的心跳,就认为此DataNode不可用了
数据完整性校验
为了验证数据的完整性,hdfs 使用crc (32) 数据校验算法来验证数据的完整性。
- 数据传输过程中会校验
- DataNode在文件创建过会周期性校验