Hadoop系列-HDFS工作流程和原理详解(九)

一、HDFS 数据读写流程

HDFS 是 Hadoop 生态里面的数据存储层,它是一个具有容错性的非常可靠的分布式文件系统。HDFS 以主从( Master / Slave )架构的方式工作,Namenode 是 Master 节点上的守护进程,而 Datanode 是 Slave 节点上的守护进程。

本节将详细介绍 HDFS 数据读写操作工作原理。

1、Hadoop HDFS 数据写操作

要把文件写入到 HDFS,客户端需要先和 Namenode(master)节点交互,Namenode 给客户端提供 Datanode(slave)节点的具体地址之后,客户端才能把数据写入到 Datanode。那客户端怎么把数据写入到 Datanode 呢?其实,客户端是直接跟 Datanode 通信并写入数据的,Datanode 会创建数据写入管道(pipeline),第一个 Datanode 把数据块复制到另一个 Datanode(第二个 Datanode ),之后第二个 Datanode 再把数据块复制到第三个 Datanode。数据块副本创建完成之后,Datanode 会给前一个发送确认消息。

HDFS 数据写入工作流程

前面介绍的数据写入流程是分布式写入的,客户端把数据并发写到很多 Datanode节点。

下面一步一步详细介绍数据的写入流程:

1、客户端调用 DistributedFileSystem 的 create() 方法,开始创建新文件。DistributedFileSystem 创建 DFSOutputStream,产生一个 RPC 调用,让 NameNode 在文件系统的命名空间中创建这一新文件。

2、NameNode 接收到用户的写文件的RPC请求后,首先要执行各种检查,如客户是否有相关的创佳权限和该文件是否已存在等,检查通过后才会创建一个新文件,并将操作记录到编辑日志,然后 DistributedFileSystem 会将 DFSOutputStream 对象包装在 FSDataOutStream 实例中,返回客户端;否则文件创建失败并且给客户端抛 IOException 。

3、客户端开始写文件,DFSOutputStream 会将文件分割成 packets 数据包,HDFS 中每个 block 默认情况下是128M,由于每个块比较大,所以在写数据的过程中是把数据块拆分成一个个的数据包( packet )以管道的形式发送的。然后将这些 packets 写到其内部的一个叫做 data queue(数据队列)。data queue 会向 NameNode 节点请求适合存储数据副本的DataNode 节点的列表,然后这些 DataNode 之前生成一个 Pipeline 数据流管道,我们假设副本因子参数为3,那么这个数据流管道中就有三个 DataNode 节点。

4、首先 DFSOutputStream 会将 packets 向 Pipeline 数据流管道中的第一个 DataNod e节点写数据,第一个 DataNode 接收 packets 然后把 packets 写向 Pipeline 中的第二个节点,同理,第二个节点保存接收到的数据然后将数据写向 Pipeline 中的第三个 DataNode 节点。

5、DFSOutputStream 内部同样维护另外一个内部的写数据确认队列——ack queue。当 Pipeline 中的第三个 DataNode 节点将 packets 成功保存后,该节点回向第二个 DataNode 返回一个确认数据写成功的信息,第二个 DataNode 接收到该确认信息后在当前节点数据写成功后也会向 Pipeline 中第一个 DataNode 节点发送一个确认数据写成功的信息,然后第一个节点在收到该信息后如果该节点的数据也写成功后,会将 packets 从 ack queue 中将数据删除。

在写数据的过程中,如果 Pipeline 数据流管道中的一个 DataNode 节点写失败了会发生什问题、需要做哪些内部处理呢?如果这种情况发生,那么就会执行一些操作:

首先,Pipeline 数据流管道会被关闭,ack queue 中的 packets 会被添加到 data queue 的前面以确保不会发生 packets 数据包的丢失。

接着,在正常的 DataNode 节点上的已保存好的 block 的 ID 版本会升级——这样发生故障的 DataNode 节点上的 block 数据会在节点恢复正常后被删除,失效节点也会被从 Pipeline 中删除。

最后,剩下的数据会被写入到 Pipeline 数据流管道中的其他两个节点中。

如果 Pipeline 中的多个节点在写数据是发生失败,那么只要写成功的 block 的数量达到 dfs.replication.min(默认为1) ,那么就任务是写成功的,然后 NameNode 后通过一步的方式将 block 复制到其他节点,直到数据副本达到 dfs.replication 参数配置的个数。

6、完成写操作后,客户端调用 close() 关闭写操作IO流,刷新数据。

7、在数据刷新完后 NameNode 关闭写操作流。到此,整个写操作完成。

2、Hadoop HDFS 数据读操作

为了从 HDFS 读取数据,客户端首先得和 Namenode(master节点)交互,因为 Namenode 是 Hadoop集群的中心,它保存着集群的所有元数据。然后 Namenode 会检查客户端是否有相应权限,如果客户端有权限读取数据,那么 Namenode 才会给它返回存储文件的 Datanode ( Slave ) 节点列表。之后客户端将直接从 Datanode 读取相应的数据 block。

HDFS 文件读取流程

下面详细介绍 HDFS 读文件的流程:

1、客户端调用 FileSystem 对象的 open()函数,打开想要读取的文件。其中FileSystem 是 DistributeFileSystem 的一个实例。

2、DistributedFileSystem 使用 RPC 调用 Namenode 来确定文件中前几个块的位置。对于每个块,Namenode 会返回存储该数据块副本的 Datanode 节点的地址,并根据 Datanode 与客户端的距离排序。

3、DistributedFileSystem 会给客户端返回一个输入流对象 FSDataInputStream,客户端将从该对象读取数据,该对象分装了一个用于管理 Datanode 和 Namenode IO 的对象 —— DSFInputStream。 客户端调用 FSDataInputStream 的 read() 函数。由于 DFSInputStream 已经存储了 Datanode 地址,那么接下来就是连接到距离客户端最近的 Datanode 进行读取数据文件的第一个块。

4、客户端可以重复的在数量流调用 read() 函数,当块读取结束后,DFSInputStream 将关闭和 datanode 的连接,然后再找下一个离客户端最近的存储数据块的 Datanode 。

5、如果 DFSInputStream 在和 Datanode 通信的时候发生故障,它将会从存储该数据块的距离客户端最近的 Datanode 读取数据。而且它会把发生故障的 Datanode 记录下来,在读取下一个数据块的时候,就不会再从这个 Datanode 读取了。DFSInputStream 会对数据做校验和( checksum )验证。如果发现损坏的块,在 DFSInputStream 从其他 Datanode 读取损坏的块的副本之前,把损坏的块告知给 Namenode。

6、客户端读取数据完成后,调用 close() 函数关闭数据流。

二、HDFS 数据块

要把大文件存储在 HDFS上,HDFS 会把大文件分割成小块,即我们通常说的数据块( block ),它是 Hadoop 分布式文件系统最小的存储单元,而且我们没办法决定指定块的存储节点地址,这些 Namenode 会替我们决定。数据块默认大小是 128MB,比操作系统里面的块概念要大很多(操作系统块大小是 4KB ),我们可以根据实际需求修改 HDFS 块大小。文件的所有数据块大小都是一样的,除了最后一个,它可能小于块大小或者刚好等于块大小。文件会被分割成若干个 128MB 的小数据块,再写入HDFS的。

假如需要把一个 518MB 的文本文件 Example.txt 存储到 HDFS,在块大小默认情况下,HDFS 将会创建 5 个数据块,前面4个数据块大小将是 128MB,最后一个是 6MB,而不是 128MB。这样会节省不少存储空间。

HDFS 块大小为什么默认是 128MB

HDFS 存储的数据集一般比较大,数据量级一般是 TB 级别或者 PB 级别的。如果像 Linux 系统那样每个块只有 4KB。那么 HDFS 将会存储非常多的数据块,这将导致元数据暴增,NameNode 管理维护这些元数据将非常吃力。且很快会成为集群性能的瓶颈。另一方面,数据块的大小不能太大,不然文件系统处理数据延迟会更加严重。

HDFS 数据块的优势

以下是 HDFS 数据块的优势:

方便管理

由于数据块的固定的,磁盘能够存储多少数据块很容易就可以计算出来。

存储大文件

HDFS 可以存储比单个磁盘容量还大的数据文件,因为文件会被划分成多个 HDFS 数据块,并存储在集群的多个Datanode 磁盘上。

容错性和高可用

数据块很容易在 Datanode 之间复制,以便达到数据的容错性和高可用性。

简单的 Datanode 存储机制

HDFS 数据块的概念简化了 Datanode 的数据存储方式。所有块的元数据都是在 Namenode 维护的。Datanode 不需要关心块的元数据,比如文件权限,存储位置等。

三、HDFS 机架感知

客户端向 Namenode 发送写请求时,Namenode 为这些数据分配 Datanode 地址,HDFS 数据块副本的放置对于系统整体的可靠性和性能有关键性影响。一个简单但非优化的副本放置策略是,把副本分别放在不同机架,甚至不同IDC,这样可以防止整个机架,甚至整个IDC崩溃带来的数据丢失,但是这样文件写必须在多个机架之间、甚至IDC之间传输,增加了副本写的代价,是否有较优的方案来解决这个问题呢?

机架感知是什么

告诉 Hadoop 集群中哪台机器属于哪个机架。

那通过什么方式告知呢?答案是人工配置。因为Hadoop 对机架的感知并非是自适应的,亦即,hadoop 集群分辨某台 slave 机器是属于哪个 rack 并非是智能感知的,而是需要 hadoop 的管理者人为的告知 hadoop 哪台机器属于哪个 rack,这样在 hadoop 的 namenode 启动初始化时,会将这些机器与 rack 的对应信息保存在内存中,用来作为对接下来所有的 HDFS 的写块操作分配 datanode 列表时(比如 3 个 block 对应三台 datanode)的选择 datanode 策略,尽量将三个副本分布到不同的 rack。

机架感知使用场景

在 Hadoop 集群规模很大的情况下,就需要用到机架感知。

机架感知需要考虑的情况

  • 不同节点之间的通信能够尽量发生在同一个机架之内,而不是跨机架。
  • 为了提高容错能力,名称节点会尽可能把数据块的副本放到多个机架上。

机架感知配置步骤

默认情况下,hadoop 的机架感知是没有被启用的。所以,在通常情况下,hadoop 集群的 HDFS 在选机器的时候,是随机选择的,也就是说,很有可能在写数据时,hadoop 将第一块数据 block1 写到了 rack1 上,然后随机的选择下将 block2 写入到了 rack2 下,此时两个 rack 之间产生了数据传输的流量,再接下来,在随机的情况下,又将 block3 重新又写回了 rack1,此时,两个 rack 之间又产生了一次数据流量。在 job 处理的数据量非常的大,或者往 hadoop 推送的数据量非常大的时候,这种情况会造成 rack 之间的网络流量成倍的上升,成为性能的瓶颈,进而影响作业的性能以至于整个集群的服务。

要将 hadoop 机架感知的功能启用,配置非常简单,在 namenode 所在机器的 hadoop-site.xml 配置文件中配置一个选项:

<property>
<name>topology.script.file.name</name>
<value>/path/to/RackAware.py</value>
</property>

这个配置选项的 value 指定为一个可执行程序,通常为一个脚本,该脚本接受一个参数,输出一个值。接受的参数通常为某台 datanode 机器的 ip 地址,而输出的值通常为该 ip 地址对应的 datanode 所在的 rack,例如”/rack1”。Namenode 启动时,会判断该配置选项是否为空,如果非空,则表示已经用机架感知的配置,此时 namenode 会根据配置寻找该脚本,并在接收到每一个 datanode 的心跳时,将该 datanode 的 ip 地址作为参数传给该脚本运行,并将得到的输出作为该 datanode 所属的机架,保存到内存的一个 map 中。

至于脚本的编写就需要将真实的网络拓朴和机架信息了解清楚后,通过该脚本能够将机器的 ip 地址正确的映射到相应的机架上去。可以参考Hadoop官方给出的脚本

官方给的是利用 shell 写的,下面是用 Python 代码来实现的:

#!/usr/bin/python
#-*-coding:UTF-8 -*-

import sys

rack = {
"hadoopnode-176.tj":"rack1",
"hadoopnode-178.tj":"rack1",
"hadoopnode-179.tj":"rack1",
"hadoopnode-180.tj":"rack1",
"hadoopnode-186.tj":"rack2",
"hadoopnode-187.tj":"rack2",
"hadoopnode-188.tj":"rack2",
"hadoopnode-190.tj":"rack2",
"192.168.1.15":"rack1",
"192.168.1.17":"rack1",
"192.168.1.18":"rack1",
"192.168.1.19":"rack1",
"192.168.1.25":"rack2",
"192.168.1.26":"rack2",
"192.168.1.27":"rack2",
"192.168.1.29":"rack2",
}

if __name__=="__main__":
print "/" + rack.get(sys.argv[1],"rack0")

由于没有确切的文档说明到底是主机名还是 ip 地址会被传入到脚本,所以在脚本中最好兼容主机名和 ip 地址,如果机房架构比较复杂的话,脚本可以返回如:/dc1/rack1 类似的字符串。

执行命令:

chmod +x RackAware.py

重启 namenode,如果配置成功,namenode 启动日志中会输出:
INFO org.apache.hadoop.net.NetworkTopology: Adding a new node: /rack1/192.168.1.15:50010

网络拓扑机器之间的距离


这里基于一个网络拓扑案例,介绍在复杂的网络拓扑中 hadoop 集群每台机器之间的距离。有了机架感知,NameNode 就可以画出上图所示的 datanode 网络拓扑图了。

D1,R1 都是交换机,最底层是 datanode。则 H1 的 rackid=/D1/R1/H1,H1 的 parent 是 R1,R1 的是 D1。这些 rackid信息可以通过 topology.script.file.name 配置。有了这些 rackid 信息就可以计算出任意两台 datanode 之间的距离。

  1. distance(/D1/R1/H1,/D1/R1/H1)=0 相同的 datanode
  2. distance(/D1/R1/H1,/D1/R1/H2)=2 同一 rack 下的不同 datanode
  3. distance(/D1/R1/H1,/D1/R1/H4)=4 同一 IDC(互联网数据中心、机房)下的不同 datanode
  4. distance(/D1/R1/H1,/D2/R3/H7)=6 不同 IDC 下的 datanode 

四、HDFS Federation(联邦机制)

HDFS Federation 中文意思为 HDFS 联盟或者 HDFS 联邦。这里并非指多个集群,更准确的应该是一个集群有多个命名空间,即多个 Namenode。

Hadoop 1.0 HDFS 架构

Hadoop HDFS 有两个主要的分层:HDFS包含两个层次:命名空间管理(Namespace) 和 块/存储管理(Block Storage)。

  • 命名空间管理(Namespace)
    HDFS的命名空间包含目录、文件和块。命名空间管理是指命名空间支持对HDFS中的目录、文件和块做类 似文件系统的创建、修改、删除、列表文件和目录等基本操作。

  • 块/存储管理(Block Storage)
    在块存储服务中包含两部分工作:块管理和物理存储。这是一个更通用的存储服务。其他的应用可以直接建立在Block Storage上,如HBase,Foreign Namespaces等。

    • 块管理
      • 处理 DataNode 向 NameNode 注册的请求,处理 Datanode 的成员关系,接受来自 DataNode 周期性的心跳。
      • 处理来自块的报告信息,维护块的位置信息。
      • 处理与块相关的操作:块的创建、删除、修改及获取块信息。
      • 管理副本放置(replica placement)和块的复制及多余块的删除。
    • 物理存储
      所谓物理存储就是:DataNode 把块存储到本地文件系统中,对本地文件系统的读、写。

Hadoop 1.0 HDFS 架构的局限性

Hadoop 1.0 HDFS 架构只允许整个集群中存在一个 namespace,而该 namespace 被仅有的一个 namenode 管理。这个架构使得 HDFS 非常容易实现,但是,它在具体实现过程中耦合度比较高,进而导致了很多局限性。

HDFS的局限性主要为:

  • 块存储和 namespace 高耦合
    当前 namenode 中的 namespace 和 block management 的结合使得这两层架构耦合在一起,难以让其他可能namenode 实现方案直接使用 block storage。

  • namenode 扩展性
    HDFS 的底层存储,即 Datanode 节点是可以水平扩展的,但 namespace 不可以。当前的 namespace 只能存放在单个namenode 上,而 namenode 在内存中存储了整个分布式文件系统中的元数据信息,这限制了集群中数据块,文件和目录的数目。

  • 性能
    文件操作的性能制约于单个 namenode 的吞吐量,单个 namenode 当前仅支持约 60,000 个并发 task,而下一代Apache MapReduce 将支持超过 1,00,000 个并发任务,这意味着将需要更多的 Namenode 。

  • 隔离性
    现在大部分公司的集群都是共享的,每天有来自不同部门的不同用户提交作业。单个 namenode 难以提供隔离性,即:某个用户提交的负载很大的 job 会减慢其他用户的 job ,单一的 namenode 难以像 HBase 按照应用类别将不同作业分派到不同 namenode 上。

需要注意的,HDFS Federation 并不能解决单点故障问题,也就是说,每个 NameNode 都存在在单点故障问题,你需要为每个 namenode 部署一个备用 namenode 以应对 NameNode 挂掉对业务产生的影响。

HDFS Federation 架构

为了解决上述第二节提到的问题,Hadoop 2.0引入了基于共享存储的高可用解决方案和 HDFS Federation。

为了水平扩展 namenode,federation 使用了多个独立的 namenode / namespace。这些 namenode 之间是联合的,也就是说,他们之间相互独立且不需要互相协调,各自分工,管理自己的区域。分布式的 datanode 被用作通用的数据块存储存储设备。每个 datanode 要向集群中所有的 namenode 注册,且周期性地向所有 namenode 发送心跳和块报告,并执行来自所有 namenode 的命令。

一个 block pool 由属于同一个 namespace 的数据块组成,每个 datanode 可能会存储集群中所有 block pool 的数据块。

每个 block pool 内部自治,也就是说各自管理各自的 block,不会与其他 block pool 交互。一个 namenode 挂掉了,不会影响其他 namenode。

某个 namenode 上的 namespace 和它对应的 block pool 一起被称为 namespace volume(命名空间卷)。它是管理的基本单位。当一个 namenode / nodespace 被删除后,其所有 datanode 上对应的 block pool 也会被删除。当集群升级时,每个 namespace volume 作为一个基本单元进行升级。

命名空间管理

Federation 中存在多个命名空间,如何划分和管理这些命名空间非常关键。在 Federation 中并没有采用“文件名 hash ”的方法,因为该方法的本地性非常差,比如:查看某个目录下面的文件,如果采用文件名 hash 的方法存放文件,则这些文件可能被放到不同 namespace 中,HDFS 需要访问所有 namespace,代价过大。为了方便管理多个命名空间,HDFS Federation 采用了经典的 Client Side Mount Table。

如上图所示,下面四个深色三角形代表一个独立的命名空间,上方浅色的三角形代表从客户角度去访问的子命名空间。各个深色的命名空间 Mount 到浅色的表中,客户可以访问不同的挂载点来访问不同的命名空间,这就如同在 Linux 系统中访问不同挂载点一样。

这就是 HDFS Federation 中命名空间管理的基本原理:将各个命名空间挂载到全局 mount-table 中,就可以做将数据到全局共享;同样的命名空间挂载到个人的 mount-table 中,这就成为应用程序可见的命名空间视图。

Block pool(块池)

所谓 Block pool (块池)就是属于单个命名空间的一组 block(块)。每一个 datanode 为所有的 block pool 存储块。Datanode 是一个物理概念,而 block pool 是一个重新将 block 划分的逻辑概念。同一个 datanode 中可以存着属于多个 block pool 的多个块。Block pool 允许一个命名空间在不通知其他命名空间的情况下为一个新的 block 创建 Block ID。同时,一个 Namenode 失效不会影响其下的 datanode 为其他 Namenode 的服务。

当 datanode 与 Namenode 建立联系并开始会话后自动建立 Block pool。每个 block 都有一个唯一的标识,这个标识我们称之为扩展的块 ID( Extended Block ID )= BlockID+BlockID。这个扩展的块 ID 在 HDFS 集群之间都是唯一的,这为以后集群归并创造了条件。

Datanode 中的数据结构都通过块池 ID(Block Pool ID)索引,即 datanode 中的 BlockMap,storage 等都通过BPID 索引。

在 HDFS 中,所有的更新、回滚都是以 Namenode 和 BlockPool 为单元发生的。即同一 HDFS Federation 中不同的 Namenode / BlockPool 之间没有什么关系。

五、HDFS 磁盘均衡

HDFS 磁盘均衡器

HDFS 提供了一个用于 Datanode 内多磁盘之间的数据均衡工具,即 Diskbalancer (磁盘均衡器),它把数据均衡的分发到一个 Datanode 下的多个磁盘。Diskbalancer 和 Hadoop 2.0 版本以前提供的 Balancer 不同,因为 Balancer 关心的是不同 Datanode 之间的数据均衡,Datanode 内多个磁盘的数据均衡它是不起作用的。

HDFS 由于以下原因,在把数据存储到 Datanode 多个磁盘的时候,会出现磁盘之间数据不均衡的情况:

  • 大量的数据写入和删除
  • 磁盘更换

上面这两点可能导致数据在 Datanode 内的多个磁盘发生明显倾斜。这种情况现有的 HDFS balancer 均衡工具没办法处理,上面说了,它只关心 Datanode 之间的数据均衡,所以,Hadoop 3.0 提供了 Diskbalancer 工具,用于均衡一个Datanode 内多个磁盘之间的数据均衡。

Diskbalancer

Hadoop HDFS balancer 工具通过创建一个计划(命令集)并在 Datanode 执行该计划来工作。这里的计划主要描述的是有多少数据需要在磁盘之间做迁移。一个计划有很多迁移步骤,比如,源磁盘,目标磁盘和需要迁移的字节数。计划可以针对某一个 Datanode 执行特定操作。默认情况下,Diskbalancer 是未启用状态,您可以在 hdfs-site.xml 配置文件把 dfs.disk.balancer.enabled 设置为 true 来启用它。

当我们往 HDFS 上写入新的数据块,DataNode 将会使用 volume 选择策略来为这个块选择存储的地方。目前 Hadoop 支持两种 volume 选择策略:round-robin(循环策略) 和 available space(可用空间策略),选择哪种策略我们可以通过下面的参数来设置。

dfs.datanode.fsdataset.volume.choosing.policy

  • 循环策略:将新块均匀分布在可用磁盘上。
  • 可用空间策略:它是优先将数据写入具有最大可用空间的磁盘(通过百分比计算的)。

默认情况下,DataNode 是使用基于 round-robin 策略来写入新的数据块。然而在一个长时间运行的集群中,由于 HDFS 中的大规模文件删除或者通过往 DataNode 中添加新的磁盘,仍然会导致同一个 DataNode 中的不同磁盘存储的数据很不均衡。即使你使用的是基于可用空间的策略,卷(volume)不平衡仍可导致较低效率的磁盘I/O。比如所有新增的数据块都会往新增的磁盘上写,在此期间,其他的磁盘会处于空闲状态,这样新的磁盘将会是整个系统的瓶颈。

Apache Hadoop 社区之前开发了几个离线脚本来解决磁盘不均衡的问题。然而,这些脚本都是在HDFS代码库之外,在执行这些脚本往不同磁盘之间移动数据的时候,需要要求 DataNode 处于关闭状态。结果,HDFS-1312 还引入了一个在线磁盘均衡器,旨在根据各种指标重新平衡正在运行 DataNode 上的磁盘数据。和现有的 HDFS 均衡器类似,HDFS 磁盘均衡器在 DataNode 中以线程的形式运行,并在相同存储类型的卷(volumes)之间移动数据。注意,本文介绍的HDFS 磁盘均衡器是在同一个 DataNode 中的不同磁盘之间移动数据,而之前的 HDFS 均衡器是在不同的 DataNode 之间移动数据。

Diskbalancer 的使用

我们通过一个例子逐步探讨如何使用 Diskbalancer。

首先,确保所有 DataNode 上的 dfs.disk.balancer.enabled 参数设置成 true。本例子中,我们的 DataNode 已经挂载了一个磁盘(/mnt/disk1),现在我们往这个DataNode 上挂载新的磁盘(/mnt/disk2),我们使用 df命令来显示磁盘的使用率:

$ df -h
….
/var/disk1 5.8G 3.6G 1.9G 66% /mnt/disk1
/var/disk2 5.8G 13M 5.5G 1% /mnt/disk2

从上面的输出可以看出,两个磁盘的使用率很不均衡,所以我们来将这两个磁盘的数据均衡一下。典型的磁盘平衡器任务涉及三个步骤(通过 HDFS 的 diskbalancer 命令):planexecute 和 query

第一步,HDFS 客户端从 NameNode 上读取指定 DataNode 的必要信息以生成执行计划:

$ hdfs diskbalancer -plan lei-dn-3.example.org
16/08/19 18:04:01 INFO planner.GreedyPlanner: Starting plan for Node : lei-dn-3.example.org:20001
16/08/19 18:04:01 INFO planner.GreedyPlanner: Disk Volume set 03922eb1-63af-4a16-bafe-fde772aee2fa Type : DISK plan completed.
16/08/19 18:04:01 INFO planner.GreedyPlanner: Compute Plan for Node : lei-dn-3.example.org:20001 took 5 ms
16/08/19 18:04:01 INFO command.Command: Writing plan to : /system/diskbalancer/2016-Aug-19-18-04-01

从上面的输出可以看出,HDFS 磁盘平衡器通过使用 DataNode 报告给 NameNode 的磁盘使用信息,并结合计划程序来计算指定 DataNode 上数据移动计划的步骤,每个步骤指定要移动数据的源卷和目标卷,以及预计移动的数据量。

截止到撰写本文的时候,HDFS仅仅支持 GreedyPlanner,其不断地将数据从最常用的设备移动到最少使用的设备,直到所有数据均匀地分布在所有设备上。用户还可以在使用 plan 命令的时候指定空间利用阀值,也就是说,如果空间利用率的差异低于此阀值,planner 则认为此磁盘已经达到了平衡。当然,我们还可以通过使用 —bandwidth 参数来限制磁盘数据移动时的I/O。

磁盘平衡执行计划生成的文件内容格式是 Json 的,并且存储在 HDFS 之上。在默认情况下,这些文件是存储在 /system/diskbalancer 目录下面:

$ hdfs dfs -ls /system/diskbalancer/2016-Aug-19-18-04-01
Found 2 items
-rw-r--r-- 3 hdfs supergroup 1955 2016-08-19 18:04 /system/diskbalancer/2016-Aug-19-18-04-01/lei-dn-3.example.org.before.json
-rw-r--r-- 3 hdfs supergroup 908 2016-08-19 18:04 /system/diskbalancer/2016-Aug-19-18-04-01/lei-dn-3.example.org.plan.json

可以通过下面的命令在 DataNode 上执行这个生成的计划:

$ hdfs diskbalancer -execute /system/diskbalancer/2016-Aug-17-17-03-56/172.26.10.16.plan.json
16/08/17 17:22:08 INFO command.Command: Executing "execute plan" command

这个命令将 JSON 里面的计划提交给 DataNode,而 DataNode 会启动一个名为 BlockMover 的线程中执行这个计划。我们可以使用 query 命令来查询 DataNode 上diskbalancer 任务的状态:

$ hdfs diskbalancer -query lei-dn-3:20001
16/08/19 21:08:04 INFO command.Command: Executing "query plan" command.
Plan File: /system/diskbalancer/2016-Aug-19-18-04-01/lei-dn-3.example.org.plan.json
Plan ID: ff735b410579b2bbe15352a14bf001396f22344f7ed5fe24481ac133ce6de65fe5d721e223b08a861245be033a82469d2ce943aac84d9a111b542e6c63b40e75
Result: PLAN_DONE

上面结果输出的 PLAN_DONE 表示 disk-balancing task 已经执行完成。为了验证磁盘平衡器的有效性,我们可以使用 df -h 命令来查看各个磁盘的空间使用率:

$ df -h
Filesystem Size Used Avail Use% Mounted on
….
/var/disk1 5.8G 2.1G 3.5G 37% /mnt/disk1
/var/disk2 5.8G 1.6G 4.0G 29% /mnt/disk2

上面的结果证明,磁盘平衡器成功地将 /var/disk1 和 /var/disk2 空间使用率的差异降低到 10% 以下,说明任务完成!


参考文章: HDFS 数据读写流程 | HDFS 教程

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值