02. HDFS 简介

Par @Martin dans le
Tags :

分布式文件系统

HDFS 是一种分布式文件系统, 即 Hadoop Distributed File System (Hadoop 的分布式文件系统).

常见的分布式文件系统有, GFS、HDFS、Lustre 、Ceph 、GridFS 、mogileFS、TFS、FastDFS等; 各自适用于不同的领域, 它们都不是系统级的分布式文件系统, 而是应用级的分布式文件存储服务.

什么是分布式文件系统?

数据量越来越多, 在一个操作系统管辖的范围存不下了, 那么就分配到更多的操作系统管理的磁盘中, 但是不方便管理和维护, 因此迫切需要一种系统来管理多台机器上的文件, 这就是分布式文件管理系统.

它是一种允许文件通过网络在多台主机上分享的文件系统, 可让多机器上的多用户分享文件和存储空间, 为进一步的大数据分析提供数据支持.

通透性: 让实际上是通过网络来访问文件的动作, 由程序与用户看来, 就像是访问本地的磁盘一般 (对比下共享文件的方式, 需要知道对方的 ip 地址才能访问).

容错: 自动数据冗余, 即使系统中有某些节点脱机, 整体来说系统仍然可以持续运作而不会有数据损失; 功能类似 Raid 自动备份功能, 在 hdfs-site.xml 文件中会配置个 dfs.replication 参数用来指定 Hadoop 集群的复制因子 (也就是一个文件会备份几份).

分布式文件管理系统很多, HDFS 只是其中一种; 它是流式的数据访问, 也就是说批量读取而非随机读取, 所以它擅长的是 OLAP, 而不是 OLTP, 适用于一次写入多次查询的情况, 不支持并发写情况, 不合适存储小文件.

一次写入即写入后无法更新, 只能删除重写, 2.0 好像支持追加内容.

不支持并发写并不是指不能同时存储多份文件, 而是指, 一份大文件被分成多个块时, 是一个块写满再写另一个块的, 不能同时写所有的块.

不合适存储小文件, 因为 NameNode 将文件系统中的元数据存储在内存中, 因此, HDFS 所能存储的文件数量会受到 NameNode 内存的限制.

下面是一张 HDFS 的简单示图, 参考下:

HDFS

Client 需要存储一份文件的时候, 如 a.log (200M), Client 先和 HDFS 中的 NameNode 取得联系, NameNode 从自己的集群中找到一个可以存储的 DataNode 并告诉 Client, Client 开始向指定的 DataNode 存储数据.
存储数据的时候, 会根据文件的大小分块存储(2.0 默认 128M), 当存储完一个块后, DataNode 会检查 NameNode 中设置的副本数量, 如果不止一份的话, 当前 DataNode 就会水平传递 (pipline) 块给其他的 DataNode, 然后再申请另一个块, 继续存储剩余的文件内容…以此类推.

HDFS 的可靠性

  • 冗余副本策略
    • 数据会根据复制因子保存多份, 即使系统中有某些节点脱机, 整体来说系统仍然可以持续运作而不会有数据损失
  • 机架感知
    • 集群一般放在不同机架上, 副本保存在不同的机架上, 这样可以防止机架故障时丢失数据
  • 心跳机制
    • NameNode 周期性从 DataNode 接收心跳信号和块报告 (blockport) 以验证 DataNode 是否宕掉及数据块是否正常
  • 安全模式
    • 当集群的 DataNode 不满足复制因子数据时, 集群进入安全模式, 不允许使用
  • 校验和
    • 在 DataNode 中, 除了保存数据块外, 还保存一份数据块的 .mete 文件, 该文件保存正常情况下数据块的校验和, DataNode 通过验证当前数据块的校验和与 .mete 中的是否相等来判断数据块是否正常
  • 回收站
    • 该功能默认关闭, 开启时, 当 HDFS 删除数据时, 不是直接删除, 而是放到回收站中, 避免误删
  • 元数据保护
    • 即 NameNode 中的元数据也可以配置成保存多份
  • 快照机制
    • 支持存储某个时间点的映像, 需要时, 可以使数据重迒返个时间点的状态

体系结构

  • NameNode
    • 事务日志
    • 映像文件
  • SecondaryNameNode
  • DataNode

NameNode

NameNode 是管理节点, 维护集群的命名空间 (就是集群 HDFS 的目录结构), 包括文件目录树、文件/目录的元数据、每个文件对应的数据块列表, 以及接收用户的操作请求.

MetaData (元数据)

存储细节: 为了性能和安全兼具, Metadata 在 HDFS 中会存储份, 内存一份, 磁盘一份(镜像).

/test/a.log 3 {blk_1, blk_2} [{blk_1: [h0, h1, h3]}, {blk_2: [h0, h2, h4]}]
文件名 副本数 文件被分多少块 每个块分别被保存在哪个 DataNode 上

HDFS 通过 CRC 校验块的完整性.

Namenode 使用事务日志记录 HDFS 元数据的变化, 使用映像文件存储 MetaData.

事务日志和映像文件保存在 /tmp/dfs/name/current 目录下:

  • fsimage: 元数据镜像文件, 存储某一时候 NameNode 的内存元数据信息
  • edits: 操作日志文件
  • fstime: 保存最近一次 checkpoint 的时间 (还原点)

当有操作发生时, NameNode 首先向写操作日志到磁盘, 即向 edits 文件中写日志, 成功返回后, 才会修改内存, 并且向客户端返回.

Hadoop 会维护一个 fsimage 文件, 也就是 NameNode 中 MetaData 的硬盘镜像, 但是 fsimage 不会随时与 NameNode 内存中的 MetaData 保持一致, 而是每隔一段时间通过合并 edits 文件来更新内容 (仅限 1.0 和 伪分布式, 2.0 集群是实时的), edits 合并后就会进行一次清空. Secondary NameNode 就是用来合并 fsimageedits 文件来更新 NameNode 的 MetaData 的.

/tmp/dfs/name/current 目录下, 除了那三个文件外, 还有个 VERSION 文件:

martin@smallcpp01:/usr/smallcpp/hadoop-2.7.3/tmp/dfs/name/current$ cat VERSION
#Sun Oct 16 17:28:41 CST 2016
namespaceID=1526300018
clusterID=CID-cc6f47f9-4021-4f9b-87a6-ad7ad82b7af7
cTime=0
storageType=NAME_NODE
blockpoolID=BP-1803451175-192.168.142.155-1476610121875
layoutVersion=-63


这里的数据是用来标识集群、节点唯一性的.

Secondary NameNode

Secondary NameNode 的作用主要体现在 Hadoop 1.x 下, Secondary NameNode 会按照配置的时间及大小定期合并 NameNode 节点上的 fsimage 和 edits, 这样做的目的包含三方面:

  • edits 中可能包含一些文件的创建及删除的操作, 合并之后, 删除的文件就不存在了, 只保存了存在的文件, 删除的文件也就不会合并到 fsimage 中, 这样会节省空间
  • NameNode 在每次重启时都会将 fsimage 和 edits 合并, 如果不在 Secondary NameNode 中定期合并, 在重启 NameNode 时会非常慢
  • 提供冷备份的功能

在 Hadoop 2.X 中, 提供了 HA 解决方案 (高可用), 彻底解决了 NameNode 单点的问题, 也就不存在 Secondary NameNode, 引入了 Standby NameNode,可以顺利的将 Active NameNode 进行热备.

工作模式:

从 NameNode 下载元数据信息 (fsimage, edits), 然后合并二者, 生成新的 fsimage, 在本地保存一份后推送到 NameNode, 替换旧的 fsimage.

什么时候同步?

  • fs.checkpoint.period 指定两次 checkpoint 的最大时间间隔, 默认 3600 秒
  • fs.checkpoint.size 规定 edits 的大小, 默认 64M

DataNode

真实文件数据存储服务

文件块(block): 最基本存储单位, hp 1.0 默认 64M, hp 2.0 默认 128M, 从文件的 0 编号开始, 按指定大小, 顺序对文件进行划分并编号.

HDFS 中, 如果一个文件小于一个数据块的大小 , 并不占用整个数据块 (这些文件最终被保存在 tmp/dfs/data/current/…/finalized/ 中).

默认 3 个副本 (如果已存在一份存储好的文件(3个副本), 突然有一个小弟挂掉了, NameNode 会再安排另一个小弟重新备份这个文件, 这是基于心跳包机制的).

读取数据流程 (以 block 为单位)

  • 客户端要访问 HDFS 中的一个文件
  • 首先从 NameNode 获得组成返个文件的数据块位置列表
  • 根据列表知道存储数据块的 DataNode
  • 访问 DataNode 获取数据
  • NameNode 并不参不数据实际传输

写入数据流程 (以 block 为单位)

HDFS client 上传数据到 HDFS 时, 会首先在本地缓存数据, 当数据达到一个 block 大小时, 请求 NameNode 分配一个 block. NameNode 就会从当前集群寻找一个空闲 block, 然后把 block 所在的 DataNode 的信息告诉 HDFS client. HDFS client 拿到信息后直接和 DataNode 通信, 把数据写到 DataNode 节点的一个 block 文件中 (在传输过程中, 以 Packet 为最小单位).

由于 Hadoop 有副本机制, 例如三个, 所以 NameNode 会寻找三个空闲 block 组成 pipeline, 依次将目标数据块写入各个 DataNode, 建立多个副本, 待所以副本数据都上传完毕后, HDFS client 会继续下一个 block 的操作.