LinuxEye - Linux系统教程

LinuxEye - Linux系统教程

当前位置: 主页 > Linux教程 >

HDFS的升级管理

时间:2015-04-29 09:23来源:未知 编辑:linuxeye 点击:
HDFS是一个分布式文件系统,目前还处于heavy development的周期中,随着HDFS版本的升级,功能的加强,HDFS的升级功能就显得特别重要,Hadoop项目官方文档中对于升级功能只提了几段,对于一
HDFS是一个分布式文件系统,目前还处于heavy development的周期中,随着HDFS版本的升级,功能的加强,HDFS的升级功能就显得特别重要,Hadoop项目官方文档中对于升级功能只提了几段,对于一个处于生产环境上的HDFS升级来说,这显然是不够的,这里在最新的Hadoop 2.6.x版本源码上分析HDFS升级的过程。

升级HDFS的概要过程和命令

Hadoop的官方文档中,对于HDFS的升级建议分三个步骤,1,先停掉HDFS服务,再启动,HDFS合并FsEditLog到FsImage 之中,再停掉HDFS服务,2,备份namenode的meta文件,在新版本HDFS安装目录的配置文件中,配置namenode的meta文件目录指 向旧有的meta文件目录,以-upgrade选项启动HDFS,让HDFS服务执行升级过程,3,升级进度达到100%后,执行hadoop dfsadmin -finlizeUpgrade告诉HDFS服务,升级结束。如果升级失败,以-rollback选项启动HDFS,执行回滚。

从升级到本身使命来说,升级到目的就是为了保护现有数据,如果在升级过程中,出现因为升级而导致数据丢失,或者整个HDFS文件系统的损坏,那升级 就失败了。看完Hadoop官方文档的升级过程和命令后,我很好奇,如此简单的描述,能保证升级过程一定是“达到”目的的吗?或者至少保证不会损坏 HDFS文件系统本身!

怀疑是怀疑,等了解它,再对它下结论!然而HDFS升级的资料非常少,这里从源码分析,力求做到最接近作者升级设计的意图。

NameNode的存储结构

在HDFS文件系统中,NameNode的一个基本职责是分配块到datanode上;告诉客户端一个块的内容可以从哪台机器上能读取到。块信息对 于HDFS文件系统来说,是非常重要的,为了不丢失块信息,并能让客户端,datanode能快速懂检索块信息,HDFS的开发者们在设计 NameNode的存储结构时,借鉴了数据库的WAH一些做法,但同时又采用了一种非常不常见到做法。

为了保证块信息不丢失,HDFS在分配一个块时,先写块信息的日志到磁盘的文件系统,这个存储在HDFS中叫fsEditLog,日志写成功后,再 向处于JVM heap中的一种可以存储重复HASH值条目的Map中,添加这个块信息,如果是删除块操作,则是从这个Map中删除相应的条目。这个Map中保存了 HDFS中所有块信息,而不是常见的缓存部分数据。在HDFS的namenode存储中,磁盘上存储的块信息和Map中存储的块信息的实际数量是一样的。 为了保证这个Map所存储的数据不超过JVM Heap的限制,Namenode在启动的时候,默认JVM的-Xx启动参数值的2%作为计算这个Map最多可以容纳条目的数量,Map中的条目达到这个 最大值时,则不可以添加新的块。在磁盘端,块信息主要存储在fsImage中,块操作日志存储在fsEditLog中,自fsImage文件中最后的事务 点后,对于块的操作是首先顺序对追加到fsEditLog中,而不是直接写入fsImage。HDFS服务在启动时会启动一个checkpointer组 件,定期的把当前的fsImage和fsEditLog合并到新的fsImage中。

NameNode的meta内部物理结构如下图所示:
`-- current
    |-- edits_0000000000000000001-0000000000000000007
    |-- edits_inprogress_0000000000000000008
    |-- fsimage_0000000000000000000
    |-- fsimage_0000000000000000000.md5
    |-- seen_txid
    `-- VERSION

从存储上看HDFS的存储结构,只有在HDFS启动时,从fsImage中载人块信息到内存端的Map,或者在需要时,把内存端的Map写入磁盘端 去。对于块的增、删、改这样的操作,内存端和磁盘端独立,日志能保证最终的块信息在两端是一致的。这样设计的好处是,简单,容易实现,缺点是内存端大小会 受到限制,不容易扩展。

DataNode的存储结构

相对NameNode对于块的存储结构,DataNode上对于块的存储结构简单很多,DataNode只存储<块,大小>对。一个块 被写入到DataNode上后,会写一个和这个块名相同的meta文件,存储块大小和块生成的时间。DataNode中,对块的生命周期进行了细致的划 分,当向文件追加的新块时,块的是位于RBW目录(1.x则是在BlockBeingWritten目录),当这个块达到系统指定的块大小或者文件写关 闭,这个块会变成finallized状态,DataNode把此块移动到current/finallized目录中。

在HDFS 2.x中,对于数据块的存储是按照BlockPoolSlice来管理的,从逻辑上说,一个BlockPoolSlice代表DataNode配置项 dfs.datanode.data.dir中的一个目录。DataNode上新建块,移动和删除块是通过BlockPoolSlice完成逻辑上的操 作。BlockPoolSlice内部的物理结构如下图所示:

|-- current
|   |-- BP-1134876178-10.1.101.51-1427874067591
|   |   |-- current
|   |   |   |-- dfsUsed
|   |   |   |-- finalized
|   |   |   |-- rbw
|   |   |   `-- VERSION
|   |   |-- dncp_block_verification.log.curr
|   |   |-- dncp_block_verification.log.prev
|   |   `-- tmp
|   `-- VERSION
|-- detach
|-- in_use.lock
|-- storage
`-- tmp

NameNode存储的升级

对于NameNode meta文件的升级,NameNode在启动时,如果分析带有upgrade命令选项,会升级meta文件夹中的current目录,现在假设HDFS刚 重启过一次,并没有新的块添加到HDFS中,此时,fsEditLog都是空的,对于此目录的操作按照下面的操作步骤进行。

  1. 如果meta文件夹中有previous目录,删除它

  2. 把current重名为previous.tmp

  3. 新建current目录

  4. 把previous.tmp文件下的VERSION文件升级为当前安装的版本,写入current目录

  5. 载人previous.tmp文件下fsImage到中的文件和块到内存,载入这个过程是向下兼容的,经载入后的文件和块已经被转化成当前安装版本的格式。

  6. 旧版本fsImage中的genStamp、clusterId、numFiles信息保留不变,但旧版本的fsImage文件名和fsEditLog文件可能会改变

  7. 在fsImage载入成功后,把处于内存端的文件和块按照最新的格式写入磁盘的current目录,并且在这个时候,告诉系统,NameNode的meta文件升级操作完成!

从这个过程中,可以看出,NameNode在升级的过程中,并没有在旧版本的fsImage和fsEditLog上直接进行操作,而是新建一个文件夹,这样的方式是非常安全的。首先解开了开篇的一半疑问。

DataNode存储的升级

DataNode升级的过程和NameNode的fsImage升级的方式比较类似,但在具体操作上有很大的不同,DataNode上存储的主要是 数据块,如果采用复制数据块到新的目录,那复制PB,EB级别的数据,这个过程将变得不可想象。大师们为DataNode的升级寻找到了一个合适的方案。

对于一个BlockPoolSlice的物理文件夹,升级执行下面的步骤

  1. 如果BlockPoolSlice的物理文件夹中有previous,删除它

  2. 重命名current目录为previous.tmp

  3. 在DataNode文件中创建current/{bpid}/current目录, 这里的bpid是blockpool ID,是HDFS 2.x版本中引入的新术语,用于管理block pool slice

  4. 遍历previous.tmp文件中所有的块,为这些块建立硬连接,保存到current/{bpid}/current目录,这个过程 中,块文件的名称可能会因版本跨度而发送变化。回想一下NameNode中块信息的升级,由于块信息中块的文件名主要包户一个生成的时间戳和文件的序列 号,因此,在变更块文件名的时候,只要NameNode和DataNode按照相同的规则进行变换,在升级后,仍能保持和HDFS升级前在用户层面上是的 一致的。

  5. 重命名previous.tmp目录为previous,并在这个时候告诉HDFS,此block pool slice升级完成

硬连接是某些文件系统的一个高级特性,某些文件系统中设计有两种Node,一种是INode,存储文件的索引信息,一种是DataBlock,存储 文件真正的数据,文件的名字是存储在INode中的,INode还使用一个指针指向此文件的第一个DataBlock,建立硬连接实质上是为文件的数据块 新建一个INode,这个INode也是指向此文件的第一个数据块。而这个INode中包含的文件名可以和此文件的原名字不同。当然,原文件名其实也是存 储该文件DataBlock的硬连接。在这样的文件系统上,删除一个文件,只是删除这个文件的INode,如果一个文件的DataBlock没有 INode指向它,这些DataBlock就可以被文件系统回收。只要还有一个INode指向这些DataBlock,它们就不会被回收。在这样的文件系 统中,INode可以被设计得很小,比如Ext4,一个INode占512字节,一个DataBlock至少是4K,在格式化磁盘时,系统分配INode 的个数约为DataBlock个数的1/8。

所以,采用硬连接的方式升级DataNode所需要操作的磁盘空间就小很多了。从逻辑上说,也不会对文件系统造成损坏。

rollback和upgradeFinallized

如果升级过程失败,需要通过rollback命令选项,让HDFS回滚到升级之前到状态。由于此时两个版本的HDFS文件系统信息都是是存在的,回滚的过程其实是一个文件夹重命名的过程,具体的步骤如下面:

  1. 把current目录重名为remove.tmp

  2. 把previous目录重命名为current

  3. 删除remove.tmp目录

到此时,HDFS升级的细节讨论的差不多,对HDFS能成功升级也抱有信心,当然能安全回滚也不是问题,当HDFS程序正确的执行完升级各步骤后,还需要手工的执行

hadoop dfsadmin -upgradeFinallized
让HDFS删除升级之前到文件,这个命令执行完后,也就不能回滚到以前的HDFS版本了。
原文:http://my.oschina.net/yishanhu/blog/406996

转载请保留固定链接: https://linuxeye.com/Linux/2463.html

------分隔线----------------------------
标签:HDFS
栏目列表
推荐内容