现在的位置: 首页 > 综合 > 正文

Apache Hadoop2.0之HDFS均衡操作分析

2013年03月01日 ⁄ 综合 ⁄ 共 13268字 ⁄ 字号 评论关闭

 

1 HDFS均衡操作原理

HDFS默认的块的副本存放策略是在发起请求的客户端存放一个副本,如果这个客户端在集群以外,那就选择一个不是太忙,存储不是太满的节点来存放,第二个副本放在与第一个副本相同的机架但是不同节点上,第三个放在与第二个和第一个副本不同的机架上,原则是尽量避免在相同的机架上放太多的副本。

随着时间的推移,在各个DataNode节点上的数据块会分布的越来越不均衡。如果集群不均衡的程度很严重,会降低Mapreduce的使用性能,导致部分DataNode节点相对而言变得更加繁忙。所以,应该尽量的避免出现这种情况。

HDFSBalancer类,是为了实现HDFS的负载调整而存在的。Balancer类是以一个独立的进程存在的,可以独立的运行和配置。它NameNode节点进行通信,获取各个DataNode节点的负载状况,从而进行调整DataNode上的Block的分布。主要的调整其实就是一个操作,将一个数据块从一个服务器搬迁到另一个服务器上。Balancer会向相关的目标DataNode节点 发出一个DataTransferProtocol.OP_REPLACE_BLOCK 消息,接收到这个消息的DataNode节点,会将从源DataNode节点传输来的数据块写入本地,写成功后,通知NameNode,删除源DataNode上的同一个数据块,直到集群达到均衡为止,即每个DataNode的使用率(该节点已使用的空间和空间容量之间的百分比值)集群的使用率(集群中已使用的空间和集群的空间容量之间的百分比值)非常接近,差距不超过均衡时给定的阈值。

其中,一个块是否可以被移动,要满足三个条件:

(1)正在被移动或者已经被移动的块,不会重复移动

(2)一个块如果在源节点和目标节点上都有其副本,则此块不会被移动;

(3)移动不会减少一个块所在的机架的数目;

可见,由于上述等条件的限制,均衡操作并不能使得HDFS达到真正意义上的均衡,它只能是尽量的减少不均衡

均衡操作依靠一个均衡操作服务器、NameNode的代理和DataNode来实现,其逻辑流程如下:

其中,

Step1:RebalanceServer从Name Node中获取所有的Data Node情况,即每一个Data Node磁盘使用情况;

Step2: RebalanceServer计算哪些Dataode节点需要将数据移动,哪些Dataode节点可以接受移动的块数据,并且从NameNode中获取需要移动的数据分布情况;

Step3: RebalanceServer计算出来可以将哪一台Dataode节点的block移动到另一台机器中去;

Step4、5、6需要移动block的Dataode节点将数据移动到目标DataNode节点上去,同时删除自己节点上的block数据;

Step7: RebalanceServer获取到本次数据移动的执行结果,并继续执行这个过程,一直到没有数据可以移动或者HDFS集群以及达到了平衡的标准为止;

    step2中,HDFS会把当前的DataNode节点根据阈值的设定情况划分到四个链表中:

(1)over组:此组中的DataNode的均满足

DataNode_usedSpace_percent  > Cluster_usedSpace_percent+ threshold

(2)above组:此组中的DataNode的均满足

Cluster_usedSpace_percent+ threshold> DataNode_ usedSpace _percent  > Cluster_usedSpace_percent

 (3)below组:此组中的DataNode的均满足

Cluster_usedSpace_percent> DataNode_ usedSpace_percent  > Cluster_
usedSpace
_percent - threshold

 (4)under组:此组中的DataNode的均满足

Cluster_usedSpace_percent - threshold> DataNode_usedSpace_percent

    用一个示例图表示:

在移动块的时候,会把over组和above组中的块向below组和under组移动,直到均衡状态或者达到均衡退出的条件为止。

总得来说,均衡操作的步骤可以分为4步:

(1)从namenode获取datanode磁盘使用情况;

(2)计算哪些节点需要把哪些数据移动到哪里;

(3)分别移动,完成后删除旧的block信息;

(4)循环执行,直到达到平衡标准;

2 HDFS均衡操作的启动

    使用HDFS的balancer命令,可以配置一个Threshold来平衡每一个DataNode磁盘利用率。命令如下:

start-balancer.sh -threshold 8

运行之后,会有Balancer进程出现:

上述命令设置了Threshold为8%,那么执行balancer命令的时候,首先统计所有DataNode的磁盘利用率的均值,然后判断如果某一个DataNode的磁盘利用率超过这个均值Threshold,那么将会把这个DataNode的block转移到磁盘利用率低的DataNode,这对于新节点的加入来说十分有用。Threshold的值为1到100之间,不显示的进行参数设置的话,默认是10。

范围超出之后,会有异常抛出:

java.lang.IllegalArgumentException: Numberout of range: threshold = 0.07

     atorg.apache.hadoop.hdfs.server.balancer.Balancer$Cli.parse(Balancer.java:1535)

     atorg.apache.hadoop.hdfs.server.balancer.Balancer$Cli.run(Balancer.java:1510)

     atorg.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)

     atorg.apache.hadoop.hdfs.server.balancer.Balancer.main(Balancer.java:1582)

2012-12-19 16:28:33,299 ERRORorg.apache.hadoop.hdfs.server.balancer.Balancer: Exiting balancer due anexception

java.lang.IllegalArgumentException: Numberout of range: threshold = 110.0

     atorg.apache.hadoop.hdfs.server.balancer.Balancer$Cli.parse(Balancer.java:1535)

     atorg.apache.hadoop.hdfs.server.balancer.Balancer$Cli.run(Balancer.java:1510)

     atorg.apache.hadoop.util.ToolRunner.run(ToolRunner.java:70)

     atorg.apache.hadoop.hdfs.server.balancer.Balancer.main(Balancer.java:1582)

如果参数值设置的越小,花费的时间就越长。使用此命令时,会反复的从磁盘使用率高的节点上,把块转移到磁盘使用率低的磁盘上,每次移动不超过10G大小,每次移动不超过20分钟。

在做均衡的时候,会对网络带宽有影响,可在配置文件中对均衡操作的带宽做限制:

<property>

   <name>dfs.balance.bandwidthPerSec</name>

    <value>1048576</value>

<description> 

Specifies themaximum bandwidth that each datanode can utilize for the balancing purpose interm of the number of bytes per second.

    </description>

</property>

若不设置,则balance操作时,速度默认为1M/S大小。参数重启时生效。不允许在集群中使用多个均衡同时操作。

3 HDFS均衡操作的退出

    除了在命令行直接使用stop-balancer.sh脚本来执行退出均衡操作之外,当发生以下几种情况时,当前执行的均衡操作也会退出:

(1)集群已经达到均衡状态;

(2)没有块可以再被移动;

(3)连续五次迭代操作时没有块移动;

(4)和NameNode通信时出现IOException;

(5)另外一个均衡操作启动;

4 实例分析

均衡操作之前DataNode节点块分布情况:

在sbin目录下执行命令./start-balancer.sh -threshold 5

均衡过程中:

开始做均衡操作时,会有如下日志打印出:

2012-12-2810:08:55,667 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Using athreshold of 5.0

2012-12-2810:08:55,668 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: namenodes =[hdfs://goon]

2012-12-2810:08:55,669 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: p        = Balancer.Parameters[BalancingPolicy.Node, threshold=5.0]

其中会标明对哪些nameservice进行均衡,同时对输入的参数进行说明,默认的策略是BalancingPolicy.Node,表示均衡的对象是DataNode,否则是对块池做均衡。

之后,会计算需要移动的块,移动的字节数,块的源地址和目标地址:

2012-12-2810:08:57,200 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: 1over-utilized: [Source[10.28.169.126:50010, utilization=25.807874799394025]]

2012-12-2810:08:57,200 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: 1underutilized: [BalancerDatanode[10.28.169.122:50010, utilization=9.395091359283992]]

2012-12-2810:08:57,201 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Need to move2.75 GB to make the cluster balanced.

2012-12-2810:08:57,202 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Decided tomove 2.46 GB bytes from 10.28.169.126:50010 to 10.28.169.122:50010

2012-12-2810:08:57,203 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Will move2.46 GB in this iteration

此时:

刚开始均衡操作时,其进程所占资源:

同时,可以观察到会有块的移动,从datanode0移动到sdc2和Tdatanode0,在NN会有如下日志(hadoop-hdfs-balancer-Tdatanode0.log):

2012-12-2810:08:57,724 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Moving block365766470964968392 from 10.28.169.126:50010 to 10.28.169.122:50010 through10.28.169.225:50010 is succeeded.

2012-12-2810:08:57,724 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Moving block7346867188539736438 from 10.28.169.126:50010 to 10.28.169.122:50010 through10.28.169.225:50010 is succeeded.

2012-12-2810:08:57,724 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: Moving block-6882244909405313690 from 10.28.169.126:50010 to 10.28.169.122:50010 through10.28.169.225:50010 is succeeded.

由于在Balancer类中对线程池做了限制,

final static privateint MOVER_THREAD_POOL_SIZE = 1000;

所以最多可以有1000个线程并发块移动操作,但是向一个DN进行移动块时,最多5个块并发移动。

由于机器资源的限制,当均衡操作创建的线程数量达到900多的时候,就无法再创建线程,并有如下错误:

2012-12-2810:19:32,905 WARN org.apache.hadoop.hdfs.server.balancer.Balancer: Dispatcherthread failed

java.lang.OutOfMemoryError:unable to create new native thread

       at java.lang.Thread.start0(Native Method)

       at java.lang.Thread.start(Thread.java:640)

       atjava.util.concurrent.ThreadPoolExecutor.addIfUnderCorePoolSize(ThreadPoolExecutor.java:703)

       atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:652)

       atorg.apache.hadoop.hdfs.server.balancer.Balancer$PendingBlockMove.scheduleBlockMove(Balancer.java:402)

       atorg.apache.hadoop.hdfs.server.balancer.Balancer$PendingBlockMove.access$3500(Balancer.java:236)

       atorg.apache.hadoop.hdfs.server.balancer.Balancer$Source.dispatchBlocks(Balancer.java:746)

       at org.apache.hadoop.hdfs.server.balancer.Balancer$Source.access$2000(Balancer.java:591)

       atorg.apache.hadoop.hdfs.server.balancer.Balancer$Source$BlockMoveDispatcher.run(Balancer.java:598)

       atjava.util.concurrent.Executors$RunnableAdapter.call(Executors.java:441)

       at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)

       atjava.util.concurrent.FutureTask.run(FutureTask.java:138)

       atjava.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)

       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)

       at java.lang.Thread.run(Thread.java:662)

 

查看均衡操作所产生的线程会被阻塞:

Thread15688: (state = BLOCKED)

 -sun.misc.Unsafe.park(boolean, long) @bci=0 (Interpreted frame)

 -java.util.concurrent.locks.LockSupport.park(java.lang.Object) @bci=14, line=156(Interpreted frame)

 -java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await()@bci=42, line=1987 (Interpreted frame)

 -java.util.concurrent.LinkedBlockingQueue.take() @bci=29, line=399 (Interpretedframe)

 -java.util.concurrent.ThreadPoolExecutor.getTask() @bci=78, line=947(Interpreted frame)

 -java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=18, line=907(Interpreted frame)

 -java.lang.Thread.run() @bci=11, line=662 (Interpreted frame)

 

 

Thread15687: (state = BLOCKED)

 -sun.misc.Unsafe.park(boolean, long) @bci=0 (Interpreted frame)

 -java.util.concurrent.locks.LockSupport.park(java.lang.Object) @bci=14, line=156(Interpreted frame)

 -java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await()@bci=42, line=1987 (Interpreted frame)

 -java.util.concurrent.LinkedBlockingQueue.take() @bci=29, line=399 (Interpretedframe)

 -java.util.concurrent.ThreadPoolExecutor.getTask() @bci=78, line=947 (Interpretedframe)

 -java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=18, line=907(Interpreted frame)

 -java.lang.Thread.run() @bci=11, line=662 (Interpreted frame)

    

同时,由于无法创建线程,RPC通信也会阻塞,

2012-12-2810:37:28,710 WARN org.apache.hadoop.io.retry.RetryInvocationHandler: Exceptionwhile invoking renewLease of class ClientNamenodeProtocolTranslatorPB. Tryingto fail over immediately.

2012-12-2810:37:28,716 WARN org.apache.hadoop.io.retry.RetryInvocationHandler: Exceptionwhile invoking renewLease of class ClientNamenodeProtocolTranslatorPB after 1fail over attempts. Trying to fail over immediately.

2012-12-2810:37:28,721 WARN org.apache.hadoop.io.retry.RetryInvocationHandler: Exceptionwhile invoking renewLease of class ClientNamenodeProtocolTranslatorPB after 2fail over attempts. Trying to fail over immediately.

。。。

。。。

2012-12-2810:37:28,795 WARN org.apache.hadoop.io.retry.RetryInvocationHandler: Exceptionwhile invoking renewLease of class ClientNamenodeProtocolTranslatorPB after 14fail over attempts. Trying to fail over immediately.

2012-12-2810:37:28,802 WARN org.apache.hadoop.io.retry.RetryInvocationHandler: Exceptionwhile invoking class org.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.renewLease.Not retrying because failovers
(15) exceeded maximum allowed (15)

java.io.IOException:Failed on local exception: java.io.IOException: Couldn't set up IO streams;Host Details : local host is: "Tdatanode0/10.28.169.126"; destinationhost is: "sdc1":9000;

       atorg.apache.hadoop.net.NetUtils.wrapException(NetUtils.java:760)

       at org.apache.hadoop.ipc.Client.call(Client.java:1168)

       atorg.apache.hadoop.ipc.ProtobufRpcEngine$Invoker.invoke(ProtobufRpcEngine.java:202)

       at $Proxy11.renewLease(Unknown Source)

       atorg.apache.hadoop.hdfs.protocolPB.ClientNamenodeProtocolTranslatorPB.renewLease(ClientNamenodeProtocolTranslatorPB.java:452)

       at sun.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)

       atsun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)

       at java.lang.reflect.Method.invoke(Method.java:597)

       atorg.apache.hadoop.io.retry.RetryInvocationHandler.invokeMethod(RetryInvocationHandler.java:164)

       atorg.apache.hadoop.io.retry.RetryInvocationHandler.invoke(RetryInvocationHandler.java:83)

       at $Proxy12.renewLease(Unknown Source)

       atorg.apache.hadoop.hdfs.DFSClient.renewLease(DFSClient.java:613)

       at org.apache.hadoop.hdfs.LeaseRenewer.renew(LeaseRenewer.java:411)

       atorg.apache.hadoop.hdfs.LeaseRenewer.run(LeaseRenewer.java:436)

       atorg.apache.hadoop.hdfs.LeaseRenewer.access$700(LeaseRenewer.java:70)

       at org.apache.hadoop.hdfs.LeaseRenewer$1.run(LeaseRenewer.java:297)

       at java.lang.Thread.run(Thread.java:662)

Causedby: java.io.IOException: Couldn't set up IO streams

atorg.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:640)

       atorg.apache.hadoop.ipc.Client$Connection.access$1700(Client.java:220)

       atorg.apache.hadoop.ipc.Client.getConnection(Client.java:1217)

       at org.apache.hadoop.ipc.Client.call(Client.java:1144)

       ... 15 more

Causedby: java.lang.OutOfMemoryError: unable to create new native thread

       at java.lang.Thread.start0(Native Method)

       at java.lang.Thread.start(Thread.java:640)

       atorg.apache.hadoop.ipc.Client$Connection.setupIOstreams(Client.java:633)

       ... 18 more

     

对每个NameNode都会尝试两次连接 set up IO streams,周期性的进行连接请求,即NN1连接不成功(尝试连接2次),然后就去尝试连接NN2(若尝试连接2次,不成功,就再去连接NN1),每次尝试连接NN后,都会有15次的更新租约尝试,当然,连接不上NN,租约更新也是失效的。

同时,由于此均衡是在NN上操作的,此NN节点会出现僵死状态(在NN上进行均衡仅为测试用,真正使用时不会在NN上),通过web不能访问,因为操作不能再创建新的线程。并且,如果当前是在active节点上进行的操作,而且在配置文件中配置了允许HA自动切换,那么此时会发生HA自动切换,即当前的standby节点变为active节点。

可以在linux下更改参数进行扩大线程创建的数量:

更改之前:

 

通过命令ulimit-u 65535临时修改,或者通过修改配置文件永久修改:/etc/security/limits.conf

 

 

同时也可以修改栈空间所占的大小,默认是10240字节,可以通过ulimit -s 1024修改为1024字节,减少每个堆栈的使用空间,也可以增加线程的数量。

另外,在均衡过程中,Balancer的内存使用情况如下:

 

所占内存大概是140M左右。在运行过程中,会产生一些垃圾文件,因为节点硬件的限制,所以必须对缓存进行清理:

 

 

若出现socket连接异常,则块移动失败,会提示:

2012-12-2113:10:42,915 WARN org.apache.hadoop.hdfs.server.balancer.Balancer: Error movingblock -5503762968190078708 from 10.28.169.225:50010 to 10.28.169.122:50010through 10.28.169.225:50010: Connection reset

 

均衡之后:

当出现日志:

2012-12-2812:20:00,430 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:/default-rack/10.28.169.122:50010

2012-12-2812:20:00,431 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:/default-rack/10.28.169.225:50010

2012-12-2812:20:00,431 INFO org.apache.hadoop.net.NetworkTopology: Adding a new node:/default-rack/10.28.169.126:50010

2012-12-2812:20:00,431 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: 0over-utilized: []

2012-12-2812:20:00,431 INFO org.apache.hadoop.hdfs.server.balancer.Balancer: 0underutilized: []

 

均衡操作结束,因为Over组没有DataNode,Under组也没有DataNode,此时:

 

 

在均衡操作之前:        clusterAvg1

               =集群dfs已使用空间/集群总空间

               =(12.7+12.19+4.62)/(49.22+49.22+49.22)

               =19.99%

我们在做均衡时所设定的阈值为5,即百分之5,clusterAvg1-5%=14.99%,clusterAvg1+5%=24.99%,而Tdatanode0的空间使用率是25.81%,超过 24.99% ,属于over节点,sdc2的空间使用率是9.4%,低于14.99%,属于under节点,datanode0的空间使用率是24.76%,高于14.99%但是低于24.99%,属于above节点,符合均衡操作的发生条件

集群的空间平均使用率为:   clusterAvg2

               =集群dfs已使用空间/集群总空间

               =(12.7+11.46+8.91)/(49.22+49.22+49.22)

               =22.41%

我们在做均衡时所设定的阈值为5,即百分之5,clusterAvg2-5%=17.41%,clusterAvg2+5%=27.41%,所有的DN的空间使用率都在17.41%--27.41%中间,说明集群已经达到均衡状态


更过云计算相关的文章,请见:

http://www.ccplat.com/

抱歉!评论已关闭.