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
cat ~/fsimage.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在文件创建过会周期性校验