在现有的HDFS中,为了保证元数据的高可用性,我们可以在配置项dfs.namenode.name.dir中配置多个元数据存储目录来达到多备份的作用。这样一来,如果其中一个目录文件损坏了,我们可以选择另外可用的文件。那么问题来了,如果所有备用的元数据都损坏了,不能用了,这个时候怎么办,那么是否就意味着集群就永远启动不起来了呢?这将会是一个多么糟糕的结果啊。在这里,我们就要引出本文的主题:HDFS的数据恢复模式(Recovery Mode)。
其实就是元数据的自我恢复的启动模式,所以他并不是DataNode上真实数据的恢复。
另外,数据恢复针对的是editlog,而不是fsimage。
HDFS数据恢复模式的原理:
NameNode在启动的时候,会通过FSNamesystem加载fsimage文件和editlog. 如果Editlog文件有损坏,会导致NameNode启动异常,从而导致NameNode不能正常启动.
在Recovery Mode下,NameNode则会智能的跳过这些错误的情况,从而保证NameNode成功启动,启动之后,会生成一个新的FSImage,然后再次退出,下一次就可以正常的启动的集群了,因为现在用的是新的fsimage
第一步:使用hdfs命令: hdfs namenode -recover
第二步:NameNode启动,会根据参数recover,进入recover流程,NameNode.doRecovery(startOpt,conf);
private static void doRecovery(StartupOption startOpt, Configuration conf) throws IOException {
String nsId = DFSUtil.getNamenodeNameServiceId(conf);
String namenodeId = HAUtil.getNameNodeId(conf, nsId);
initializeGenericKeys(conf, nsId, namenodeId);
//省略
FSNamesystem fsn = null;
try {
//开始正式进入数据恢复流程
fsn = FSNamesystem.loadFromDisk(conf);
fsn.getFSImage().saveNamespace(fsn);
MetaRecoveryContext.LOG.info("RECOVERYCOMPLETE");
} catch (IOException e) {
MetaRecoveryContext.LOG.info("RECOVERYFAILED: caught exception", e);
throw e;
} catch (RuntimeException e) {
MetaRecoveryContext.LOG.info("RECOVERYFAILED: caught exception", e);
throw e;
} finally {
if (fsn != null)
fsn.close();
}
}
第三步: FSNamesystem调用loadFromDisk会判断参数是不是RECOVER,如果是RECOVER则会设置安全模式
第四步: 构造数据恢复上下文对象
MetaRecoveryContextrecovery = startOpt.createRecoveryContext();
final boolean staleImage = fsImage.recoverTransitionRead(startOpt, this, recovery);
第五步: recoverTransitionRead会调用loadFSImage方法,最后会调用到LoadEditRecords方法,如果文件损坏,如果不是recover模式,抛出异常;否则跳过错误记录,重新定位到下一个有效的操作记录
第六步:读完有效的editlog记录,就把它apply到内存
第七步:然后NameNode会执行一次saveNameSpace方法,就会生成一个新的FSImage文件