DataNode BlockReport bug分析(一)

一、现象描述

       上周2.4集群迁移升级,使用平滑升级将中心机从较差的几台机器迁移到真正的中心机服务器上,升级前原始中心机一切状态都是正常的,不存在丢块等问题,将image等文件拷贝到新集群并启动一起正常,随后将所有DataNode重启,将ns地址换位新中心机节点地址,汇报所有block,猛然发现竟然后18个missing block。回滚后,重新将ns地址位置换位老中心机节点地址,重启所有DataNode发现竟然也有missing block,没有办法只能将ns地址还是换为新中心机地址,重启DataNode,继续向下升级。

       第二天去调查这个问题,可以肯定的是原先中心机内存中blockmap肯定是不正确的,深入调查这个问题,从文件入手,看块的分布,得到了以下情况分(只分析一个文件中的一个block)。

       1、9月30号,block被写入,三个副本,分别写入到A,B,C三台机器,一切正常,pipeline无异常,block 文件写入datanode没有问题,说明在这一时刻没有任何问题。

                                                Miss1

       2、10月10号问题产生了

       机器A  /data10在13:00磁盘损坏,机器B,机器C由于服务器硬件问题,分别在13:57和14:15下线。按照以前的方式,这种方式没有任何问题,每下线一台服务器都会等足够的时间,使namenode将under  replica给repicate出去,保证3副本的稳定性。

       然而机器B、C下线后,namenode一直认为机器A是正常的,并且反复要求机器A去replicate block。A机器由于磁盘损坏,没有该Block反复报错。

                                               Miss2

                                                   Miss3

       至此,该Block丢失,但是NameNode仍然认为机器A保留该块,产生了严重的问题。

 

 

二、产生原因

       首先看一下DataNode BlockReport及Disk Error处理流程。DataNode默认第一次启动以及稍后的每六个小时向NameNode汇报一次全量数据,其它时候只汇报增量数据。

       Block Report流程为: 

       1、BPServiceActor读取每个盘的所有Block数据,包括finalized和underConstrunction

                                         Miss4

       2、如果block过多(超过1M),会按照磁盘分批发送,否则一次发送 

                                          Miss5

一般不会超过1M,所以都是一次发送,但是注意,blockReport方法第三个参数reports是一个数组,表示每个磁盘对应的不同storageId所有的block。

       如果磁盘没有问题的话这个逻辑没有什么问题,但是,假如磁盘除了问题,同时设置了dfs.datanode.failed.volumes.tolerated这个参数,就出现了问题。

       dfs.datanode.failed.volumes.tolerated是DataNode可容忍出错的磁盘数,默认是0,它指的是当磁盘损坏多少块时,DataNode 下线,停止提供服务。在我们集群中,这一参数设置为3,即损失3块磁盘仍然能够提供服务。

       为了继续深入研究这个问题,还需要看一下Disk Error的处理流程。对于Hadoop集群,磁盘损坏是非常正常的,较大的集群一天损坏2、3块盘都是可以忍受的。 

       DataNode当接收或者发送Block出现异常时候都会对磁盘进行检查,检查的方法是

                              Miss6

       随后会对每个磁盘进行检查,首先对出现问题的磁盘从FsVolumeList去掉,同时从内存中Block对应存储的映射volumeMap清除。

随后会立即做一次BlockReport,让NameNode迅速将掉的盘中的Block通过别的机器replicate出去。

 

       NameNode中处理Block Report的主要方法是:

                                                Miss7

       参数解释为dn是当前DataNode,storage对应汇报的磁盘,newReport是该DataNode得对应磁盘汇报的所有block,剩下的是经过diff后需要处理的相应block。

       首先看一下0.20.203版本时期的report: 

                              Miss8

 

       DataNode汇报所有Block,NameNode将汇报的所有Block与内存中数据比较,如果多余内存中数据并且该block属于某个INode,则加入ToAdd中,稍后加入blockMap中;如果不属于任何INode,则加入ToRemove,放入invalidateSet中,稍后让DataNode进行删除。如果内存中数据有汇报不存在的Block,则加入ToRemove中,从blockMap中删除该节点对应Block得映射,同时更新neededBlock,即需要replicate的block列表,随后让其他存有该节点的Block将该block repicate出去。

       然而,2.4中并不是全量进行处理,而是按照磁盘处理,这样处理主要是为了异构存储而设计的。这就出现了问题,由于磁盘故障后该磁盘Volume会被清除,这样就不会汇报该Volume得任何信息了。这个磁盘就不会调用reportDiff去更新NameNode中的数据结构,这样NameNode会一直认为这个节点中其实由于磁盘故障而丢失的Block是一直存在的。就出现了我一开始提到的问题。