【前言】
对于hdfs而言,磁盘故障的处理或者节点的扩容是比较常见的运维操作。对于这种场景的运维操作是相对比较简单的,但关键在于如何快速的使数据在各个dn之间平衡,或者快速的使block数据达到指定的副本数,本文就来聊聊这个小技巧。
【块平衡】
在hdfs的实际使用过程中,难免会遇到DN的异常停止服务的时候。在这种情况下,当NN检测到DN处于异常后,为保证数据满足指定的副本数,因此会逐步为该DN上的所有block,选择新的DN节点,并完成block数据的拷贝,那么长时间运行后,各个DN的磁盘容量就会出现不一致的情况,或者说会有较大的差距。
另外一个比较典型的场景,就是对DN进行扩容。扩容后,新DN上是没有数据的。
对于这两种场景,我们可能会期望将磁盘使用率较高的节点中的一部分数据迁移到其他DN节点中,保证各个DN磁盘容量在一个较平衡的水平。因此,我们可以通过hdfs自带的命令,完成各个DN之间磁盘容量的平衡。
具体命令的使用如下所示:
代码语言:javascript复制hdfs balancer –threshold 10 –idleiterations 5
# 参数说明:
# -threshold:datanode之间磁盘使用率的相差阈值,取值范围0-100
# -idleiterations:迭代次数,默认为5(即连续5次迭代都没有需要移动的block,则退出)
# 其他可选参数:
# -policy <policy>:policy的可选值为datanode/blockpool,其中datanode表示在datanode间进行平衡,blockpool表示在blockpool之间进行平衡(用于联邦)
# -exclude [-f <host-file>]:指定的datanode不参与平衡
# -include [-f <host-file>]:包含指定的datanode
# -source [-f <host-file>]:指定的datanode作为平衡的源
# -blockpools <空格分隔的blockpool列表>:仅对指定的blockpool进行平衡
在使用过程中需要注意的是:在平衡的过程中,datanode之间用于块复制的网络带宽是有一定限制的,默认大小为20MB。可通过如下命令来查看:
代码语言:javascript复制[root@namenode-0 ~]# hdfs dfsadmin -getBalancerBandwidth 192.168.100.165:50020
Balancer bandwidth is 20971520 bytes per second.
默认20MB的原因,是考虑在平衡过程中,dn之间对block数据拷贝占用的带宽不影响正常业务的数据写入。因此对于有较多数据需要平衡时,耗时会比较长。
如果明确平衡过程中没有数据写入时,可以通过命令手动设置平衡时DN之间的带宽,以加速平衡的过程。带宽设置命令如下所示:
代码语言:javascript复制hdfs dfsadmin -setBalancerBandwidth 104857600
# 参数的单位是字节
此外,还有其他几个参数也会影响整体的平衡速度:
代码语言:javascript复制dfs.balancer.moverThreads(默认值1000):迁移过程使用的线程数(每个线程每次迁移一个block)
dfs.datanode.balance.max.concurrent.moves(默认值50):单个datanode最大并发移动的block个数
dfs.balancer.max-size-to-move(默认值10GB):每次迭代过程中最大移动的大小
dfs.balancer.getBlocks.size(默认值2GB):每次移动block的最大大小
dfs.balancer.getBlocks.min-block-size(默认值10MB):每次移动block的最小大小(移动前先查询需要移动的block,小于该大小的block会被过滤)
【块平衡的原理】
block平衡的处理过程分为如下几个步骤:
1)获取所有DN的存储信息
2)根据DN的存储情况对DN进行分类
这一步主要是对汇总的DN存储情况计算出存储的平均值,根据设置的阈值,将DN分为超额使用(OverUtil)、超过平均值(aboveAvgUtil),低于平均值(belowAvgUtil)、低使用率(underUtilized)四个类型,并最终计算出此次需要移动的数据量。
3)选择block移动的源和目的
这一步就是选择需要移动的块的dn源节点和dn目的节点,进一步展开来看,包括
- 从超额使用的节点中选择作为源节点,低使用率的节点中选择作为目的节点
- 从超额使用的节点中选择作为源节点,低于平均值的节点中选择作为目的节点
- 从超过平均值的节点中选择作为源节点,低于平均值的节点中选择作为目的节点
4)下发块复制任务
这里主要就是进行block数据的复制迁移动作,也就是从"源节点"向目的节点建立pipeline完成block数据的复制,当目的节点完成block数据的复制后,再通知源节点删除副本数据。此后各自向NN进行块汇报,完成元数据信息的同步修改。
以上就是一次完整迭代的过程,可能会有多次迭代(具体由对应参数指定)。最终各节点间数据的存储大小其偏差在一个设定的范围内。
此外,这里还有一些小的实现细节:
在block移动过程中,源节点并不一定就是真正直接进行数据拷贝的节点。考虑到源DN节点可能会有较多的block需要移动,负载可能会比较大,因此引入了一个代理DN节点的概念。所谓代理DN节点,指的是与源节点某个block数据块具有同样副本的节点,因此可以代理完成block数据块的拷贝动作。
整体流程如下图所示:
注:这里的rebalance server也就是执行balance命令的所在节点。
【块复制】
除了上面的场景外, 还有一个典型场景:那就是实际运行过程中,部分节点的磁盘异常了。
这种情况下,部分数据的副本数可能没有达到指定的副本数(默认3副本)。那么,更换异常的磁盘后,NN会自动对不满足副本数的文件,进行block复制以满足指定副本个数。
和上面副本平衡的处理策略一样,考虑到保证正常业务写入的性能,block副本复制的速度是比较慢的。如果不是在线处理,期望以更快的速度完成block副本复制,可以通过下面三个参数进行调整。
代码语言:javascript复制# 块复制操作数量倍数, 默认值为2,
# 另外一个因子是DN的个数, 即2个DN, 每次执行2*2个块复制
dfs.namenode.replication.work.multiplier.per.iteration
# 块复制任务分配时,单个DN处理的最大个数,默认值为2
dfs.namenode.replication.max-streams
# 当DN的复制任务大于该值时,不会将该DN选为复制的源节点,默认值为4
dfs.namenode.replication.max-streams-hard-limit
这三个配置的一些推荐值为"32,64,128"或者"64,128,256",实测下来,确实是有明显的速度提升。当然还是要结合集群间实际的网络、节点的内存、磁盘利用率,IO读写耗时等因素来进行配置。
【总结】
本文主要讲述了hdfs中一个比较常见运维操作的小技巧:如何提升块平衡、快复制的速度。在3.x的版本中,还能对同一个dn节点中的多块磁盘进行平衡,有兴趣的可以去了解下。