HBase-数据恢复

3月25日(周二)
最开始就是namenode启动不了,感觉很奇怪。
网上给了好几种解决办法,其中包含修改代码
按照这个思路将namenode启动了,但是发现hbase的master启动有问题,master做了日志切分,然后一会就挂掉了,启动了几次
都是这样,后来我想是不是可以将日志切分那段给停止住,然后等所有的region server处理完了,这段也就可以跳过去了。于是加了断点,让master执行切分,发现这样仍然不行,master还是会挂掉。
感觉这个问题非常棘手,找了数据平台的来帮忙
用fsck检查了hbase表,发现有很多损坏的块,然后怀疑是不是掉电引起的故障
后来发现.logs里面也有很多损坏的块,然后大致推测master启动不了可能是.logs里面的有很多坏块导致的。
将这些坏块先move到备份目录再启动,master和region server都可以启动了,但是发现没有META表,只有root表,kvdb表也认不出来了。
后来跟又找了咨询了情况,尝试将META表的info数据先移动走,再用修复工具修复试试。
发现仍然是修复不了,修复了很长时间,只修复了3个块。
之后他们说解决不了,又找了微博的人,开始分析问题
首先是定位namenode为什么起不了,原因是hadoop的一个bug,在创建父目录的时候有问题,于是父目录下的文件就创建不了,但是也没抛错,而是返回了null,也就是我们一开始看到的那个空指针异常。
我们将这些创建失败的文件打印出来,发现都是一些tmp文件,可以忽略掉,于是修改代码,将hadoop的代码修改后,直接替换jar包,然后部署上去,这下namenode就可以启动了。
启动后发现block的数量似乎不对,然后查看日志,有很多deleting的信息,大致估计是有很多数据块被删除了,这个也是低版本hadoop的一个bug,高版本就解决了。我们决定先将集群变只读,然后现有的部分数据先拷贝到新集群上,临时提供服务,之后
的数据再慢慢导入进去,并将之前的namenode image镜像保存
晚上一起处理故障, 将原先的数据rsync到新集群
我们没有用dd拷贝,因为hadoop数据下的文件并不多,有几千个,用rsync就可以了。



3月26日(周三)
早上数据已经导入完成,过来帮忙分析问题
因为新环境的datanode少了很多数据
所以hbase启不来,将那些有问题的block 移动到备份目录中 lost+found,这个这个版本
有一个bug,移动到这个目录下的数据变成文件了
再启动,可以起来了,但是只有ROOT表,没有META表,META表修复有问题
后来发现是有region空洞,就是有一些region虽然存在,但是里面已经没有数据了
而这个regioin下的.regioninfo文件却存在,这个文件保存了当前region的start key和end key,相当于真是的数据(HFile没了),但是指向这些数据的引用还在,所以修复META表就不对了。
将这些空洞的文件删除,然后再修复META表
用 -repair 和 -repairHoles 两个命令
这样hbase最终就正常了,大概在下午1:30左右开始对外提供服务了。
不过后来我发现修复工具可能没有100%的修复,貌似还是有些region有空洞
数据平台的人提供了一个数据恢复工具,extundelete
晚上开始用 extundelete做文件系统恢复



3月27日(周四)
已经将恢复的文件找到了 40% -70%不等,有4台机器
10.67.21.26 在/mnt/yq2126-restore/RECOVERED_FILES 中,恢复数量/被删除数 
量2830/6350 比率44.5%
10.67.21.27 在/mnt/yq2127-restore/RECOVERED_FILES 中 恢复数量/被删除数量 
2290/3828 比率59.8%
10.67.15.146 在/mnt/yq146-restore/RECOVERED_FILES 中 恢复数量/被删除数量 
3464/5008 比率69.2%
10.67.15.147 在/mnt/yq147-restore/RECOVERED_FILES 中 恢复数量/被删除数量 
3522/4978 比率70.7%
26,27,28,146,147,恢复的数据放在各自的/mnt目录下
因为启动namenode时,可能会将映射不正确的数据删除,所以将全部数据备份了
之后发现少了一台机器28,又做了恢复
恢复的文件中都是纯block,几乎没有meta文件,所以启动namenode显示block数为0
meta的作为校验用的,可以忽略,
比如一个block为:
blk_1816460560686614455
那么对应的meta文件就是:
blk_1816460560686614455_457634.meta
可以看到meta文件的规则就是block后面加了一个随机数,如果没有meta,则不认block,有这么几种办法可以忽略掉meta:
1.我们手动拷贝一个正确的meta快,namenode的image中保存了meta的的文件名,我们将所有的meta文件名都改
成_999999.meta结尾
2.修改代码,代码中有校验meta的部分,将这块给去掉
3.代码中如果找不到block对应的meta,则默认找_1.meta这个文件,
我们使用了第三种方案,批量产生对应的_1.meta文件
此时namenode就能认出block了,但是get到本地还是有问题,默认情况下get到本地是要CRC校验的,现在使用下面这个命令即可:(即加一个忽略参数)
/usr/local/server/hadoop-1.0.4/bin/hadoop dfs -get -ignoreCrc /hbase/kvdb/db7b5ced84023d12fc61bdde54bed4a2/.regioninfo /tmp/



3月28日(周五)
如果datanode的current目录下没有VERSION,namenode会将datanode的数据格式化,也就是删除所有数据,所以拷贝的时候应该放一个VERSION,一个机器多个目录的VERSION内容可以一样
批量产生了对应的_1.meta文件,namenode认出了所有block,然后get所有的完整HFile,并将这些HFile put到远端集群
命令:
/usr/local/server/hadoop-1.0.4/bin/hadoop dfs -put /data0/fix/c1a7d1a5aef94287ba5cc73b91b0b08e hdfs://10.67.21.29:8020/tmp/fix/value
注意这里有一个bug,远端集群配置的是ip,这里必须写ip,同时必须写上端口号
再将这些数据通过blukload导入到线上kvdb表
/data0/server/hbase-0.94.7/bin/hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles hdfs://10.67.21.29:8020/tmp/fix kvdb
这里要注意,导入的目录必须包含 value,也就是kvdb表的family,导入的路径是value的父目录
如果数据再/tmp/fix/value中,则导入目录是/tmp/fix即可
大量数据put到远端集群,网卡会跑慢,最快也要10秒1G,所以很慢
晚上12点将所有完整的HFile导入到线上集群,一共347G



3月29日(周六)
开始研究恢复不完整的HFile
写程序读取一个最简单的HFile,可以读取一条数据
所谓的不完整的HFile可以这么理解:
假设一个hadoop的block大小为100M
如果我有一个HFile是250M的,那么肯定会被分成三个block,
第一个100M,第二个100M,第三个50M
但是如果第一个block找不到了,只有第二个和第三个block,这样整个HFile就不完整了,直接用HFile的API是无法读取的,但是这两个block中还是有数据的,得找到一种方式能直接分析这个不完整的HFile block,然后将里面的数据一条一条读出来,然后再写成一个完整的HFile,写成一个完整的HFile可以利用现有的API,这个比较简单,难点就在如何正确的读取这些block。
一个HFile的格式如下(v2版本)
我们需要将绿色部门的数据块 DATA BLOCK读取出来,并重新生成一个完整的HFile即可



3月30日(周日)
完善程序,之前的程序没有对索引块处理,索引块是需要过滤掉的
包括叶索引和中间索引
之后可以读取一个复杂的完整HFile,并重新写入,校验正确
我们的数据是没有压缩的,也没有bloom过滤器,这样就很好了,分析文件的复杂度要小很多,
但是代码的逻辑变化不大
HFile的文件块中包含了各种类型的文件块,每个类型的块开头都是8个字节的魔术字表示
有这么一些:
数据块 DATABLK*
压缩的数据块 DATABLKE
叶索引块 IDXLEAF2
bloom数据块 METABLKc
中间层索引块 IDXINTE2
根索数据索引块 IDXROOT2
根元数据索引块 IDXROOT2 (元数据和根数据索引块莫数字都一样)
此外还有文件信息块,bloom元数据块,尾索引块等。
首先要解析到第一个数据块 DATABLK,也就是直接匹配二进制文件,扫描是否满足DATABLK* 这八个字节,如果满足就认为是一个数据块。
但是需要注意,如果用户的数据中也包含了这个数据,那就不对了。
块开头8个魔术字之后,是4个字节的压缩数据长度,和4个字节的未压缩数据长度,以及8个字节的上一个块的偏移量。
我们可以利用这三个值进行判断,我们的数据是未压缩的,所以压缩数据长度和未压缩数据长度这两个值是相当的,另外一个数据块的默认值是64K(我们使用的是默认值),数据块的长度可能超过64K,但是不可能低于64。
另外 上一个块的偏移量,这个值肯定是小于当前读取文件的position,也肯定小于整个文件的长度(也有一些特殊的情况,这个值会为-1或者0)。
所以经过这三个值的判断,可以大致当前的块是否是一个合法的块。
如果是一个合法的块,就将这个块读出来,然后再做遍历分析。如果不合法,无法解析,那就记录这个块,然后跳过,再分析后面的。



3月31日(周一)
完善程序,对迷你奇迹应用分析
发现程序还是有很多不完善的地方
主要是对key,value的细节判断处理
增加了很多数据校验,如果碰到错误的数据,则跳过这个block
比如,我们的fmaily长度是固定的,肯定是5个字节,并且为value,如果不满足则跳过,
另外qualifier长度肯定为1,并且不是v就是f
时间戳我没有做判断,有一些数据的时间戳是0,就是我们之前做数据迁移的时候插入的
key的长度也有限制,必须是大于11个字节(10个字节的accesskey和1个字节的下划线)
value长度可能为0,但是应该小于2G(这个是hbase的限制)
另外还有1个字节的KeyValue的类型,这个类型是写入的时候做的检查,这里也可以做判断
有了这么一些判断之后,一条KeyValue就比较完整了。
目前写出的程序虽然可以正确读不完整的HFile了,但是写入的时候不知道怎么排序,所以实现了自定义的排序接口,然后采用了不排序的方式,这样虽然写入的HFile格式是对的,但是内容是乱序的,也是有问题
解决办法比较麻烦:
先写入一个不排序的HFile,再将这个HFile读出来,每读一个KeyValue再插入到一个测试集群,由测试集群的RegionServer来完成排序工作,这样就可以生成一个正确的且排序的HFile了。
采用跟之前一个的put到远端集群,再做bulkload就可以恢复数据了。



4月1日(周二)
对KeyValue判断的时候只获取类型为Put的KeyValue,然后保存为新的HFile
实现分析程序基本上已经完善了,需要一个不完整的HFile列表,这个列表可以这样取得:
全部的hfile文件列表在 /usr/local/server/hadoop-1.0.4/bin kvdb.txt
对每个文件路径
/hbase/kvdb/fe71c16b249aeec588d68ab682d2853f/value/5db31aac286d43c58e853d4f78aebe89
执行一遍:
./hadoop fsck /hbase/kvdb/ff29a10da08b3518fae79b0984466024/value/ecf528d281b34aaa9cf7f4bfd46ef76d
检查 Corrupt blocks: 大于0的文件
如果大于0, 并且这个值和 Total blocks (validated): 相当,就是完全损坏的块,丢弃
否则就是那些有些损坏的块
根据 Under replicated 关键字,找到后面的block名字,如:
/hbase/kvdb/ff29a10da08b3518fae79b0984466024/value/ecf528d281b34aaa9cf7f4bfd46ef76d: Under replicated blk_6168151387131741601_953148. Target Replicas is 3 but found 1 replica(s).
也就是blk_6168151387131741601_953148,最后将_953148去掉,因为这个是meta块,我们需要找的是数据块
blk_6168151387131741601 才是真正需要处理的数据块,
拿到这个数据块名字后
去/data/backup 下find 一下,找到这个block的具体路径,如:
/data0/backup/yq147.kvdb/current/subdir14/subdir10/blk_8746131526468930686 这样的绝对路径
最后将这些文件放到一个目录下,用程序跑一遍,生成完整的HFile即可
中午XX提供了一份不完整的HFile的block列表,根据这个列表,做批量恢复一共188G
完善程序,找到排序方式了,保存的时候HBase有一个比较的类,是对二进制内容比较
写入的时候也有一个比较类,但是都是用相同的compare实现,是KeyValue的内部类
这样就省去了写入再读取,再插入到测试集群,可以节省很多时间
对迷你奇迹应用进行恢复
最终全部恢复完成,导入到线上



4月2日(周三)
完善程序,恢复A和B两个重要客户剩余的小部分数据
XX测试数据完整性,将所有不完整的HFile导入线上集群
恢复A数据356M
恢复B数据1.2G
恢复了684个文件一共75G
恢复了剩余1000个block,一共113G
这些恢复完了,剩下都是一些边边角角的了,总体工作到这里基本上就快完成了
上面的完成之后还没有真正结束,还有一些 .logs文件需要恢复,.logs文件相当要好恢复的多,默认配置的.logs文件不会超过一个hadoop 文件块大小(我们用的是默认值),而且这个.logs文件结构相比HFile要简单的多,跟上面的处理过程一样,读取.logs文件,然后生成一个未排序的HFile,再读取这个HFile,将keyvalue 保存到测试集群,再从测试集群中将这些数据get到本地,put到线上集群,导入到kvdb表中,整个 .logs文件恢复完毕。



4月3日(周四)
解析所有.log文件,但是导入有问题
原来是hbase的bug,meta表中有空洞就会出现循环,无法导入
于是将这些文件先get到本地,然后先读HFile,每读取一个,再插入到远端集群
上周五批量导入的那些文件中,还有2.4G的文件没有导入,这些没有导入的文件,也是因为META空洞导致的,也是一样遍历读取每个文件的KeyValue,然后一个个的插入到线上集群
周三完成批量恢复的不完整HFile同样也有这个问题,数据大概在9.15G左右 ,最终也是批量读取再导入,不过这个导入是很慢的,是用HBase的客户端一条一条的插入。



4月4日(周五)
剩下的都是一些扫尾的工作
log文件有些插入的时候有问题,
因为log文件是对整个hbase的记录,不光是kvdb表,所以有些key实际是插入到META表甚至ROOT表中的
这些key要全部忽略掉,从错误日志中看到有一些key是 METAROW
忽略掉这些key就可以了
最初XXX这个应用不不需要恢复了,所以讲他的完整HFile,全部备份了,因为这写HFile中不光有XXX的keyvalue,所以再读取一遍,然后将非XXX应用的key都插入到kvdb中,数据很少,只有600多K。
处理A和B两个客户时剩余了接近900K的数据,这些小数据很难处理了,先备份起来了
另外处理不完整HFile时,又有322M的数据有问题,有3000多个小文件,一共这么多。这批文件都非常难处理了,只能先保留了。这些文件放在 swift.saet 20的 /root/very_hard的目录下
出事那天的hbase kvdb表的数据总量是有精确记录的,是1393G,到目前为止恢复后数据总量是1415G
看起来是全部恢复了?但是未必
肯定是丢了一些,之所以现在的数据比以前多,可能是有这么几点导致的
1.恢复后的数据,因为格式原因,比原先的大,比如log文件生成前有8G,变成HFile就变成30G了
2.在这段时间内,可能有更多的数据被插入,甚至可能有1500G,所以1415这个数字不能代表就恢复了。
整个恢复过程花费了很多时间,大体上分完整HFile恢复和不完整HFile恢复,以及一些扫尾工作,整个恢复过程中,碰到了4个hadoop和hbase的bug。
完整HFile恢复相对简单,这个恢复时间比较慢了,到周五晚上才恢复,还是以前处经验不足导致的
不完整HFile恢复时间更长,这其中花了3天完善程序,因为没有现有的工具,只能一点点分析,所以时间很长,如果有工具的话,修复时间应该和完整HFile时间差不多。
扫尾工作是对上面两个中一些没有导入的HFile再做导入,以及log文件的恢复,这些没有导入的文件,因为HBase的bug,不能用bulkload批量导入,只能一条一条插入,所以比较慢,好在文件很少,log文件比HFile文件格式简单很多,但是一些未知的情况(比如里面有插入到META表中的key)没有考虑进去,所以比预计的时间又长了。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值