HDFS的升级管理

Wesley13
• 阅读 600

升级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://en.wikipedia.org/wiki/Hard\_link
http://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-hdfs/HdfsUserGuide.html#Upgrade\_and\_Rollback
git://hadoop.apache.org/hadoop.git

原创博客,转载敬请注明@仪山湖!

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Hadoop2.7.3完全分布式集群安装过程
需要安装的软件Hadoop包含HDFS集群和YARN集群。部署Hadoop就是部署HDFS和YARN集群。机器数量、角色4台。NameNode1台、DataNode3台主机名IP角色amaster192.168.37.143NameNode:9000ResourceManag
Wesley13 Wesley13
2年前
HDFS是什么?为什么要使用它?
hdfs是hadoop的文件管理系统,主要有两部分:一个是NameNode:主要作用是负责接收客户端求,告知datanode的blk存储位置,维护hdfs的目录树;另一个是DateNode:主要是把文件分割成blk存储在不同的服务器上,并且负责产生副本,也能定期向NameNode汇报blk的情况;使用hdfs的好处是提高系统的高可用性:因为hdf
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
1个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这