1 HDFS均衡操作原理HDFS默认的块的副本存放策略是在发起请求的客户端存放一个副本,如果这个客户端在集群以外,那就选择一个不是太忙,存储不是太满的节点来存放,第二个副本放在与第一个副本相同的机架但是不同节点上,第三个放在与第二个和第一个副本不同的机架上,原则是尽量避免在相同的机架上放太多的副本。 随着时间的推移,在各个DataNode节点上的数据块会分布的越来越不均衡。如果集群不均衡的程度很严重,会降低Mapreduce的使用性能,导致部分DataNode节点相对而言变得更加繁忙。所以,应该尽量的避免出现这种情况。 HDFS的Balancer类,是为了实现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类中对线程池做了限制,
所以最多可以有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%中间,说明集群已经达到均衡状态。 |