一、数据与元数据的区别
以块形式存储是目前最常用的一种数据存储方式,我们在进行数据存储时,使用的是元数据加数据的形式,元数据相当于是数据的一个摘要信息,保存着文件的属性、长度、存储位置、类型等信息,类似于字典中的索引和正文的关系,数据块作为文件存储的最小的单位,对存储区域进行了区域划分,在写入数据时按需分配。
我们可以按照字典的方式进行类比:文件系统就相当于是字典,元数据相当于索引目录,数据相当于是正文,我们查字典就和查找数据一样,首先需要访问文件系统,然后根据元数据找到对应的数据位置和相关的属性信息,最后根据元数据的描述找到数据。
二、HDFS 介绍
HDFS(Hadoop Distribution File System)是运行在通用硬件(所谓通用硬件就是指软件对于底层的硬件平台的配置和设备没有需求,可以随意搭建并且兼容, 对于 HDFS 文件系统包括整体的 Hadoop 组件来说,这样做就可以达到完美的拓展性,由于本身设备对于硬件没有要求之后,那么我们就可以按照无限堆积硬件的 方式进行性能的拓展,直到满足大数据处理系统的需求,这样做可以减小在实际操作中的成本,并且可以提供更好的容错性,如果我们对于设备的型号或者性能有需求那么不免我们会在搭建时使用相同的设备来进行操作,这样的话,如果某一厂商的设备存在有 BUG,那么就会在一批设备上出现相同的问题,造成整体性能的下降或者系统的安全性危机)上的分布式文件系统,它具有高容错性,高吞吐量并且支持大文件存储。
HDFS 支持的主要是大文件的流数据,对于离散的小文件的支持性较弱,尤其是对延迟比较敏感的应用,由于 HDFS 要支持高吞吐量,所以势必要以牺牲延迟作为代价。
注:由于 HDFS 的文件系统是需要将元数据加载在内存中进行维护的,我们又将该维护进程称之为 Namenode,系统需要为每一个数据文件维护约 150 字节的元数据,所以存储小文件和大文件都是消耗同样的元数据空间,这样的话,在支持性上,小文件过多就会影响最终数据的存储容量,对于相同的元数据空间,我们所能存储的单位数据越大,那么大数据文件系统的支持性也就越强,所以 HDFS 在相同的文件数目下,存储大文件和小文件的开销相同,那么存储大文件就更加合理。并且作为大数据主要使用的文件系统,HDFS 主要提供的是文件的读操作,所以它整个分布式进程中只有一个写进程,其他的进程全部都是读进程,并且该写进程是在所有进程的最末尾的。设计者针对于大数据操作系统的处理特点,为了保护数据的一致性和读写的性能,就提出了 WORM 模型作为 HDFS 整体的系统设计目标,WORM-write once read many,WORM 的最开始是使用在存储系统中,用于对关键数据进行保护的,比如政府,法院的判决文件等,这些文件可以允许进行读取,但是由于需要保护其不受篡改所以就需要 WORM 来进行保证,当一个文件写入到文件系统中之后,在更改期限内,我们还可以进行改写操作,当进入到保护周期之后,就只能进行读取,无法进行写操作了,这里说的写操作是任何写都不能执行。当文件大小为 0 字节,也就是指该文件为空文件时,那么文件在保护期内还有一次追加写的机会。这是在存储中的 WORM 的特点,在 HDFS 中,由于我们的设计目标并不是为了防止文件的篡改,而是为了保证高效率的读取,所以我们并没有将 WORM 设计的很严格。我们写入的文件是不再允许修改的,但是我们可以在文件末尾进行无限的追加写操作。
三、HDFS 架构
在大数据的组件架构中,HDFS 提供的是整个结构最底层的文件存储功能,他组织了文件形式,将数据切分为数据块存储起来,并且记载和维护元数据。
HDFS 分为三个组件:Namenode,Datanode,Client
(1) Namenode:用于存储生成元数据,运行一个实例。该进程是由 HDFS 调入到内存中运行的。NameNode 作为元数据的维护进程,为了能够提升整体读取的效率,所以我们将元数据的维护进程搭载在内存中进行运行,但是内存中的数据是易失的,所以平时元数据还是在 DataNode 中进行维护的。当系统启动之后, 服务器会拉起 HDFS 进程,然后将 NameNode 加载到内存中,然后 NameNode 会加载元数据镜像文件到自身内存中。
(2) Datanode:用于存储实际的数据,每个 Datanode 会将自己维护的数据块信息上报到 Namenode,运行多个实例。HDFS 默认最小的存储空间为 block,每个 block 默认的大小为 128MB。DataNode 除了需要维护数据之外,还需要留有一部分的空间用于存储元数据镜像文件
Fsimage 。如果 NameNode 和 DataNode 是部署在一起的,那么
Fsimage 就在 DataNode 上,其实相当于是在服务器的存储介质上。如果 NameNode 和 DataNode 是分开部署的,那么就相当于 Fsimage
是存储在部署 NameNode 的服务器上的。如下图所示:
(3) Client:支持业务访问 HDFS,并从 Namenode 和 Datanode 中获取数据, 返回给用户,多个业务和实例一起运行。这里所说的 Client 并不是指实际的用户应用,而是 HDFS 本身自带的进程,通过该进程我们可以访问 HDFS,相当于 HDFS 是一间房,Client 为我们提供了进入的门,Client 提供的接口主要有 JDBC 和 ODBC 接口。
四、HDFS_HA
HDFS 为了达成高可用性进群,设计了以下几个组件来保证可靠性。由于 HDFS 认为硬件总是不可靠的,所以为了保证自身业务的正常执行和数据的安全性,那么就需要有对应的保护机制来保证整体业务的运行。在 HA 中,我们提供的主要是进程的安全性保障。
(1) zookeeper:分布式协调进程,用来存储 HA 下的状态文件,主备信息, zk 建议为三个或三个以上的奇数个。Zookeeper 进程主要提供的是对 NameNode 进程的保护,这里所谓的保护其实是用于裁决 NameNode 的主备状态,并且存储 NameNode 的状态信息。
(2) Namenode 主备:在主备模式中,主节点提供服务,备节点合并元数据并作为主节点的热备。NameNode 为了能够保护自身的可靠性,维护元数据和业务的持续运行,这里设计了两个进程来进行保护,一个进程用于正常提供业务,另一 个进程作为备进程,但是备进程并不是冷备,而是处于热备状态,一旦进程出现 故障,那么备进程可以立即受到消息,然后切换状态,由于备进程本身在进行元 数据持久化的过程中存储了 Fsimage 元数据镜像文件,所以切换的时候延迟很小。在切换时,主要涉及到了两个文件的操作,一个是 Editlog,一个是 Fsimage,Editlog 记录的是用户对于元数据的修改操作,Fsimage 是元数据的镜像。
(3) ZKFC(zookeeper Failover Controller):用于控制在故障时 Namenode
的主备状态。进行故障切换。该进程的作用是为了保障当主 NameNode 出现故障的时候可以及时的进行故障切换,将业务切换到备 NameNode 中进行运行,保障业务的连续性,所以 ZKFC 需要及时检测主备 NameNode 的状态,并且将心跳信息及时上报给 Zookeeper,所以 ZKFC 进程和
NameNode 进程一样多,并且需要和 NameNode 部署在一起。ZKFC 进程主要的两个工作就是获取 NameNode 上报的心跳, 并且进行故障切换这两个操作。首先,ZKFC 进程并不属于 Zookeeper,而是属于 HDFS 的进程, ZKFC 进程和 NameNode 进程强耦合,所以两个进程需要部署在一起,
NameNode 上报心跳给 Zookeeper 的时候就是通过 ZKFC 来进行发送的,所以我们可以理解 ZKFC 是一个通道或者是一个发送的接口进程。当
Zookeeper 没有收到心跳时,就会下发对应的 failover 操作给 ZKFC,
那么这个时候 ZKFC 就负责控制备 NameNode 接管业务。
(4) JN(JournalNode):用于共享 Namenode 生成的 Editlog 文件。Editlog
文件是我们对 HDFS 的操作的日志文件,这些信息并未写入到 FSimage 的元数据镜像文件中,所以我们需要进行持久化,保障整体的元数据镜像.
在 HDFS 进程在重启的时候可以正常加载。
注:Namenode 在执行由 HDFS 提交的创建文件或者移动文件的请求时,会首先记录 Editlog,然后在更新文件系统镜像(内存中,由于 Editlog 中记录的对于元数据镜像文件的操作是在内存中的,所以当出现故障的时候主 NameNode 的数据就会丢失,如果我们没有及时的进行持久化的操作,那么就会导致元数据的部分丢失,这个时候,我们需要根据 Editlog 中记录的操作来恢复元数据,然后就需要用到 JN 进程来做 Editlog 的同步),内存中的文件系统镜像用于 Namenode 向 Client 提供服务,而 Editlog 用于在发生故障的情况下,进行 Failover 动作时使用。记录在 Editlog 中的每一个操作又称一个事务并且使用事务 ID 来作为编号。所以 JN 进程一共有两个作用,一个是用于进行元数据的持久化操作,另一个就是在主 NameNode 出现故障时同步 Editlog。
五、HDFS 的数据副本机制
由于 HDFS 认为硬件总是不可靠的,所以 HDFS 就需要负责进程和数据的可靠性保证,HA 提供的是进程的可靠性保证,那么数据的可靠性保证就可以通过两种方式进行保护,目前业内主要使用的保护机制有两种,一种是 RAID 技术,主要通过硬件 RAID 卡进行操作保护,但是一旦 RAID 卡损坏,就会导致 RAID 组全部数据失效。而本身 HDFS 认为硬件不可靠,所以数据的保护机制就没有使用 RAID 的保护机制,而是交给自身的软件进行保护,使用的副本机制进行保护。这样的话,当某一个节点出现故障之后,就可以直接使用其他副本的数据,避免了像 RAID 中出现降级重构或者预拷贝而导致的性能影响问题。我们默认会存储三份副本数据,所以和源数据一起,一个数据会被存储 4 份,我们认为 A 数据和 B 数据在一个服务器内的时候,距离为 0,A 数据和 B 数据在同一机架内的时候距离为 2,认为 A 数据和 B 数据不在同一机架内的时候距离为 4.所以存储的距离公式
为:
Distance(R1/D1,R1/D1)=0
Distance(R1/D1,R1/D*)=2
Distance(R1/D1,R*/D*)=4
R:机架编号;D:服务器编号那么副本放置的机制遵循如下公式:副本 1:Distance=0
副本 2:Distance=4
副本 3:通过检测,查看两个副本是否在同一个机架上,如果是,则选择在不同机架上,否则选择在和副本相同机架的不同节点上。
副本 4+:随机选择
第一份副本会存储在和源数据同一位置的服务器上,所以距离为 0,第二份数据随机进行存储,存储在除源数据服务器以外的任意一个位置,第三份副本通过检测,查看两个副本是否在同一个机架上,如果是,则选择在不同机架上,否则选择在和副本相同机架的不同节点上。第四份副本随机选择位置,我们默认是 3 副本机制,也可以设置多副本。副本的位置前三份必须要满足距离 0/2/4 的需求,从第 4 份以及之后的副本,那么就随机选取位置,当然选择的副本数量越多。越安全,但是占用的空间就越大。
六、HDFS 元数据持久化
我们在进行 HDFS 的操作时,数据都是存储在内存中的。这样的话,数据在内存中维护,我们在对操作进行记录的时候,一方面记录了 Editlog,另一方面记录了 Fsimage 文件,但是 Fsimage 文件是在内存中维护的,所以关机之后,当前正在使用的在内存中的 Fsimage 文件就会丢失,那么服务器中存储的 Fsimage 文件就是上一次开机时加载的文件,从时效性上来说,就会很落后。当下一次开机时,我们加载旧的 Fsimage 文件之后,元数据其实是处于不可用的状态的。因为元数据和数据是不一致状态的,这时候如果进行写操作或者读操作,就会读取出错误的文件,或者是无法读取文件。这个时候我们在开机进行加载的时候就需要通过 Editlog 来对元数据进行进一步的恢复性加载。这个时候需要耗费过长的时间来进行。也就影响了整体进程的加载速度。
并且同时为了保证数据的安全性(比如在突然断电的情况下,Editlog 和 Fsimage 就可以出现数据丢失的情况),我们也需要元数据持久化,主要是为了更新 Namenode 中的 Editlog(操作记录日志文件)和 Fsimage(文件系统镜像)两个文件,保证两个文件在主备节点中的同步,最终当出现故障的时候,可以进行 Failover 操作,保证整体大数据平台的可用性。而且,做 Editlog 和 Fsimage 的合并也有利于在进程重启之后,可以尽快的进行元数据的加载操作。那么为了达成这个目的,我们就需要通过元数据持久化这个操作来保证主备之间关于 Editlog 和 Fsimage 这两个文件的可用性和实时性。并且我们需要将 Fsimage 文件进行操作文件和元数据的整合,这个操作也同样保证了 Namenode 中的元数据能够保持一个长可用性。我们默认当时间为 1 小时或者 Editlog 文件大小达到 64M 时,启动一次元数据持久化操作。
持久化流程如下:
(1)首先备节点会下发一个请求到主节点,之后主节点会生成一个临时文件 Editlog.new 文件(一个新文件,不包含原 Editlog 文件中的任何信息)继续使用,并将原先的 Editlog 和 Fsimage 文件发送到备节点中,由于 Editlog 是实时进行修改的,所以在 Editlog 文件在进行写入的时候,就无法进行发送,因为文件处于一个写入状态,所以在这个步骤中,必须要创建一个 Editlog.new 文件,来记录临时操作,将原先的 Editlog 文件发送到备端进行合并和读取。
由于 Editlog 文件是在 JN 进程中做共享的,那么一旦备 NameNode 进行读写操作,这个时候主 NameNode 如果对 Editlog 进行读写操作,就会显示文件被占用的状态,为了解决这个问题,我们就需要创建一个 Editlog.new 的文件,来保障主 NameNode 可以正常执行业务,并且备 NameNode 也可以正常的进行读写操作。( 2 )备节点会对 Editlog 和 Fsimage 两个文件做合并,生成一个名为 Fsimage.ckpt 的文件,并将该文件发送给主端。
在这里我们针对 Editlog 中显示的用户对于元数据更改操作,按照时间顺序,对元数据文件进行更改操作。那么依据的根据就是 Editlog 中记录的操作的时间戳和分配的事务 ID。生成的文件我们称之为.ckpt 文件,所谓说 checkpoint,就是指我们认为在该时间点之前的数据都是正常并且正确的文件可以被持久化到硬件中进行记录和记载的。而检查点时间之后的数据,我们认为其是不可靠的,主要体现在存储的位置,比如内存中,或者说执行的进度等等,比如执行中,或执行未完成。只有执行完毕。并且执行的事务没有出现异常,而且数据、元数据、日志都记录明确的更改操作,而且存储在可靠的位置,才可以被执行持久化,被划分在检查点内(3)主节点在收到 Fsimage.ckpt 文件之后,会用这个文件将原先的 Fsimage 文件进行回滚。
由于合成的 Fsimage 文件最终都需要提供给主 NameNode 进行使用,所以我们需要将文件还发送回主 NameNode 中。当检查点文件发送到主 NameNode 之后,我们需要进行相关文件操作,将 Fsimage 和 Fsimage.ckpt 文件进行合并。那么合并涉及的操作,我们称之为回滚,其实可以理解为覆盖操作。Fsimage,ckpt 将自身文件覆盖回 Fsimage,如果数据发生了改变,那么 Fsimage.ckpt 的文件就会进行正常的覆盖操作。如果数据未改变,那就直接跳过覆盖。这个新更改过的文件最终会被持久化到磁盘中进行存储。
(4)最终主节点将 Editlog.new 更名为 Editlog。
首先我们需要明确一个问题就是 Editlog 这个文件作用就是记录其和元数据镜像文件的差异。那么在进行元数据持久化之后,旧的 Editlog 文件和回滚后的 Fsimage 中的元数据已经信息同步,所以旧的 Editlog 文件已经失去了存在的意义。这个时候我们直接删除掉旧的 Editlog 文件,并将在持久化过程中创建的
Editlog.new 文件直接重命名为 Editlog。这时候,新的 Editlog 文件记录的差异就是从上一次持久化开始的时候到下一次同步开始的这个周期内的改变。
七、元数据持久化健壮机制
该机制主要是为了保证数据在失效或故障状态下的一个可靠性保证,由于之前我们也提过,HDFS 作为大数据文件系统,有一个特点就是它认为硬件总是不可靠的,所以 HDFS 就必须承担起可靠性保障的机制运行。我们通过 HA 保障了 HDFS 的进程可靠性,通过元数据持久化保障了元数据的可靠性,数据的可靠性一方面是由数据副本机制来保障,但是在实际出现问题的时候,具体的处理方式和相关的故障前的维护,还是由元数据持久化健壮机制决定的。那么元数据的持久化健壮机制主要分为以下两个部分:
(1)重建失效数据盘的副本机制
Datanode 和 Namenode 之间需要通过心跳机制来保证数据状态,由 Namenode
来决定 Datanode 是否需要上报完整性,如果 Datanode 由于损坏无法进行完整性上报,那么 Namenode 就认为 Datanode 已经失效,并且发起恢复重建进程恢复丢失的数据。在这里,首先我们需要明确一个概念就是,心跳其实并没有按照心跳信息的形式去发送。虽然我们说 NameNode 和 DataNode 之间是通过心跳机制去保障数据状态的,但是他们之间发送的并不是心跳报文,而是周期性上报数据完整性的报文,那么 NameNode 一旦收到了数据完整性报文,那么就等同于收到了心跳报文,所以两个报文其实是整合为一个报文进行发送的,如果作为 NameNode 没有收到周期性发送的数据完整性报文,也就相当于 DataNode 出现了故障。
(2)集群数据均衡
集群数据均衡主要是通过该机制保证各个节点中的数据量基本均衡,保证各节点整体的利用率基本相同,不会因为某一个节点承载了过多的任务而导致压力过大。这里说到的集群的数据均衡主要是由两个形式进行保证的,第一个,在数据写入的时候,我们通过三副本机制,就可以进行一次数据的均衡操作,我们在写入数
据之前,首先会获取到节点的综合负载,根据负载的情况,选择当前最小的负载的设备,将数据写入。这样做理论上就会保证数据的均衡,但是会有一种情况,导致不均衡的状态出现,就是节点扩容,新添加的节点内没有任何数据,这时候它与旧节点之间就出现了不均衡的情况。均衡首先需要均衡服务来进行相关的信息收集和评估,然后在根据评估的情况,进行数据的迁移操作。
①均衡服务要求 Namenode 获取 Datanode 数据分布汇总情况由于 NameNode 本身需要周期性的获取 DataNode 的数据完整性信息,那么
NameNode 本身就可以根据自身的机制从 DataNode 上获取数据的分布情况,所以本身就存在着获取数据分布的能力,然后 NameNode 作为元数据维护节点,自身还可以进行该信息的汇总收集和存储,一方面 NameNode 从 DataNode 上获取了数据的分布情况,另一方面 NameNode 也根据该信息可以维护更新元数据,另外 DataNode 上报该信息也相当于是上报了心跳信息,告知了 NameNode 自身数据的完整性。所以在这里 Rebalancing Server 可以直接向 NameNode 获取该信息,而不再需要自身获取,自身汇总。减小了进程执行的开销。②服务查询到待均衡的节点之后,向 Namenode 请求对应数据的分布情况均衡服务会根据 NameNode 上报的数据分布汇总的情况,决定哪一些节点需要
进行数据的均衡操作。然后在根据分析的情况再向 NameNode 请求详细的数据分布情况,之后根据 NameNode 反馈回来的详细的数据分布情况,Rebalancing Server 就会制定策略,指定哪一些数据块是需要做迁移操作的。然后开始下发迁移的请求。
③每迁移一个数据块,均衡服务都需要拷贝这个数据块做备份在迁移的过程中,由于迁移相当于是剪切的操作也可以理解为移动 move 操作,
所以在迁移的过程中,为了保证数据的安全性,我们的 Rebalancing Server 就需要对迁移中的数据做保护。迁移中的数据会由 Rebalancing Server 做备份。相当于除了迁移操作之外,Rebalancing Server 还会对每一个迁移的数据做一次拷贝操作,那么一旦数据迁移完成之后,备份的数据块就会被从 Rebalancing Server 中删除。
④从源节点向目的节点拷贝数据在均衡的过程中,我们需要考虑一个问题, 就是迁移对网络的影响,由于迁移
是跨设备的操作,而且设备与设备之间是通过网络进行的连接,所以在进行迁移的时候,我们需要注意的一个问题就是网络带宽对迁移的影响。由于在迁移的过程中,迁移数据会对业务有一些影响,因为在迁移的过程中,数据有一部分是无法访问的,所以我们需要在业务空窗期进行数据的迁移是最合适的,那么业务的空窗期例如在视频网站中,每晚的凌晨时间就是空窗期,那么空窗期是有时间范围的,所以我们如果必须要保障在空窗期内将数据迁移完成,那么就必须考虑网络带宽对迁移的影响了。迁移的数据量/迁移的空窗期=迁移的网络带宽。
⑤迁移完成后,修改 Namenode 中的元数据信息
⑥向源端返回确认消息
⑦向迁移服务返回均衡完成消息,均衡服务释放拷贝的数据。
(3)数据有效性保障
为了保障 Datanode 中的数据读出都是有效的,HDFS 设计了一个机制,针对每一个数据在写入时都设计了校验值,当数据读出的时候,首先要去计算校验值,如果校验值不匹配,Namenode 就会认为数据已经失效,将会从其他节点上的副本进行数据的读取。数据的校验值主要是为了保障数据的有效性的,我们的校验值是通过哈希值算法得到的,跟随数据一起写入到 DataNode 中。
(4)安全模式
当节点硬盘故障时,进入安全模式,HDFS 只支持访问元数据,此时 HDFS 上 Datanode 的数据是只读的,其他的操作如创建、删除文件等操作都会导致失败。待硬盘问题解决、数据恢复后,再退出安全模式。由于在节点硬盘出现问题的时候我们需要通过副本机制或其他相关的机制对数据进行可靠性的保证,所以这个时候本身数据就是处于一个被占用的状态,所以在这个时候,我们的数据只能执行读取操作,这个时候如果有写操作的话,由于硬盘的故障就可以能会导致如下的问题,就是数据写入的时候,由于硬盘故障写入失败或者是数据写入之后由于硬盘原因导致数据丢失,但是元数据已经修改完成,这个时候就会出现数据的不一致情况。所以我们会将数据和硬盘拉进安全模式中,只提供访问的只读操作,而不能进行写操作,这个主要是为了保证数据的安全性和可靠性,更主要的是为了保证数据的一致性。
八、HDFS 数据写入流程
( 1 )首先,客户端请求写入一个文件, Client 通过自身的 Distributed FileSystem 进程向 Namenode 发起 RPC 请求。
这里说的 Client 并不是指客户的应用程序,而是本身 HDFS 自带的进程,该进程提供了对外的访问接口,和对内其他组件之间的交互接口。RPC 远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
(2)收到 RPC 请求之后,Namenode 会在自己的 NameSpace 中创建一个文件信息该过程的主要作用就是创建元数据,如果执行的是改写的操作,那么元数据本身就存在与 NameNode 上,这个时候就不需要执行相关的创建操作了,如果是新写入数据,那么就需要我们执行这个创建元数据的操作。创建元数据操作主要的作用是分配写空间。
(3)创建完成之后,DistributedFileSystem 会在 Client 本地返回一个 FSDataOutputStream 进程元数据创建完成之后,我们需要提供一个接口用于连接 DataNode 并且进行相关
的数据写入操作,这个时候我们使用的就是流式数据导入进程。那么 FsDataOutputStream 提供的还是一个访问接口,但是数据这次在进行写入的时候,就是通过流式写入的。我们写入数据的时候还是需要调用接口的。
(4)业务会在本地调用 writeAPI 接口,将数据写入到 Client 进程中。
(5) Client 收到数据之后,会将数据通过 FSDataOutputStream 进程进行包装,包装为 DFSOutputStream,联系 DataNode,并和需要写入数据的 DataNode 建立起流水线。完成后,Client 再通过自有协议,按照从 DistributedFileSystem
获取到的数据写入的数据块编号和位置信息(也就相当于是元数据创建时分配的写空间),写入数据到 DataNode1,再由 DataNode1 复制到 DataNode2,DataNode3
(创建数据副本机制,数据副本机制是节点之间的数据传输,所以相当于在进行实际的数据写入时,Client 只写了一份数据,其他的副本数据是由 DataNode 之间进行拷贝和传输的)
(6)数据在写入成功之后,将会由 DataNode 向 Client 返回确认信息(7)所有数据确认完成后,文件写入成功,业务调用 HDFSClient 关闭文件的写入进程。
(8)业务调用 close 关闭进程,flush 后 HDFSClient 联系 NameNode,确认数据写完成,NameNode 更新元数据
九、HDFS 数据读取流程
( 1 )首先,客户端请求读取一个文件, Client 通过自身的 Distributed FileSystem 进程向 Namenode 发起 RPC 请求(2)HDFSClient 联系 NameNode,获取到文件信息(数据块、DataNode 位置信息)。
(3)业务应用调用 readAPI 读取文件。DistributedFileSystem 通过对 NameNode 发出 RPC 请求,确定要读取文件的 block 的位置,DistributedFileSystem 返回一个 FSDataInputStream 给 HDFS 客户端,让它从 FSDataInputStream 中读取数
据
(4)HDFSClient 根据从 NameNode 获取到的信息,(Client 采用就近原则读取数
据)。由 FSDataInputStream 包装一个 DFSInputStream,用来与 DataNode 及 NameNode 的 I/O 通信。
(5)HDFSClient 会与多个 DataNode 通讯获取数据块。(6)数据读取完成后,业务调用 close 关闭连接。
根据数据的读进程,我们可以关注到如下的几个问题,首先第一个就是我们在进行读取的时候还是通过流式数据访问进程进行的读操作,这里就体现了 HDFS 的流式访问,也体现了 HDFS 的高吞吐量的特点,第二个就是关于数据副本机制的问题,数据副本机制其实可以理解为一个数据的多个副本没有主备关系,互为副本,实际在进行读取操作的时候,Client 会选择读取就近的 DataNode 上的数据,这样可以极大的减小数据传输对于网络的影响。另外一个就是我们在进行读取操作的时候,不仅仅是只读一个副本的数据,读取是一个并发的读操作,所以各个副本都会进行数据的读取传输,这样就可以提升整体的数据传输效率,减小读取消耗的时间。
九、HDFS 数据读取流程
( 1 )首先,客户端请求读取一个文件, Client 通过自身的 Distributed FileSystem 进程向 Namenode 发起 RPC 请求(2)HDFSClient 联系 NameNode,获取到文件信息(数据块、DataNode 位置信息)。
(3)业务应用调用 readAPI 读取文件。DistributedFileSystem 通过对 NameNode 发出 RPC 请求,确定要读取文件的 block 的位置,DistributedFileSystem 返回一个 FSDataInputStream 给 HDFS 客户端,让它从 FSDataInputStream 中读取数
据
(4)HDFSClient 根据从 NameNode 获取到的信息,(Client 采用就近原则读取数
据)。由 FSDataInputStream 包装一个 DFSInputStream,用来与 DataNode 及 NameNode 的 I/O 通信。
(5)HDFSClient 会与多个 DataNode 通讯获取数据块。(6)数据读取完成后,业务调用 close 关闭连接。
根据数据的读进程,我们可以关注到如下的几个问题,首先第一个就是我们在进行读取的时候还是通过流式数据访问进程进行的读操作,这里就体现了 HDFS 的流式访问,也体现了 HDFS 的高吞吐量的特点,第二个就是关于数据副本机制的问题,数据副本机制其实可以理解为一个数据的多个副本没有主备关系,互为副本,实际在进行读取操作的时候,Client 会选择读取就近的 DataNode 上的数据,这样可以极大的减小数据传输对于网络的影响。另外一个就是我们在进行读取操作的时候,不仅仅是只读一个副本的数据,读取是一个并发的读操作,所以各个副本都会进行数据的读取传输,这样就可以提升整体的数据传输效率,减小读取消耗的时间。
十、HDFS 联邦
Federation 支持上层应用使用多个独立的基于 NameNode/Namespace 的文件系统。这些 NameNode 之间相互独立且不需要互相协调,各自分工管理自己的区域。一个 Namespace 使用一个 blockpool 管理数据块,每个 blockpool 内部自治,不会与其他 blockpool 交流。同时 Federation 中存在多个命名空间,可以使用 Client Side Mount Table 对命名空间划分和管理。
扩展性:支持 NameNode/Namespace 水平扩展,后向兼容,结构简单。
性能:文件操作的性能不再制约于单个 NameNode 的吞吐量,支持多个 NameNode。隔离性:可按照应用程序的用户和种类分离 Namespacevolume,进而增强了隔离性。
NameSpace(NS):命名空间。 HDFS 的命名空间包含目录、文件和块。Pool: blockpool.FederationHDFS 中有多个独立的命名空间(Namespace),并且每一个命名空间使用一个块池(blockpool)。Blockpool(块池)是属于单个命名空间的一组 block(块),每一个 DataNode 为所有的 blockpool 存储块。DataNode 是一个物理概念,而 blockpool 是一个重新将 block 划分的逻辑概念。同一个
DataNode 中可以存着属于多个 blockpool 的多个块。Blockpool 允许一个命名空间在不通知其他命名空间的情况下为一个新的 block 创建 BlockID。同时,一个
NameNode 失效不会影响其下的 DataNode 为其他 NameNode 的服务。
十一、HDFS 数据存储策略
HDFSNameNode 自动选择 DataNode 保存数据的副本。在实际业务中,存在以下场景:
DataNode 上存在的不同的存储设备,数据需要选择一个合适的存储设备分级存储数据。
DataNode 不同目录中的数据重要程度不同,数据需要根据目录标签选择一个合适的 DataNode 节点保存。
DataNode 集群使用了异构服务器,关键数据需要保存在具有高度可靠性的节点组中。
数据存储策略可以广泛的应用于各种业务,就像存储中使用的分级存储一样,在 HDFS 中,我们一样可以提供很高的一个存储的策略,用于做不同业务的数据保证,比如,作为一个视频网站,那么新上架一个电视剧,那么访问流量就会很大,这个时候就可以将数据放在内存虚拟硬盘或者是 SSD 中,电视剧结局之后,访问量就会逐渐减小,这个时候就会访问量逐渐较小,我们就可以将数据放在 SAS 硬盘中,随着时间增长,访问量很小的时候就可以将数据放在 SATA 硬盘或者进行归档。HDFS 的策略和以上存储的策略比较类似,但是 HDFS 存放数据涉及的根据访问量迁移的情况不多,主要是在一开始就进行数据的相关存放操作,比如关键业务的数据就可以放在访问快可靠性高的介质中,普通业务就可以提供一个正常的保护策略。
存储策略分为以下几个方面:
(1)标签存储
用户通过数据特征配置 HDFS 数据块存放策略,即为一个 HDFS 目录设置一个标签
表达式,每个 DataNode 可以对应一个或多个标签;当基于标签的数据块存放策略为指定目录下的文件选择 DataNode 节点进行存放时,根据文件的标签表达式选择出将要存放的 DataNode 节点范围,然后在这个 DataNode 节点范围内,遵守下一个指定的数据块存放策略进行存放。
这里详细解释可以理解为,我们是给元数据进行的打标签的操作,由 NameNode 在进行写空间分配的时候进行相关的策略控制,根据数据的写入操作,我们可以发现,不管是什么写操作,在写入之前第一步都必须要对元数据做访问,那么这个时候,我们给元数据上就打了标签,也就相当于是在元数据分配写空间的时候就已经对数据的写入位置做了相关的控制。其实也就相当于我们在数据写入之前就给数据做了对应的存储策略,那么这样就可以保证数据被写入到对应的介质中。
(2)节点组存储
配置 DataNode 使用节点组存储:关键数据根据实际业务需要保存在具有高度可靠性的节点中,此时 DataNode 组成了异构集群。通过修改 DataNode 的存储策略,系统可以将数据强制保存在指定的节点组中。
节点组存储和标签存储最大的不同主要有以下的几个方面:①节点组存储是由 DataNode 执行的,标签存储是由 NameNode 来做的。②节点组存储的作用对象是副本数据。控制的源是第一份数据。标签存储作用对象是第一份数据的写入,控制的源是元数据中的目录标签。③节点组存储保证的是数据的可靠性,标签存储保障的不仅是数据的可靠性还有安全性和可用性,所以标签存储的控制范围要比节点组存储的范围要大。
使用约束:在使用约束之前,首先需要配置约束,我们需要为数据副本指定强制机架组。
第一份副本将从强制机架组(机架组 2)中选出,如果在强制机架组中没有可用节点,则写入失败。所以第一份副本是做强制保护的,必须保障写入成功。
第二份副本将从本地客户端机器或机架组中的随机节点中(当客户端机器机架组不为强制机架组时)选出。
强制机架组只会存放一份副本数据,所以当节点需要创建副本的时候,首先我们需要将数据写入到强制机架组中,当数据写第二份副本的时候,我们就需要检查
节点所属的机架组是否是强制机架组,如果是,那么根据强制机架组只存放一份副本的强制策略,我们就不能再把数据写入到本机架组了,这个时候就需要随机选机架组写入。如果节点所属的机架组不是强制机架组,那么就正常的进行写入,第一份数据——强制机架组,第二份数据——本地设备或本地机架组
第三份副本将从其他机架组中选出。各副本应存放在不同的机架组中。
如果所需副本的数量大于可用的机架组数量,则会将多出的副本存放在随机机架组中
(3)分级存储
分级存储主要是提供如上所说的高性能和高可用性的目的,具体意义不在赘述。配置 DataNode 使用分级存储:HDFS 的异构分级存储框架提供了 RAM_DISK(内存虚拟硬盘)、DISK(机械硬盘)、ARCHIVE(高密度低成本存储介质)、SSD(固态硬盘)四种存储类型的存储设备。
通过对四种存储类型进行合理组合,即可形成适用于不同场景的存储策略。
例如策略 10,策略 ID 为 10 号,名称为 ONE_SSD,Block 的放置机制为,第一份数据存放在 SSD 中,第二份及之后的数据存放在硬盘中,如果 Block 放置位置策略中指定的介质出现问题,无法正常的进行写入,那么我们就需要通过备选存储
策略来进行存放,那么根据例子中所指定的,如果主策略出现问题,那么这个时候我们的第一份副本优选 SSD,如果没有 SSD 那么就存放在 Disk 机械硬盘中,副本的备选存储策略指定的是副本数据的存放位置,含义和备选存储策略一致,综上所述,所以 Block 放置位置策略指定的是在正常的情况下,数据的存储策略,如果出现问题,备选存储策略指定的是第一份数据的存储策略,副本的备选存储策略指定的是副本的存放策略,如果备选存储策略和副本的备选存储策略出现 NONE 值,那么一旦主策略无法写入那么就会直接返回写入失败。
十二、Colocation 同分布
同分布的概念很类似于存储当中的一致性组的概念,由于针对某一些业务我们需要保证数据的一致性,比如 Oracle 中不同的 LUN 的存储需要保证实时性,并且数据必须要保持在一个时间点,如果时间点的一致性无法保证,那么整体的数据库就会失效。同分布的概念就是指,针对某一些在业务上有关联性的数据,我们要尽可能的将这些数据分布到同一个节点和机架上,这样保证在进行数据的读取或者写入的时候,我们可以尽快的访问到目标数据,而且针对数据的可靠性也会随之提升。并且使用了同分布之后,我们在进行数据的读取时更可以提供一个高速访问,Client 就可以只从一个节点获取到全部的数据,减少了节点间的交互次数对于和读写次数,对于减小网络延迟也提供了很大的贡献。
编语:同分布虽然从严格意义上去看,没有满足数据的安全性需求,由于一个节点的失效可能会导致多个数据的同时失效,但是其实对于耦合性高的数据来说,一份数据的失效就意味着全部数据的失效,所以存储到不同位置其实也是对计算资源的浪费,存储在同一位置还更利于数据的读取,并且大数据平台本身就提供了副本机制,也可以正常的保证数据的一致性和可靠性。
同分布操作前:
同分布操作后: