目录
HDFSNN 和 2NN 工作机制
思考:NameNode 中的元数据是存储在哪里的?
首先,我们做个假设,如果存储在 NameNode 节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的 FsImage。
这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新 FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦 NameNode 节点断电,就会产生数据丢失。因此,引入 Edits 文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到 Edits 中。这样,一旦 NameNode 节点断电,可以通过 FsImage 和 Edits 的合并,合成元数据。
但是,如果长时间添加数据到 Edits 中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行 FsImage 和 Edits 的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于 FsImage 和 Edits 的合并。
1)第一阶段:NameNode 启动
- (1)第一次启动 NameNode 格式化后,创建 Fsimage 和 Edits 文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
- (2)客户端对元数据进行增删改的请求。
- (3)NameNode 记录操作日志,更新滚动日志。
- (4)NameNode 在内存中对元数据进行增删改。
2)第二阶段:Secondary NameNode 工作
- (1)Secondary NameNode 询问 NameNode 是否需要 CheckPoint。直接带回 NameNode是否检查结果。
- (2)Secondary NameNode 请求执行 CheckPoint。
- (3)NameNode 滚动正在写的 Edits 日志。
- (4)将滚动前的编辑日志和镜像文件拷贝到 Secondary NameNode。
- (5)Secondary NameNode 加载编辑日志和镜像文件到内存,并合并。
- (6)生成新的镜像文件 fsimage.chkpoint。
- (7)拷贝 fsimage.chkpoint 到 NameNode。
- (8)NameNode 将 fsimage.chkpoint 重新命名成 fsimage。

NN 和 2NN 工作机制详解:
Fsimage:NameNode 内存中元数据序列化后形成的文件。
Edits:记录客户端更新元数据信息的每一步操作(可通过 Edits 运算出元数据)。
NameNode 启动时,先滚动 Edits 并生成一个空的 edits.inprogress,然后加载 Edits 和Fsimage 到内存中,此时 NameNode 内存就持有最新的元数据信息。Client 开始对NameNode 发送元数据的增删改的请求,这些请求的操作首先会被记录到 edits.inprogress中(查询元数据的操作不会被记录在 Edits 中,因为查询操作不会更改元数据信息),如果此时 NameNode 挂掉,重启后会从 Edits 中读取元数据的信息。然后,NameNode 会在内存中执行元数据的增删改的操作。
由于 Edits 中记录的操作会越来越多,Edits 文件会越来越大,导致 NameNode 在启动加载 Edits 时会很慢,所以需要对 Edits 和 Fsimage 进行合并(所谓合并,就是将 Edits 和Fsimage 加载到内存中,照着 Edits 中的操作一步步执行,最终形成新的 Fsimage)。SecondaryNameNode 的作用就是帮助 NameNode 进行 Edits 和 Fsimage 的合并工作。
SecondaryNameNode 首先会询问 NameNode 是否需要 CheckPoint(触发 CheckPoint需要满足两个条件中的任意一个,定时时间到和 Edits 中数据写满了)。直接带回NameNode 是否检查结果。SecondaryNameNode 执行 CheckPoint 操作,首先会让 NameNode滚动 Edits 并生成一个空的edits.inprogress,滚动 Edits 的目的是给 Edits 打个标记,以后所有新的操作都写入 edits.inprogress,其他未合并的 Edits 和 Fsimage 会拷贝到SecondaryNameNode 的本地,然后将拷贝的 Edits 和 Fsimage 加载到内存中进行合并,生成 fsimage.chkpoint,然后将 fsimage.chkpoint 拷贝给 NameNode,重命名为 Fsimage 后替换掉原来的 Fsimage。NameNode 在启动时就只需要加载之前未合并的 Edits 和 Fsimage即可,因为合并过的 Edits 中的元数据信息已经被记录在 Fsimage 中。
Fsimage 和 Edits 解析
Fsimage和Edits概念
NameNode被格式化之后,将在/opt/module/hadoop-2.7.2/data/tmp/dfs/name/current目录中产生如下文件:
- fsimage_0000000000000000000
- fsimage_0000000000000000000.md5
- seen_txid
- VERSION
- (1)Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
- (2)Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到Edits文件中。
- (3)seen_txid文件保存的是一个数字,就是最后一个edits_的数字
- (4)每 次NameNode启动的时候都会将Fsimage文件读入内存,加 载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并。
1)oiv 查看 Fsimage 文件
(1)查看 oiv 和 oev 命令
[hadoop@hadoop102 current]$ hdfs
oiv apply the offline fsimage viewer to an fsimage
oev apply the offline edits viewer to an edits file
(2)基本语法
hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
(3)案例实操
[hadoop@hadoop102 current]$ pwd
/opt/module/hadoop-3.1.3/data/dfs/name/current
[hadoop@hadoop102 current]$ hdfs oiv -p XML -i fsimage_0000000000000000025 -o /opt/module/hadoop-3.1.3/fsimage.xml
[hadoop@hadoop102 current]$ cat /opt/module/hadoop-3.1.3/fsimage.xml
将显示的 xml 文件内容拷贝到 IDEA 中创建的 xml 文件中,并格式化。部分显示结果如下。
<inode>
<id>16386</id>
<type>DIRECTORY</type>
<name>user</name>
<mtime>1512722284477</mtime>
<permission>atguigu:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16387</id>
<type>DIRECTORY</type>
<name>atguigu</name>
<mtime>1512790549080</mtime>
<permission>atguigu:supergroup:rwxr-xr-x</permission>
<nsquota>-1</nsquota>
<dsquota>-1</dsquota>
</inode>
<inode>
<id>16389</id>
<type>FILE</type>
<name>wc.input</name>
<replication>3</replication>
<mtime>1512722322219</mtime>
<atime>1512722321610</atime>
<perferredBlockSize>134217728</perferredBlockSize>
<permission>atguigu:supergroup:rw-r--r--</permission>
<blocks>
<block>
<id>1073741825</id>
<genstamp>1001</genstamp>
<numBytes>59</numBytes>
</block>
</blocks>
</inode >
思考:可以看出,Fsimage 中没有记录块所对应 DataNode,为什么?
在集群启动后,要求 DataNode 上报数据块信息,并间隔一段时间后再次上报。
2)oev 查看 Edits 文件
(1)基本语法
hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
(2)案例实操
[hadoop@hadoop102 current]$ hdfs oev -p XML -i edits_0000000000000000012-0000000000000000013 -o /opt/module/hadoop-3.1.3/edits.xml
[hadoop@hadoop102 current]$ cat /opt/module/hadoop-3.1.3/edits.xml
将显示的 xml 文件内容拷贝到 Eclipse 中创建的 xml 文件中,并格式化,显示结果如下。
<?xml version="1.0" encoding="UTF-8"?>
<EDITS>
<EDITS_VERSION>-63</EDITS_VERSION>
<RECORD>
<OPCODE>OP_START_LOG_SEGMENT</OPCODE>
<DATA>
<TXID>129</TXID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ADD</OPCODE>
<DATA>
<TXID>130</TXID>
<LENGTH>0</LENGTH>
<INODEID>16407</INODEID>
<PATH>/hello7.txt</PATH>
<REPLICATION>2</REPLICATION>
<MTIME>1512943607866</MTIME>
<ATIME>1512943607866</ATIME>
<BLOCKSIZE>134217728</BLOCKSIZE>
<CLIENT_NAME>DFSClient_NONMAPREDUCE_-1544295051_1</CLIENT_NAME>
<CLIENT_MACHINE>192.168.1.5</CLIENT_MACHINE>
<OVERWRITE>true</OVERWRITE>
<PERMISSION_STATUS>
<USERNAME>atguigu</USERNAME>
<GROUPNAME>supergroup</GROUPNAME>
<MODE>420</MODE>
</PERMISSION_STATUS>
<RPC_CLIENTID>908eafd4-9aec-4288-96f1-e8011d181561</RPC_CLIENTID>
<RPC_CALLID>0</RPC_CALLID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ALLOCATE_BLOCK_ID</OPCODE>
<DATA>
<TXID>131</TXID>
<BLOCK_ID>1073741839</BLOCK_ID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_SET_GENSTAMP_V2</OPCODE>
<DATA>
<TXID>132</TXID>
<GENSTAMPV2>1016</GENSTAMPV2>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_ADD_BLOCK</OPCODE>
<DATA>
<TXID>133</TXID>
<PATH>/hello7.txt</PATH>
<BLOCK>
<BLOCK_ID>1073741839</BLOCK_ID>
<NUM_BYTES>0</NUM_BYTES>
<GENSTAMP>1016</GENSTAMP>
</BLOCK>
<RPC_CLIENTID></RPC_CLIENTID>
<RPC_CALLID>-2</RPC_CALLID>
</DATA>
</RECORD>
<RECORD>
<OPCODE>OP_CLOSE</OPCODE>
<DATA>
<TXID>134</TXID>
<LENGTH>0</LENGTH>
<INODEID>0</INODEID>
<PATH>/hello7.txt</PATH>
<REPLICATION>2</REPLICATION>
<MTIME>1512943608761</MTIME>
<ATIME>1512943607866</ATIME>
<BLOCKSIZE>134217728</BLOCKSIZE>
<CLIENT_NAME></CLIENT_NAME>
<CLIENT_MACHINE></CLIENT_MACHINE>
<OVERWRITE>false</OVERWRITE>
<BLOCK>
<BLOCK_ID>1073741839</BLOCK_ID>
<NUM_BYTES>25</NUM_BYTES>
<GENSTAMP>1016</GENSTAMP>
</BLOCK>
<PERMISSION_STATUS>
<USERNAME>atguigu</USERNAME>
<GROUPNAME>supergroup</GROUPNAME>
<MODE>420</MODE>
</PERMISSION_STATUS>
</DATA>
</RECORD>
</EDITS >
思考:NameNode 如何确定下次开机启动的时候合并哪些 Edits?
CheckPoint 时间设置
1)通常情况下,SecondaryNameNode 每隔一小时执行一次。
hdfs-default.xml
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>3600s</value>
</property>
2)一分钟检查一次操作次数,当操作次数达到 1 百万时,SecondaryNameNode 执行一次。
<property>
<name>dfs.namenode.checkpoint.txns</name>
<value>1000000</value>
<description>操作动作次数</description>
</property>
<property>
<name>dfs.namenode.checkpoint.check.period</name>
<value>60s</value>
<description> 1分钟检查一次操作次数</description>
</property >
NameNode 故障处理
NameNode 故障后,可以采用如下两种方法恢复数据。
1)将 SecondaryNameNode 中数据拷贝到 NameNode 存储数据的目录;
- (1)kill -9 NameNode 进程
- (2)删除 NameNode 存储的数据(/opt/module/hadoop-3.1.3/data/tmp/dfs/name)
[hadoop@hadoop102 hadoop-3.1.3]$ rm -rf /opt/module/hadoop-3.1.3/data/dfs/name/*
- (3)拷贝 SecondaryNameNode 中数据到原 NameNode 存储数据目录
[hadoop@hadoop102 dfs]$ scp -r hadoop@hadoop104:/opt/module/hadoop-3.1.3/data/dfs/namesecondary/* ./name/
- (4)重新启动 NameNode
[hadoop@hadoop102 hadoop-3.1.3]$ hdfs --daemon start namenode
2)使用-importCheckpoint 选项启动 NameNode 守护进程,从而将 SecondaryNameNode中数据拷贝到 NameNode 目录中。
- (1)修改 hdfs-site.xml 中的
<property>
<name>dfs.namenode.checkpoint.period</name>
<value>120</value>
</property>
<property>
<name>dfs.namenode.name.dir</name>
<value>/opt/module/hadoop-3.1.3/data/dfs/name</value>
</property>
- (2)kill -9 NameNode 进程
- (3)删除 NameNode 存储的数据(/opt/module/hadoop-3.1.3/data/dfs/name)
[hadoop@hadoop102 hadoop-3.1.3]$ rm -rf /opt/module/hadoop-3.1.3/data/dfs/name/*
- ( 4 ) 如 果 SecondaryNameNode 不 和 NameNode 在 一 个 主 机 节 点 上 , 需 要 将SecondaryNameNode 存储数据的目录拷贝到 NameNode 存储数据的平级目录,并删除in_use.lock 文件。
[hadoop@hadoop102 dfs]$ scp -r hadoop@hadoop104:/opt/module/hadoop-3.1.3/data/dfs/namesecondary ./
[hadoop@hadoop102 namesecondary]$ rm -rf in_use.lock
[hadoop@hadoop102 dfs]$ pwd
/opt/module/hadoop-3.1.3/data/dfs
[hadoop@hadoop102 dfs]$ ls
data name namesecondary
(5)导入检查点数据(等待一会 ctrl+c 结束掉)
[hadoop@hadoop102 hadoop-3.1.3]$ bin/hdfs namenode -importCheckpoint
(6)启动 NameNode
[hadoop@hadoop102 hadoop-3.1.3]$ hdfs --daemon start namenode
集群安全模式
1、NameNode启动
NameNode启动时,首先将镜像文件(Fsimage)载入内存,并执行编辑日志(Edits)中的各项操作。一
旦在内存中成功建立文件系统元数据的映像,则创建一个 空的编辑日志。此 时,NameNode开始监听
DataNode请求。这个过程期间,NameNode一直运行在安全模式,即NameNode的文件系统对于客户端来说是只读的。
2、DataNode启动
系统中的数据块的位置并不是由NameNode维护的,而是以块列表的形式存储在DataNode中。在系统的
正常操作期间,NameNode会在内存中保留所有块位置的映射信息。在安全模式下,各 个DataNode会 向
NameNode发送最新的块列表信息,NameNode了解到足够多的块位置信息之后,即可高效运行文件系统。
3、安全模式退出判断
如果满足“最小副本条件”,NameNode会在30秒钟之后就退出安全模式。所谓的最小副本条件指的是在
整个文件系统中99.9%的块满足最小副本级别(默认值:dfs.replication.min=1)。在启动一个刚刚格式化的HDFS集群时,因为系统中还没有任何块,所以NameNode不会进入安全模式。
1)基本语法
集群处于安全模式,不能执行重要操作(写操作)。集群启动完成后,自动退出安全模式。
- (1)bin/hdfs dfsadmin -safemode get (功能描述:查看安全模式状态)
- (2)bin/hdfs dfsadmin -safemode enter (功能描述:进入安全模式状态)
- (3)bin/hdfs dfsadmin -safemode leave (功能描述:离开安全模式状态)
- (4)bin/hdfs dfsadmin -safemode wait (功能描述:等待安全模式状态)
2)案例
模拟等待安全模式
3)查看当前模式
[hadoop@hadoop102 hadoop-3.1.3]$ hdfs dfsadmin -safemode get
Safe mode is OFF
4)先进入安全模式
[hadoop@hadoop102 hadoop-3.1.3]$ bin/hdfs dfsadmin -safemode enter
5)创建并执行下面的脚本
在/opt/module/hadoop-3.1.3 路径上,编辑一个脚本 safemode.sh
[hadoop@hadoop102 hadoop-3.1.3]$ touch safemode.sh
[hadoop@hadoop102 hadoop-3.1.3]$ vim safemode.sh
#!/bin/bash
hdfs dfsadmin -safemode wait
hdfs dfs -put /opt/module/hadoop-3.1.3/README.txt /
[hadoop@hadoop102 hadoop-3.1.3]$ chmod 777 safemode.sh
[hadoop@hadoop102 hadoop-3.1.3]$ ./safemode.sh
6)再打开一个窗口,执行
[hadoop@hadoop102 hadoop-3.1.3]$ bin/hdfs dfsadmin -safemode leave
7)观察
8)再观察上一个窗口
Safe mode is OFF
9)HDFS 集群上已经有上传的数据了
NameNode 多目录配置
1)NameNode 的本地目录可以配置成多个,且每个目录存放内容相同,增加了可靠性
2)具体配置如下
- (1)在 hdfs-site.xml 文件中添加如下内容
<property>
<name>dfs.namenode.name.dir</name>
<value>file://${hadoop.tmp.dir}/dfs/name1,file://${hadoop.tmp.dir}/dfs/name2</value>
</property>
- (2)停止集群,删除三台节点的 data 和 logs 中所有数据
[hadoop@hadoop102 hadoop-3.1.3]$ rm -rf data/ logs/
[hadoop@hadoop103 hadoop-3.1.3]$ rm -rf data/ logs/
[hadoop@hadoop104 hadoop-3.1.3]$ rm -rf data/ logs/
- (3)格式化集群并启动。
[hadoop@hadoop102 hadoop-3.1.3]$ bin/hdfs namenode –format
[hadoop@hadoop102 hadoop-3.1.3]$ sbin/start-dfs.sh
- (4)查看结果
[hadoop@hadoop102 dfs]$ ll
总用量 12
drwx------. 3 hadoop hadoop 4096 12 月 11 08:03 data
drwxrwxr-x. 3 hadoop hadoop 4096 12 月 11 08:03 name1
drwxrwxr-x. 3 hadoop hadoop 4096 12 月 11 08:03 name2