首页 / 资讯中心 / 文章详情

SecondaryNameNode是NameNode备份吗?一文理清HDFS元数据合并原理

SecondaryNameNode是NameNode备份吗?一文理清HDFS元数据合并原理 ★ FEATURED ARTICLE
1. 先别急着背面试题SecondaryNameNode到底解决了什么很多人在学习大数据的时候第一次接触HDFS就被NameNode和SecondaryNameNode的关系搞糊涂了。网上流传最广的说法是“SecondaryNameNode是NameNode的备份”这句话对吗我可以直接说不准确甚至有点误导。我见过太多人在面试的时候把“SecondaryNameNode就是用来故障转移的备用节点”挂在嘴边结果面试官一追问就露馅了。先说结论SecondaryNameNode的核心职责是定期合并NameNode的编辑日志EditsLog和镜像文件FsImage生成新的检查点Checkpoint从而缓解NameNode自身的压力缩短NameNode重启时的恢复时间。它本质上是一个“辅助维护元数据状态”的角色而不是“随时准备顶替NameNode干活”的热备。那这个角色为什么会被设计出来这得从HDFS早期的元数据管理机制说起。HDFS把所有文件的目录结构、文件块映射、副本位置等信息统称为元数据存在NameNode的内存中。为了方便重启恢复NameNode又会把元数据持久化到磁盘具体就是两个文件FsImage元数据快照和EditsLog操作日志。问题就出在如果NameNode每次处理完客户端请求都立刻把最新的元数据完整写进FsImage那磁盘I/O开销会非常夸张NameNode的吞吐量会被严重拖累。所以在早期设计里NameNode只把增量变更追加写入EditsLogFsImage则保持旧的快照。NameNode重启时把FsImage加载进内存再逐条重放EditsLog里的操作就能得到最新元数据。这个方案本身没毛病但有一个隐患EditsLog会无限增长。集群运行越久日志文件越大NameNode重启时重放日志的时间就越长甚至可能出现日志文件大到重启耗时的程度完全不可接受。更麻烦的是一旦EditsLog损坏整个元数据就彻底完蛋了。SecondaryNameNode的引入正是为了解决这个“日志无限膨胀”的痛点。它周期性拉取NameNode的FsImage和EditsLog在内存中完成合并再把新的FsImage推回NameNode同时把旧的EditsLog清空。这样NameNode手里始终是一份“较新”的镜像加一小段增量日志重启恢复自然就快了。理解到这里你已经比那些只会背“SecondaryNameNode是NameNode的备份”的人强很多了。但这只是表层接下来我要把整个机制的原理、配置、坑点从头到尾拆开讲清楚。2. HDFS元数据管理机制先搞懂NameNode在忙什么要深入理解SecondaryNameNode必须先搞懂NameNode自己的元数据管理机制。这里我不讲教科书式的概念直接拿生活中的例子来类比。你可以把NameNode想象成一家公司的前台每天要处理大量访客登记。FsImage相当于一本厚厚的纸质花名册记录了当前所有员工的在册状态EditsLog则相当于一张流水小票上面记着“今天新入职了谁、谁离职了、谁的工位换了”这些变化。理论上每来一个人前台都可以把花名册全部重新誊写一遍但这显然不现实——太慢了。所以前台选择在花名册保持不变的情况下把每次变化都记在小票上。到了月末再把花名册和小票合并生成一本新的花名册。HDFS的NameNode就是这么干的。它平时收到客户端写文件、删目录、改权限等请求时先把这个操作记录到内存中的元数据里同时追加一条日志到EditsLog文件。这样即使机器突然断电内存数据丢失重启后也能靠FsImage加EditsLog恢复。这里有几个关键细节需要展开。2.1 FsImage里到底存了什么FsImage是HDFS元数据的完整快照里面保存了所有文件和目录的inode信息文件名、所有者、权限、时间戳等文件对应的数据块列表每个文件由哪些Block组成DataNode的注册信息与Block到DataNode的映射关系配额信息、快照信息等高级特性数据需要注意的是FsImage并不包含文件的实际内容文件内容分布在DataNode上。NameNode只负责“哪台机器上的哪个块属于哪个文件”这张映射表。2.2 EditsLog的增长模型每次客户端对NameNode发起写操作NameNode在内存更新完元数据后会同步写一条EditRecord到EditsLog。这个日志是追加写入的顺序I/O性能很好所以不会太影响系统吞吐。但代价是日志文件只增不减尤其是在频繁创建/删除文件、大量目录操作的时候EditsLog的膨胀速度相当惊人。我在一个测试集群上见过这种情况每天产生几千万个文件EditsLog一天就能涨到好几个GB。NameNode如果不做任何干预重启时加载FsImage只需要几分钟但重放这几个GB的EditsLog可能要花上半小时甚至更久。对于生产环境来说这种恢复时间是致命的。2.3 NameNode自身有什么保护手段很多人会问NameNode自己难道不能定期把EditsLog合并进FsImage吗实际上NameNode启动时确实会把EditsLog合并进FsImage然后清空旧日志。但NameNode运行期间它并不会主动执行合并操作。为什么因为合并操作需要把FsImage重新加载到内存、应用所有Edits、再写回磁盘这个过程非常消耗CPU和I/O。如果让NameNode自己频繁干这事就等于让前台一边接待访客一边誊写花名册前台早晚被累垮。所以HDFS早期设计才想到引入一个“编外人员”来专职干这个活这就是SecondaryNameNode的由来。3. SecondaryNameNode的工作机制与检查点触发条件现在进入正题SecondaryNameNode到底是怎么工作的它的内部流程是怎样的哪些条件会触发它执行合并很多人以为SecondaryNameNode是NameNode派生的子进程其实它们是两个独立的进程可以部署在不同机器上。SecondaryNameNode会定期主动联系NameNode执行检查点操作。3.1 检查点Checkpoint的完整流程一次完整的检查点操作可以拆成以下几步SecondaryNameNode发出检查点请求。它通过HTTP协议向NameNode请求执行检查点对应RPC接口或HTTP端点。NameNode滚动EditsLog。收到请求后NameNode会把当前正在写的EditsLog假设叫edits_inprogress关闭改名为edits_xxx编号递增的历史日志并新建一个空的edits_inprogress继续接收新操作。这一步非常关键它保证了“合并日志时最新来的操作不会被遗漏也不会被重复处理”。SecondaryNameNode拉取FsImage和历史EditsLog。它从NameNode下载最新的FsImage和刚才滚动出来的edits_xxx文件。在SecondaryNameNode内存中执行合并。把FsImage加载进内存再逐条重放edits_xxx里的操作得到一份包含最新元数据的FsImage。生成新的FsImage并回传NameNode。SecondaryNameNode把合并后的FsImage序列化通过HTTP上传回NameNode。NameNode替换旧镜像。NameNode收到新FsImage后用原子重命名的方式替换掉旧的FsImage并清理掉已经合并过的历史EditsLog。整个流程跑完NameNode手里就变成“一份最新的FsImage 一个几乎空白的EditsLog”压力瞬间归零。这里有个容易搞混的点在合并期间客户端对NameNode的写操作并没有暂停。NameNode一直在写入新的edits_inprogress所以客户端完全无感知。这也是为什么SecondaryNameNode的合并操作可以“在线”执行不会造成集群停机。3.2 检查点的触发条件SecondaryNameNode不会无限频繁地执行检查点因为合并本身有成本。它主要根据两个条件来触发时间条件默认每隔3600秒1小时执行一次对应参数dfs.namenode.checkpoint.period。日志大小条件当NameNode的历史EditsLog总大小达到阈值时立即触发检查点对应参数dfs.namenode.checkpoint.txns默认1000000即100万条事务。还有一个关联参数dfs.namenode.checkpoint.check.period默认60秒SecondaryNameNode每隔60秒检查一次NameNode的日志总量。这两个条件只要满足任意一个就会触发检查点。实际生产环境里EditsLog增长快的集群往往等不到1小时就会被日志大小条件触发。增长慢的小集群则基本靠1小时的周期来兜底。以我自己的集群为例每天处理几百GB数据写入tps不算高大约每20~30分钟EditsLog就能攒够100万条事务所以检查点实际是每半小时左右执行一次远高于默认的1小时周期NameNode的日志文件始终维持在一个很小的规模。3.3 NameNode自身也有检查点能力需要特别说明的是从Hadoop 2.x开始NameNode自己也能执行检查点操作SaveNamespace机制和SecondaryNameNode类似。但即便如此生产环境依然推荐启用SecondaryNameNode或Standby NameNode来承担这个职责原因后面在HA架构里细说。4. 生产级部署SecondaryNameNode到底该怎么配、怎么放很多人搭集群的时候直接把SecondaryNameNode和NameNode放在同一台机器上就跑起来了图省事。对于一个学习环境或测试集群这么做没太大问题但生产环境这么干等于没解决问题——SecondaryNameNode合并FsImage时会消耗大量CPU和内存和NameNode抢资源不说如果这台机器挂了NameNode和SecondaryNameNode同时宕机恢复成本直接翻倍。所以正确的做法是把SecondaryNameNode部署到与NameNode不同的机器上。理想情况下这台机器应该和NameNode的硬件配置接近因为合并FsImage需要至少和FsImage同等大小的内存并且网络带宽要好因为检查点期间要传输FsImage和EditsLog。下面我给出一个生产环境常见的部署参考。假设你的HDFS集群有三台机器node-a跑NameNodenode-b跑SecondaryNameNodenode-c至node-e跑DataNode。那么你只要在node-b的hdfs-site.xml里加一个属性即可property namedfs.namenode.secondary.http-address/name valuenode-b:50090/value /property同时NameNode需要知道SecondaryNameNode的存在。在主namenode节点的hdfs-site.xml中确认以下参数property namedfs.namenode.checkpoint.period/name value3600/value /property property namedfs.namenode.checkpoint.txns/name value1000000/value /property配置完成后在SecondaryNameNode节点上执行启动命令hdfs --daemon start secondarynamenode启动后用jps查看进程应该能看到SecondaryNameNode字样。在SecondaryNameNode的工作目录下默认${dfs.namenode.name.dir}/../tmp/dfs/namesecondary你会看到定期生成的FsImage文件这就是合并成功的证据。4.1 工作目录与日志排查SecondaryNameNode合并时生成的FsImage默认存放在本地文件系统中通过dfs.namenode.secondary.http-address和dfs.namenode.name.dir间接决定。它从NameNode拉取的FsImage会放在自己的current目录下。如果你发现SecondaryNameNode一直报错、合并失败第一件事就是去看它的日志tail -f $HADOOP_LOG_DIR/hadoop-hdfs-secondarynamenode-node-b.log常见问题包括端口不通NameNode的50090端口没开、防火墙挡了、磁盘空间不足FsImage太大导致写不进去、RPC超时网络抖动导致拉取日志失败。这些在日志里都会有明确记录排查起来其实不难。4.2 内存怎么估算SecondaryNameNode进程的内存需求主要取决于FsImage的大小。经验上FsImage每占用1GB内存SecondaryNameNode大约需要1.5~2GB的堆内存因为合并过程中会有大量临时对象。如果集群的文件数在千万级别FsImage可能达到10GB以上SecondaryNameNode的堆内存至少得给20GB。这里我建议在启动脚本里显式指定堆大小。比如在hadoop-env.sh中新增一行export HDFS_SECONDARYNAMENODE_OPTS-Xms20g -Xmx20g注意-Xms和-Xmx最好设成一样的值避免运行期动态扩容带来性能抖动。这一点和NameNode调优类似。5. 一个非常常见的误区SecondaryNameNode能用来故障转移吗这是网上争论最多、误导最严重的一个点。我先给结论SecondaryNameNode不是热备不能直接用于NameNode故障转移。为什么不能因为SecondaryNameNode拿到的FsImage是周期性合并的产物本身存在滞后。它持有的EditsLog也不是实时的拉取完就断开连接了之后NameNode产生的日志它并没有。所以当NameNode宕机时SecondaryNameNode里的元数据状态大概率不是最新的。如果你直接拿SecondaryNameNode顶上去要么丢失大量最新元数据要么根本无法启动。那有没有办法拿SecondaryNameNode的数据来恢复NameNode有但只能作为“最后的救命稻草”。具体做法是把SecondaryNameNode的FsImage拷贝回NameNode的工作目录再找一份尽量新的EditsLog启动NameNode时它会继续重放日志。恢复的完整程度取决于你SecondaryNameNode上次合并的时间点。如果恰好刚合并完那丢的数据很少如果已经过去半小时那这半小时内的元数据操作就会丢失那些新创建的文件可能“消失”。与其说这是故障转移不如说是灾难恢复手段能找回多少算多少。真正用于故障转移的是另一个东西HAHigh Availability架构里的Standby NameNode。在HA模式下会部署一主一备两个NameNode通过JournalNode共享EditsLogStandby NameNode实时同步主NameNode的元数据状态。当主NameNode宕机时Standby能秒级晋升为Active这个才是热备。而且Standby NameNode自身也会周期性合并EditsLog相当于同时扮演了SecondaryNameNode的角色。所以我的建议是如果集群规模小、不允许额外开销、没有HA需求跑一个SecondaryNameNode作为日常检查点工具完全够用。如果集群重要程度高、需要快速恢复直接上HA用Standby NameNode替代SecondaryNameNode。HA模式下不需要再单独部署SecondaryNameNode否则纯属多浪费一台机器。这里我画个简单的对比表格方便你记忆角色核心职责元数据实时性能否自动故障转移SecondaryNameNode定期合并FsImage和EditsLog有滞后周期合并否只能辅助恢复Standby NameNode (HA)实时同步主NameNode状态同时承担合并日志准实时是秒级切换6. 实操中的典型问题与排查清单接下来这部分是我在实际运维中踩过、也帮别人排查过的坑挑几个高频的写出来。这些问题往往资料里不写但一旦遇到真的很耽误事。6.1 SecondaryNameNode合并失败日志报Connection Refused这个最基础十有八九是端口问题。检查NameNode的50090端口是否监听ss -lntp | grep 50090如果没有输出说明NameNode没启动HTTP服务或者dfs.namenode.secondary.http-address没生效。还有一种情况是NameNode和SecondaryNameNode之间有iptables/安全组拦截直接telnet测一下端口通不通telnet node-a 50090连不通就去查防火墙。简单粗暴的验证方法是先关掉防火墙测一次通了再精确放行端口。6.2 EditsLog暴涨检查点一直不触发如果发现NameNode的edits文件已经到了好几个GB但SecondaryNameNode迟迟不合并多半是dfs.namenode.checkpoint.txns设得太大了或者SecondaryNameNode根本没连上NameNode。先用hdfs namenode -recover试试手动恢复但这只是救急。更根本的排查方法是在SecondaryNameNode日志里搜Checkpoint关键字看它是否在周期性运行。如果压根没跑检查dfs.namenode.secondary.http-address是否指向了正确地址。此外还有一种特殊情况使用HA架构两个NameNode时SecondaryNameNode的检查点职责由Standby NameNode承担。如果你在HA集群中手动启动了SecondaryNameNode它会去连接错误的NameNode端口表现为日志一直报错但不干活。这种情况不要强行修复SecondaryNameNode而是应该确认HA是否正常工作。6.3 合并过程中NameNode短暂无响应其实“无响应”不是合并操作本身引起的。检查点期间NameNode需要滚动EditsLog、接收新FsImage并替换旧文件这些操作会短暂加锁。如果集群写入压力极大这个“短暂”可能会被放大表现为客户端超时。解决办法把检查点时间错开到业务低谷比如设置dfs.namenode.checkpoint.period为7200秒但配合dfs.namenode.checkpoint.txns触发。提升dfs.namenode.handler.count让NameNode有更多线程处理RPC请求。检查NameNode所在机器磁盘I/O用SSD替换机械盘效果立竿见影。6.4 SecondaryNameNode磁盘被FsImage塞满如果SecondaryNameNode的current目录所在分区太小合并几次后磁盘就满了。我在某次事故里就遇到过把SecondaryNameNode的工作目录放在根分区只有20GB跑了一周直接100%检查点彻底停摆。解决方法是把dfs.namenode.name.dir和SecondaryNameNode的dfs.namenode.checkpoint.dir都指向数据盘property namedfs.namenode.checkpoint.dir/name value/data/hdfs/namesecondary/value /property注意dfs.namenode.checkpoint.dir这个参数在较新的Hadoop版本里已经不太推荐直接修改而是推荐通过dfs.namenode.checkpoint.dir的替代实现。不过不同版本行为略有不同改完配置后务必用hdfs namenode -initializeSharedEdits或重启进程验证一下实际效果。6.5 集群从普通模式切换到HA模式时SecondaryNameNode报错很多人从非HA迁移到HA时会忘了移除SecondaryNameNode的配置。Standby NameNode会主动执行检查点操作此时再让SecondaryNameNode去连NameNode就会出现行为冲突。我的建议是HA模式下直接不启动SecondaryNameNode进程也不需要配置dfs.namenode.secondary.http-address让Standby NameNode兼任这个职责即可。7. 开动脑洞SecondaryNameNode的机制还启发了什么讲完技术细节我想聊聊这个设计思想本身。SecondaryNameNode这个角色之所以有意思是因为它体现了一种典型的“分离关注点”思路把NameNode从繁重的元数据持久化工作中解放出来让专业角色干专业的事。这种思路在后面的大数据组件里非常常见。比如流处理框架里的State Backend、数据库里的WAL归档与归档恢复典型的如某些分布式数据库的Paxos日志与snapshot分离本质上都是类似的逻辑实时请求处理走增量日志后台定期做全量快照合并。你掌握了这个思想再看很多组件的设计基本能举一反三。而且SecondaryNameNode的存在还暴露了一个早期分布式系统设计的朴素思路在硬件和软件能力有限的年代用“额外节点承担辅助工作”来换主节点稳定性。到了今天虽然HA已经取代了它的高可用职能但它作为检查点工具的价值依然存在。即便在HA集群里Standby NameNode也承担着一模一样的合并逻辑。搞懂了SecondaryNameNode你等于同时理解了HA里的核心机制之一。8. 最后说一个实用小技巧如何用SecondaryNameNode恢复损坏的NameNode元数据这个技巧是很多老运维不写进文档但实际常用的。假设你的NameNode机器损坏本地FsImage和EditsLog全部丢失但SecondaryNameNode还在你可以按下面的步骤抢救在SecondaryNameNode上找到最新的FsImage文件路径大致是${dfs.namenode.checkpoint.dir}/current/fsimage_xxx。把整个current目录拷贝到新机器或修复后的NameNode工作目录。在NameNode节点上执行hdfs namenode -recover这个命令会启动一个恢复模式让你选择性地加载FsImage和EditsLog。如果没有特殊需求直接选默认让它用SecondaryNameNode的FsImage作为起点再重放本地能找到的EditsLog。启动NameNode确认文件列表和DataNode块报告是否正常。需要强调这种恢复方式是有损的从上次检查点到宕机时刻之间的所有元数据变更都会丢失。文件数据本身如果已经写入DataNode并不会丢但文件在目录结构里的“登记”会消失客户端再访问时就会报FileNotFound。所以生产环境我更推荐的做法是把NameNode的FsImage和EditsLog定期备份到可靠存储上。手动做法是写一个定时脚本每天把NameNode的current目录同步到远程备份目录rsync -a --delete /data/hdfs/namenode/current/ backup-server:/backup/hdfs/namenode/这样即使集群整体炸了你也能把元数据恢复到当天的状态。有了这层兜底SecondaryNameNode的恢复方案只作为第二道保险而不是唯一依赖。我个人在实际操作中的体会是元数据的“冗余”永远不嫌多。NameNode进程可以靠SecondaryNameNode或者HA来保护但FsImage和EditsLog的物理备份一定要自己动手做。毕竟“一个副本都没有的文件等于不存在”这个教训我用惨痛的经历买过单。希望看到这里的你不用再踩一遍。
阅读完成 · 觉得有帮助?
咨询建站