HDFS中的数据按照一定策略分布在集群中的多个数据节点上,但在某些情况下,数据的分布也会出现不均衡的情况,比如说集群新增加了节点,在新增加的节点上就没有数据存在,虽说之后新增的数据会分配到新节点上,不过,对于已有数据,新节点和原有节点上的分布很不均衡,而且这还会导致在分配MapReduce任务的时候新机器分配不到可执行的任务分配,白白浪费了新增节点的计算能力。而对于一个真实的生产环境来说,随着数据量的增加而对集群逐步扩容是一个很常见的场景,为了解决这个问题,Hadoop设计了Rebalance功能。

什么是balance

rebalance的目的是为了使数据在集群中各节点的分布尽量均衡,那么,什么样的情况被认为是不均衡,又需要达到什么样的目标才算是完成了rebalance呢?

简单来说,如果集群中没有“过载”或者“负载”的节点,则认为集群中的数据分布是均衡的,否则就是不均衡。所谓的“过载节点”是指存储使用率大于“平均存储使用率+允许偏差”的节点,“负载节点”是指存储使用率小于“平均存储使用率-允许偏差”的节点。这里又出现了几个概念,下面一一解释。

什么是一个节点的存储使用率?它表示一个数据节点上已用空间占可用空间的百分比,所谓可用空间指的是分配给HDFS可使用的空间,并非是节点所在机器的全部硬盘空间。比如,一个数据节点,共有存储空间2T,分配给HDFS的空间为1T,已经用了600G,那么使用率就是600/1000=60%。

将集群中各节点的存储使用率做个简单平均,就得到集群中节点的平均存储使用率。举例来说,假设有三个节点A,B,C,HDFS容量分别为2T,2T,1T,分别使用了50%,50%,10%,那么平均使用率是(50%+50%+10%)/3=36.7%,而不是(2*50%+2*50%+1*10%)/(2+2+1)=42%。

允许偏差,是启动Rebalance功能的时候指定的一个阈值,也是一个百分比,如果没有指定则默认为是10%,表示允许单个节点的存储使用率与集群中各节点平均存储使用率之间有10%的偏差。

Rebalance功能通过命令 hadoop balancer [-threshold] 来启动,直到集群中不再存在不均衡节点时自动停止。

Rebalance过程可以指定多次,每次可以指定不同的允许偏差值,以此来逐次渐进达到一个合理的数据均衡分布,同时又不至于使得Rebalance过程持续时间过长,影响集群的正常使用。

何时执行rebalance

rebalance 是一个非自动的管理功能,换句话说,它是由人工启动的。在任意一台能够连接到HDFS的机器上命令行下输入 hadoop balancer [-threshold]  既会启动。如果集群处于不平衡状态,这个过程就会在不平衡的节点之间迁移数据,如果rebalance过程没有被打断的话,完成此次rebalance目标后过程会自动停止。

仍以上面的例子来说,以允许偏差为10%启动rebalance(命令行下输入hadoop balancer),则Rebalance完成时各节点的存储使用率如下,A=B=45.825%,C=26.7%。

如果认为这样数据分布还不够均衡,那么可以再启动一次Rebalance。假设再启动一次Rebalance,仍不指定参数,也即仍以以10%的默认值为允许偏差。Rebalance前集群的平均存储使用率为45.825%×2+26.7%/3=39.45%。将各节点与之对比,可以看出C节点是负载节点(39.75-26.7=13.05 > 10)。输入hadoop balancer 等其结束,各节点的存储使用率为A=B=45.1375%,C=29.45%。 由这个过程可以看出,rebalance 的目的虽然是平衡数据,但它并不追求毕其功于一役,而是事先设定目标,每一次执行只实现预设目标,也即只是缩小了过载/负载节点与集群平均使用率的差值,而通过反复多次的执行来是集群内的数据逐渐趋于均衡。这样实际上是将rebalance 拆解成了许多小过程,每次小过程的执行时间都不会太长,对于一个应用中的集群来说,大多数时间需要运行业务任务,大多数的带宽也应该用于传输业务数据,这样做使得辅助管理功能对于资源的占用降低了,而且由于每次运行的时间片都不长,完全可以机动灵活的选择在集群的空闲期来rebalance,是一个非常优秀的设计。

rebalance过程是怎么执行的

上图展示了参与rebalance过程的各个角色之间的交互。绿色的线表示rebalance专用的信息交换协议,数字表示各步骤的执行顺序。rebalance server首先会向name node要求一个datanode 报告(步骤1)。得到报告之后选择了source和destination,再向namenode请求每个source的部分块映射(步骤2)。然后选择要移动的block,对于每一个block,会选择一个也有此block的数据节点作为代理源节点,被选作代理源的节点应该离destination更近,或者负载比source轻。然后命令代理源节点将这个block拷贝到destination节点上,同时示意该block应该从source节点上删除(步骤3)。代理源节点请求destination节点将该block拷贝到本地硬盘并重新放置(步骤4)。destination完成block的copy之后会提醒namenode从source节点上删除该block(步骤5)。namenode会选择该block的一个副本删除并保证删除不会使block丢失,不会使block的副本减少或者使其分布的机架数减少。然后destination会通知代理源节点block的放置已经完成(步骤6)。代理源节点再通知rebalance server操作完成(步骤7)。

rebalance server上的执行逻辑如下:

1、在输入启动命令的那台机器上会启动一个进程作为rebalance Server,为了避免给namenode带来过大的负担,整个rebalance过程由rebalance server而不是namenode来控制。 
2、rebalance server会向NameNode请求一份数据节点报告,在收到报告之后,使用获得的信息,计算出网络拓扑、集群平均存储使用率,然后把各个数据节点分成过载节点、负载节点、存储使用率高于平均水平的节点和低于平均水平的节点四类,再判断是否有节点处于过载和负载状态(也即过载节点列表和负载节点列表中是否有机器),如果是则继续,否则退出。如果判断可继续,则遍历过载节点列表和负载节点列表以生成Rebalance策略。生成rebalance策略的过程包括以下步骤:

a、选择数据移动的源节点和目的节点,选择依据如下

       对于负载节点,依据以下条件随机选取选取作为其source,条件优先级自上而下递减

  • 同一机架上的过载节点
  • 同一机架上的高于平均使用率的节点
  • 其他机架上的过载节点
  • 其他机架上的高于平均使用率的节

        对于过载节点,依据以下条件随机选取选取作为其destination,条件优先级自上而下递减

  • 同一机架上的负载节点
  • 同一机架上的低于平均使用率的节点
  • 其他机架上的负载节点
  • 其他机架上的低于平均使用率的节点

b、计算每一个source到每个destination要移动的数据量(注意以byte为单位而不是block)。 
如果source节点是过载节点,则看容积允许偏差值是否大于1GB,大于则取1GB,否则取允许偏差值。如果source只是高于平均使用率而没有达到过载的条件,则看该节点实际容积率与集群平均容积率之差是否大于2GB,大于取2GB,否则取前者。destination节点也如此计算。

3、Rebalance Server 向Name Node请求每个source节点的部分块分布报告(partial block report),请求的形式类似,默认size是1GB。所谓部分块报告,是指每次要求和返回的的只是加起来能满足size大小的block的信息,而非全部的block信息。

4、namenode随机挑选一些block,使得block的大小加起来等于请求中size的大小(见上一步,默认1GB),然后将被选中的block信息返回给rebalance server。

5、rebalance server 在返回的这些block信息中挑选出每个source上需要移动的block,直到选出的block的大小达到了前面提到过的阈值(见本节2.b中“如果source节点是过载节点……”一段)或者所有的block都被检查过了一遍。

a、选取待移动block的时候不能破坏block的分布原则,也即不能造成block丢失,不能使一个block的副本数变少,也不能使一个block放置的机架数变少。选取时依据的原则如下 
如果source和destination在不同的机架上,则destination所在的机架上不应该有待移动block的副本 
destination上不应该有待移动block的副本 
不能同时移动一个block的一个以上的个副本 
b、每个block的移动任务一旦确定就会被放入一个队列,然后把copy数据的请求发送给souce 
c、将队列中的任务按照source、destination和block分组,保证不能存在5个以上的同一source或者destination的任务,还要保证任意时刻一个block只能有一个副本在传输中 
d、当接收到source的确认信息后,一个任务才会从队列中移除,如果一个任务在队列中过长时间没有接收到反馈也会移除。

6、所有的block被扫描了一遍后,重复步骤3 
7、2中所有的移动计划已经完成,并且队列中没有任务之后,重复步骤2

datanode上执行的操作如下:
  1. 当某个datanode接收到OP_COPY命令的时候,会发送OP_REPLACE命令给destination节点并且开始传输block给destionation。
  2. block拷贝到destination之后,它会通知namenode,并提醒应该从source节点上移除该block的副本。namenode收到通知之后更新blockmap,如果这个block的副本数多于需要保存的副本数,则选择一个副本删除,选择副本的时候不能造成block丢失,不能使一个block的副本数变少,也不能使一个block放置的机架数变少。
  3. destination节点然后通知proxy source节点任务完成,proxy source节点再通知rebalance server。
  4. 每个数据节点只能将有限带宽用于rebalance,默认值是5MB/S。
  5. source和destination节点都存在资源限制,每个节点上正在进行的rebalance数据传输过程不能超过5个(包括发送和接收加起来不能超过5),在这个情况下,结合上面的条件4,也即每个传输过程的带宽限制是1MB/S。
  6. 在rebalance中的每一个sender和receiver也都有调节机制,通过所带的Throttler类的throttle( int numOfBytes )方法来实现。参数numOfBytes 表示自上次throttle方法被调用以来发送或者接收的字节数。该方法计算调用者的I/O比率,假如比率大过了带宽限制,线程就会休眠以降低数据传输速度,当它再次苏醒后又会依据已经变化的数据传输并发数来调整带宽限制。所以当每个数据块传输的时候Throttle() 都会周期性的调用。