hdfs数据节点实现

数据块存储

数据节点的磁盘目录文件结构
在第一次启动HDFS集群前,需要通过如下命令对名字节点进行格式化,让名字节点建立对应的文件结构:

bin/hadoop namenode–format
数据节点可以管理多个数据目录,被管理的目录通过配置项${dfs.data.dir}指定,如果该配置项的值为“/data/datanode,/data2/datanode”,则数据节点会管理这两个目录,并把它们作为数据块的保存目录。
数据节点根目录结构
其中各个目录的作用如下:
blocksBeingWritten:由名字可以知道,该文件夹保存着当前正在“写”的数据块。和位于“tmp”目录下保存的正在“写”的数据块相比,差别在于“blocksBeingWritten”中的数据块写操作由客户端发起。
current:数据节点管理的最重要目录,它保存着已经写入HDFS文件系统的数据块,也就是写操作已经结束的已“提交”数据块。该目录还包含一些系统工作时需要的文件。
detach:用于配合数据节点升级,供数据块分离操作保存临时工作文件。
tmp:该文件夹也保存着当前正在“写”的数据块,这里的写操作是由数据块复制引发的,另一个数据节点正在发送数据到数据块中。
数据节点上数据块的状态
d f s . d a t a . d i r 目 录 下 的 另 一 个 文 件 是 “ i n u s e . l o c k ” , 顾 名 思 义 , 它 表 明 目 录 已 经 被 使 用 , 实 现 了 一 种 “ 锁 ” 机 制 。 如 果 停 止 数 据 节 点 , 该 文 件 会 消 失 , 通 过 文 件 “ i n u s e . l o c k ” , 数 据 节 点 可 以 保 证 独 自 占 用 该 目 录 , 防 止 两 个 数 据 节 点 ( 当 然 , 可 以 在 一 个 节 点 上 启 动 归 属 不 同 H D F S 集 群 的 多 个 数 据 节 点 ) 实 例 共 享 一 个 目 录 , 造 成 混 乱 。 ∗ ∗ {dfs.data.dir}目录下的另一个文件是“in_use.lock”,顾名思义,它表明目录已经被使用,实现了一种“锁”机制。如果停止数据节点,该文件会消失,通过文件“in_use.lock”,数据节点可以保证独自占用该目录,防止两个数据节点(当然,可以在一个节点上启动归属不同HDFS集群的多个数据节点)实例共享一个目录,造成混乱。 ** dfs.data.dirinuse.lock使inuse.lockHDFS{dfs.data.dir}/current目录**
current目录结构
大部分文件都以blk_作为前缀,这些文件有两种类型:
HDFS数据块,用来保存HDFS文件的内容,如图中的文件“blk_249906531933748087”。使用meta后缀标识的校验信息文件,用来保存数据块的校验信息。
当目录中存储的数据块增加到一定规模时,数据节点会创建一个新的目录,用于保存新的块及元数据。目录块中的块数目达到64时(由配置项${dfs.datanode.numblocks}指定),便会创建子目录(如图中的目录“subdir56”),并形成一个更宽的目录树结构。同时,同一父目录下最多会创建64个子目录,也就是说,默认配置下,一个目录下最多只有64个数据块(128个文件)和64个目录。
${dfs.data.dir}/current目录中还有三个特殊的文件,“VERSION”文件是一个Java属性文件,包含了运行的HDFS版本信息。另外两个文件“dncp_block_verification.log.curr”和“dncp_block_verification.log.prev”,则是数据块扫描器工作时需要的文件,在后续的讨论中会详细分析这些文件。
数据节点存储的实现
数据节点的文件结构管理包括两部分内容,数据(节点)存储DataStorage和文件系统数据集FSDataset。
StorageInfo及其子类
数据节点存储DataStorage是抽象类Storage的子类,而抽象类Storage又继承自StorageInfo。在这个继承体系中,和DataStorage同级的FSImage类用于组织名字节点的磁盘数据,FSImage的子类CheckpointStorage,则管理着第二名字节点使用的文件结构。CheckpointStorage和FSImage的继承关系,体现了名字节点和第二名字节点的密切联系。
图7-4中的另外两个类:NamespaceInfo和CheckpointSignature,出现在HDFS节点间通信的远程接口中,它们分别应用于数据节点和名字节点通信的DatanodeProtocol接口和第二名字节点和名字节点通信的NamenodeProtocol接口。
StorageInfo包含的三个字段的含义,分别是HDFS存储系统信息结构的版本号layoutVersion、存储系统标识namespaceID和存储系统创建时间cTime。
DataStorage类图
值得分析的是StorageDirectory的成员变量和getVersionFile()、tryLock()等方法。
成员变量StorageDirectory.root保存着存储目录的根,dirType保存着该目录对应的类型。需要注意的是类型为java.nio.channels.FileLock的成员变量lock。FileLock,顾名思义,就是文件锁。Java的文件锁,要么独占,要么共享。在这里,StorageDirectory使用的是独占文件锁,对lock的加锁代码在tryLock()中。

数据节点升级

在数据节点上,升级并不需要两倍的集群存储空间,数据节点使用了Linux文件系统的硬链接,保留了对同一个数据块的两个引用,即当前版本和以前版本。通过这样的技术,就可以在需要的时候,轻松回滚到以前版本的文件系统。
升级
数据节点的升级
数据节点升级的实现在DataStorage.doUpgrade()方法中。其中,升级过程涉及如下几个目录:
curDir:当前版本目录,通过StorageDirectory.getCurrentDir()获得,目录名为“current”。prevDir:上一版本目录,目录名为“previous”,可通过StorageDirectory的getPreviousDir()方法得到;这里的“上一版本”指的是升级前的版本。tmpDir:上一版本临时目录,即目录${dfs.data.dir}/previous.tmp。doUpgrade()方法的主要流程是:首先确保上述涉及的工作目录处于正常状态。如检查“current”目录是否存在,如果不存在,也就不需要升级了(“current”目录在数据节点格式化的时候创建);如果“previous”目录存在,删除该目录。注意,该删除操作相当于提交了上一次升级,同时保证了HDFS最多保留前一版本数据的要求;保证“tmpDir”目录不存在。应该说,在(通过硬链接)移动数据前,“previous”目录和“previous.tmp”都是不存在的。
DataStorage.doUpgrade()保留升级前数据的动作一共有两步:首先将“current”目录名改为“previous.tmp”,然后,调用linkBlocks(),在新创建的“current”目录下,建立到“previous.tmp”目录中数据块和数据块校验信息文件的硬链接。DataStorage.linkBlocks()执行结束后,doUpgrade()方法还需要在“current”目录下创建新版本的“VERSION”文件,最后,将“previous.tmp”目录名改为“previous”,完成升级。
升级回滚
升级回滚doRollback()的实现有如下要点:
1)在完成对各个工作目录状态的检查后,需要保证升级能够回滚到正确的版本上去。这个步骤是通过比较保存在“previous”目录下“VERSION”文件中的HDFS存储系统信息结构的版本号layoutVersion和存储系统创建时间cTime和回滚后相应的layoutVersion和cTime来做判断。2)由于“previous”目录保存了升级前的所有数据,所以,doRollback()其实只需要简单地将“previous”目录改名成“current”,就可以完成回滚。
先将“current”目录名改为“removed.tmp”,然后将“previous”目录名修改为“current”,最后删除“removed.tmp”目录。
升级提交
升级提交doFinalize()更简单,它只需要将“previous”目录删除即可。但实际上,DataStorage.doFinalize()需要将“previous”目录名改为“finalized.tmp”,然后删除“finalized.tmp”来删除目录,而不是直接删除“previous”来提交升级。

文件系统数据集的工作机制

DataStorage专注于数据节点存储空间的生存期管理,在存储空间上,与数据节点逻辑密切相关的存储服务,如创建数据块文件、维护数据块文件和数据块校验信息文件的关系等,则实现在文件系统数据集FSDataset中。
FSDatasetInterface接口继承关系
FSDatasetInterface接口的方法可以分为以下三类:
数据块相关的方法:FSDataset管理了数据节点上的数据块,大量的FSDataset方法和数据块相关,如创建数据块、打开数据块上的输入、输出流、提交(finalize)数据块等。数据块校验信息文件相关:包括维护数据块和校验信息文件关系,获取校验信息文件输入流等方法。其他:包含FSDataset健康检查、关闭FSDataset的shutdown()方法等。
Linux的LVM引入了物理卷、卷组和逻辑卷的存储概念:
物理卷(Physical Volumn):典型的物理卷是磁盘分区,也可以是整个磁盘或者是其他块设备(如磁盘阵列)。
卷组(Volumn Group):卷组建立在物理卷之上,是由一个或者多个物理卷所组成的存储器池。
逻辑卷(Logic Volumn):相当于非LVM系统中的分区,它建立在卷组的基础上,是一个标准的块设备,可以在逻辑卷上建立文件系统。
FSDataset没有使用Linux LVM的方式管理它的存储空间,但是,FSDataset借鉴了LVM[插图]的一些概念,可以管理多个数据目录。一般情况下,这些不同的数据目录配置在不同的物理设备上,以提高磁盘的数据吞吐量。
文件数据集将它管理的存储空间分为三个级别,分别用FSDir、FSVolume和FSVolumeSet进行抽象。
 FSDataset的内部类
FSDir对象表示了“current”目录下的子目录,其成员变量children包含了目录下的所有子目录,形成目录树。FSVolume是数据目录配置项${dfs.data.dir}中的一项,数据节点可以管理一个或多个数据目录,系统中也就存在一个或多个FSVolume对象。这些FSVolume对象由FSVolumeSet管理。

流式接口的实现

读数据
读请求中包含如下字段:
blockId(数据块ID):要读取的数据块标识,数据节点通过它定位数据块。generationStamp(数据块版本号):用于进行版本检查,防止读取错误的数据。startOffset(偏移量):要读取数据位于数据块中的位置。length(数据长度):客户端需要读取的数据长度。clientName(客户端名字):发起读请求的客户端名字。accessToken(访问令牌):与安全特性相关,不讨论。DataXceiver.readBlock()给出了数据节点读数据流式接口实现的框架。方法的开始部分,会通过Socket连接的输入流,读取上述请求信息并构造一个数据块发送器BlockSender对象,然后,通过该对象发送数据,数据发送完毕后,方法执行一些清理工作。
“零拷贝”数据传输
Java通过java.nio.channels.FileChannel中的transferTo()方法,在Linux(和UNIX)系统上支持零拷贝I/O操作,transferTo()方法用于直接将字节从它被调用的通道[插图]上传输到另外一个可写字节通道上,数据无需经过应用程序。
客户端读数据块数据时的缓冲区拷贝
如果底层网络接口卡支持收集操作,就可以进一步减少内核的数据复制,实现图右下角的描述符传递方式。支持收集操作的网络接口,不要求待传输的数据连续存储,数据可以分散存储在不同的位置上。这样一来,从文件中读出的数据就根本不需要被拷贝到Socket缓冲区中,而只是需要将缓冲区描述符(缓冲区中建立的与数据包相关结构)传递到网络协议栈中去,然后通过DMA收集拷贝功能将所有的数据结合成一个网络数据包,发送到网络上。
写数据
客户端写HDFS文件数据的操作码为80,请求包含如下主要字段:
blockId(数据块ID):写数据的数据块标识,数据节点通过它定位数据块。
generationStamp(版本号):数据块的版本号,用于进行版本检查。
pipelineSize(数据流管道的大小):参与到写过程的所有数据节点的个数。
isRecovery(是否是数据恢复过程):这个写操作是不是错误恢复过程中的一部分。
clientName(客户端名字):发起写请求的客户端名字,可能为空。
hasSrcDataNode(源信息标记):写请求是否携带源信息,如果是true,则包含源信息。
srcDataNode(源信息,可选):类型为DatanodeInfo,包含发起写请求的数据节点信息。
numTargets(数据目标列表大小):当前数据节点还有多少个下游数据推送目标。
targets(数据目标列表):当前数据节点的下游数据推送目标列表。
accessToken(访问令牌):与安全特性相关,不讨论。
checksum(数据校验信息):类型为DataChecksum,包含了后续写数据数据包的校验方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值