接前文 分布式存储-HDFS 架构解析,我们总体分析了 HDFS 架构的主要构成组件包括:NameNode、DataNode 和 Client。本文首先进一步解析 HDFS NameNode 的设计和实现要点。

元数据持久化

NameNode 将所有元信息以特定的数据结构组织存放在内存中,对于 namespace 和 replication factor 的信息会进行持久化,而映射关系则不会持久化。因为映射关系是通过 DataNode 启动后定时汇报上来,即使 NameNode 重启后内存信息丢失也可以通过 DataNode 重新汇报获得,而其他元信息则必须通过读取持久化存储来重建内存数据结构。出于性能原因,所有元信息的读取直接从内存中获得,而增删改操作则借鉴来数据库的事务日志技术。每次只变更内存数据结构并记录操作日志,将随机写变为顺序写来提高吞吐能力。

NameNode 使用一个事务日志文件 EditLog 来持久化记录针对文件系统元数据的每一次操作变更。而整个 file system namesapce 文件、文件块和数据节点的映射关系被存储在另一个 FsImage 文件中。当 NameNode 启动时,它读取 FsImage 文件构建元数据的内存数据结构,接着读取 EditLog 文件应用所有的操作事务到内存数据结构中。接着基于最新的内存数据结构重新写一个新的 FsImage 文件到磁盘上,然后清除 EditLog 的内容,因为它上面记录的所有操作日志都已经反应到 FsImage 上了。这个过程称为建立了一个检查点(checkpoint),当然检查点技术也是数据库里最常用的了。启动过程中 NameNode 会进入一种特殊状态,称为安全模式(Safemode state),它等待 DataNode 报告文件块及其副本的数量并确定哪些文件的副本数量是不足的。

所有上述启动过程完成后,NameNode 才能对外提供服务。

数据分布策略

除了文件系统元数据管理外,NameNode 另一个重要作用是对写入的文件块选择合适 DataNode 来存放,称为 block placement policy。在通常情况下,replication factor(副本数)默认为 3, HDFS 的数据分布策略是将两份副本放在本地机架的两个不同 DataNode 上,最后一个副本放在另外一个机架的一个 DataNode 上。

NameNode 基于机架感知的数据分布策略并未考虑 DataNode 的磁盘空间利用率。这有效避免了将新的文件数据集中放置在一组拥有大量剩余空间的 DataNodes 上,比如新扩容的 DataNodes。这又带来另外一个问题,就是扩容新 DataNode 时导致数据分布的严重不平衡。

为了应对此类情况,HDFS 引入了一个平衡器工具(Balancer),在整个集群平衡磁盘空间利用率。这个工具通过引入设置一个在 0 和 1 之间的阈值来判断整个集群是否达到平衡。

HDFS 对于磁盘均衡的定义如下:

对于每一个 DataNode 其磁盘利用率与整个集群的平均利用率相比不超过设置的阈值偏差。

平衡器工作过程中确保不会减少副本总数,及其分布的机架数。也就是原来三个副本分布在两个机架,被平衡后也还是三个副本分布在两个机架,但可能移动到不同的 DataNode 上了。为了减少网络带宽占用,会尽可能避免机架间拷贝数据。例如:副本 A1 和 A2 在 Rack1,A3 在 Rack2,若平衡器认为 A1 所在 DataNode 磁盘利用率过高,需要移动 A1。在 Rack1 内找不到磁盘利用率低的其他 DataNode,则需要将 A1 移动到 Rack2。实际做法是直接从 Rack2 的 A3 复制一份得到 A4,并删除 Rack1 上的 A1,这样就避免了机架间复制。

复制管理

NameNode 努力确保每一个副本符合配置的 replication factor。假如 DataNode 宕机发生导致一些文件的 block 副本数变少,Namdenode 会发出复制命令给 DataNode,复制出新的副本以保持与配置的副本数一致。当宕机的 DataNode 恢复后重新加入集群后会导致一些文件的副本数超出配置数,NameNode 会检测到并删除多余的副本以节省存储空间。NameNode 维护一个复制优先级队列,对于副本不足的文件 block 按优先级排序,仅剩下一个副本的文件 block 享有最高的复制优先级。

性能设计

除了 NameNode 的单点和重启过程影响可用性外,另一个担忧因素是性能。

读操作基于内存访问还好,写操作中磁盘是一个瓶颈点。而 NameNode 支持大量 Client 的并发读写,对于大量的并发写操作 NameNode 进行了优化。多线程情况下,当一个线程为保存操作事务日志发起一个 flush-and-sync 到磁盘文件的操作,其他线程只能等待。为了优化此类情况,NameNode 将随机写转换为批量写操作。当一个 NameNode 的线程初始化了一个 flush-and-sync 操作,所有当时的事务操作日志被批量写入文件。其余的线程只需要检查它们的事务是否被保存到了文件而不再需要再发起 flush-and-sync 操作。

总结

上面描述了 NameNode 提供的功能及其设计实现要点,最后我们简单点评下它在架构设计上的权衡考量。首先 NameNode 作为中心节点简化了整体设计,但很显然它也是个单点,会影响可用性。其次 NameNode 将所有元数据存储在内存中,内存的容量决定了整个分布式文件系统能支持文件数量,如果是大量的小文件场景也是个问题。而且 HDFS 基于 java 实现,java 针对大堆内存的 GC 优化也是个麻烦事。再次上面描述的 NameNode 的启动过程看起来就很耗时,特别是在 FsImage 和 EditLog 都很大的情况下。而且在 NameNode 的启动完成前整个 HDFS 是不可用的,所以 NameNode 即使是重启也对整体的可用性有很大影响。

参考

[1] Hadoop Documentation. HDFS Architecture.

[2] Robert Chansler, Hairong Kuang, Sanjay Radia, Konstantin Shvachko, and Suresh Srinivas. The Hadoop Distributed File System


下面是我自己开的一个微信公众号 [瞬息之间],除了写技术的文章、还有产品的、行业和人生的思考,希望能和更多走在这条路上同行者交流,有兴趣可关注一下,谢谢。

后端分布式系列:分布式存储-HDFS NameNode 设计实现解析的更多相关文章

  1. HDFS NameNode 设计实现解析

    接前文 分布式存储-HDFS 架构解析,我们总体分析了 HDFS 架构的主要构成组件包括:NameNode.DataNode 和 Client.本文首先进一步解析 HDFS NameNode 的设计和 ...

  2. 后端分布式系列:分布式存储-HDFS 与 GFS 的设计差异

    「后端分布式系列」前面关于 HDFS 的一些文章介绍了它的整体架构和一些关键部件的设计实现要点. 我们知道 HDFS 最早是根据 GFS(Google File System)的论文概念模型来设计实现 ...

  3. 后端分布式系列:分布式存储-HDFS 异常处理与恢复

    在前面的文章 <HDFS DataNode 设计实现解析>中我们对文件操作进行了描述,但并未展开讲述其中涉及的异常错误处理与恢复机制.本文将深入探讨 HDFS 文件操作涉及的错误处理与恢复 ...

  4. 后端分布式系列:分布式存储-HDFS DataNode 设计实现解析

    前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...

  5. 后端分布式系列:分布式存储-HDFS Client 设计实现解析

    前面对 HDFS NameNode 和 DataNode 的架构设计实现要点做了介绍,本文对 HDFS 最后一个主要构成组件 Client 做进一步解析. 流式读取 HDFS Client 为客户端应 ...

  6. 后端分布式系列:分布式存储-HDFS 架构解析

    本文以 Hadoop 提供的分布式文件系统(HDFS)为例来进一步展开解析分布式存储服务架构设计的要点. 架构目标 任何一种软件框架或服务都是为了解决特定问题而产生的.还记得我们在 <分布式存储 ...

  7. 后端分布式系列:分布式存储-MySQL 数据库事务与复制

    好久没有写技术文章了,因为一直在思考 「后端分布式」这个系列到底怎么写才合适.最近基本想清楚了,「后端分布式」包括「分布式存储」和 「分布式计算」两大类.结合实际工作中碰到的问题,以寻找答案的方式来剖 ...

  8. HDFS DataNode 设计实现解析

    前文分析了 NameNode,本文进一步解析 DataNode 的设计和实现要点. 文件存储 DataNode 正如其名是负责存储文件数据的节点.HDFS 中文件的存储方式是将文件按块(block)切 ...

  9. HDFS Client 设计实现解析

    前面对 HDFS NameNode 和 DataNode 的架构设计实现要点做了介绍,本文对 HDFS 最后一个主要构成组件 Client 做进一步解析. 流式读取 HDFS Client 为客户端应 ...

随机推荐

  1. spring boot新建项目问题总结

    1.启动报:Unregistering JMX-exposed beans on shutdown 原因:以来的Tomcat没有启动 解决办法:在pom.xml加入依赖 <dependency& ...

  2. Unique-paths (动态规划)

    题目描述 A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below) ...

  3. ChatGirl 一个基于 TensorFlow Seq2Seq 模型的聊天机器人[中文文档]

    ChatGirl 一个基于 TensorFlow Seq2Seq 模型的聊天机器人[中文文档] 简介 简单地说就是该有的都有了,但是总体跑起来效果还不好. 还在开发中,它工作的效果还不好.但是你可以直 ...

  4. Map value类型不同的写法

    Map value类型不同的写法 Map<String, Object> accountMap=new HashMap<String, Object>(); int userI ...

  5. 腾讯云H5语音通信QoE优化

    本文首发在云+社区,未经许可,不得转载. 云+导语:4月21日,腾讯云+社区在京举办"'音'你而来,'视'而可见--音视频技术开发实战沙龙",腾讯音视频实验室高级工程师张轲围绕网络 ...

  6. drool-6.5的自学demo

    先丢代码地址 https://gitee.com/a247292980/drools 再丢pom.xml <project xmlns="http://maven.apache.org ...

  7. HTMLParser使用简介

    HTMLParser具有小巧,快速的优点,缺点是相关文档比较少(英文的也少),很多功能需要自己摸索.对于初学者还是要费一些功夫的,而一旦上手以后,会发现HTMLParser的结构设计很巧妙,非常实用, ...

  8. DEV中svg图标的使用

    0.开始之前 先看看使用效果 在操作栏的使用: 在菜单中的使用 1.简述SVG图标 中文名:可缩放矢量图形 外文名:Scalable Vector Graphics 外语缩写:SVG 开发商:万维网联 ...

  9. windows下 gvim8.0 编译器配置

    最近由于各种原因,IDE从source insight换成了vim,参考了诸多博客的文章,折腾了好久折腾了个大概的样子,现在总结一下经验: 主要参考: 改造vim变成source insight Wi ...

  10. Node.js 子进程

    稳定性: 3 - 稳定 Node 通过 child_process 模块提供了 popen(3) 数据流. 它能在非阻塞的方式中,通过 stdin, stdout, 和 stderr 传递数据. (请 ...