Hadoop的磁盘写入策略引发的问题

云计算 waitig 1032℃ 百度已收录 0评论

DataNode挂载的磁盘或者DataNode节点挂载多个磁盘,如果存在一些磁盘大小不一样,数据在落盘时就可能会出现磁盘使用率不均匀的情况,容量较小的盘容易被写满,而容量大的盘还剩很多空间。磁盘写满后,影响Hadoop集群的正常工作。国庆第一天,线上集群就报出了JournalNode挂掉的异常情况,经查是由于2T的磁盘被写满,JournalNode无法再写入数据。当时采取了临时的措施,删掉HBase和Hive中不用,占大量空间的表。磁盘使用率下降一部分后,重新启动JournalNode。

集群中每个DataNode都挂载了两个硬盘,分别为2T和4T的,2T基本都被写满,而4T的才50%多。是什么造成了这种数据落盘时的不均匀情况?本主要文调研了Hadoop的数据两种写入磁盘的策略,并分析了两种策略的主要源码实现,最后总结解决此次异常的经验。

两种写入策略

循环选取

循环选取策略是在hfds1.0中实现的,hdfs2.x默认沿用hfds1.x的方式
hdfs2.0默认沿用hfds1.0的方式,按照循环的策略,数据会均匀的落在不同大小的盘上,大磁盘和小磁盘存储的块是一样的,导致小的磁盘最先被写满。

可用空间策略

hdfs2.0也提供了另一种策略,将数据优先写入具有最大可用空间。通过一个概率计算出选择写入的磁盘,磁盘剩余空间大的将会获得更大的写入概率,这样磁盘的使用率就会相对均匀。

两种方案的对比图如下,图来源于链接,能够很清楚的看出两种策略的不同。
这里写图片描述
hdfs3.0提供了一个在线磁盘均衡器diskbalancer ,能在不停机的情况下,对数据进行均衡操作。但是hadoop3.0仍是一个测试版本,因此不可能进行升级。

源码分析

循环选取策略

循环选取的策略很简单,循环扫描整个Volumes,如果availableVolumeSize大于blockSize ,即返回该volume。为了保证每次选择的起点都不是从头开始,导致数据写满一个盘后再写另一个盘,使用了一个curVolumes定位器来防止这个问题。

int curVolume = curVolumes[curVolumeIndex] < volumes.size()
            ? curVolumes[curVolumeIndex] : 0;
int startVolume = curVolume;
long maxAvailable = 0;
while (true) {
      final V volume = volumes.get(curVolume);
      curVolume = (curVolume + 1) % volumes.size();
      long availableVolumeSize = volume.getAvailable();
      if (availableVolumeSize > blockSize) {
        curVolumes[curVolumeIndex] = curVolume;
        return volume;
      }

      if (availableVolumeSize > maxAvailable) {
        maxAvailable = availableVolumeSize;
      }

      if (curVolume == startVolume) {
        throw new DiskOutOfSpaceException("Out of space: "
            + "The volume with the most available space (=" + maxAvailable
            + " B) is less than the block size (=" + blockSize + " B).");
      }
    }

可用空间策略

1、通过计算最大剩余空间与最小剩余空间的差值,然后与阈值dfs.datanode.available-space-volume-choosing-policy.balanced-space-threshold进行对比,默认为10G,如果小于该值,将使用循环写入策略,如果不小于该值,则使用最大可用空间策略。

public boolean areAllVolumesWithinFreeSpaceThreshold() {
      long leastAvailable = Long.MAX_VALUE;
      long mostAvailable = 0;
      for (AvailableSpaceVolumePair volume : volumes) {
        leastAvailable = Math.min(leastAvailable, volume.getAvailable());
        mostAvailable = Math.max(mostAvailable, volume.getAvailable());
      }
      return (mostAvailable - leastAvailable) < balancedSpaceThreshold;
    }

2、通过与leastAvailable + balancedSpaceThreshold比较,将volume划分为两类集合。一类lowAvailableVolumes相对最小,一类highAvailableVolumes相对最大。

public List<AvailableSpaceVolumePair> getVolumesWithHighAvailableSpace() {
      long leastAvailable = getLeastAvailableSpace();
      List<AvailableSpaceVolumePair> ret = new ArrayList<AvailableSpaceVolumePair>();
      for (AvailableSpaceVolumePair volume : volumes) {
            //leastAvailable为所有Volume中容量最小的
        if (volume.getAvailable() > leastAvailable + balancedSpaceThreshold) {
          ret.add(volume);
        }
      }
      return ret;
    }

3、根据dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction的大小(默认为0.75f)和lowAvailableVolumes,highAvailableVolumes的大小计算出一个两类Volumes选取的概率。这里没有直接使用0.75f,而是考虑到了两类Volume的数量的影响,如果highAvailableVolumes的数量大于lowAvailableVolumes,则计算出的Volume选取概率将大于0.75。反之则小。

        // 获得相对最大和相对最小的磁盘集合,将volume划分为两类
      List<V> highAvailableVolumes = extractVolumesFromPairs(
          volumesWithSpaces.getVolumesWithHighAvailableSpace());
      List<V> lowAvailableVolumes = extractVolumesFromPairs(
          volumesWithSpaces.getVolumesWithLowAvailableSpace());

      // 算出一个相对概率
      float preferencePercentScaler =
          (highAvailableVolumes.size() * balancedPreferencePercent) +
          (lowAvailableVolumes.size() * (1 - balancedPreferencePercent));
      float scaledPreferencePercent =
          (highAvailableVolumes.size() * balancedPreferencePercent) /
          preferencePercentScaler;

4、最后随机生成一个概率与scaledPreferencePercent对比,从而决定从highAvailableVolumes,还是lowAvailableVolumes中选择Volume。这里同样使用了循环顺序选择策略。

       if (mostAvailableAmongLowVolumes < replicaSize ||
          random.nextFloat() < scaledPreferencePercent) {
        // 在high volume中循环选择一个
        volume = roundRobinPolicyHighAvailable.chooseVolume(
            highAvailableVolumes, replicaSize);
      } else {
        // 在low volume中循环选择一个
        volume = roundRobinPolicyLowAvailable.chooseVolume(
            lowAvailableVolumes, replicaSize);
      }

汇总:程序根据设定的阈值判断使用循环顺序策略还是最大可用空间策略,如果使用最大可用空间策略,将所有的Volume分为两类,根据设置的选取概率和每一类的数量计算出每一类的选取概率,然后在选取到的集合中再使用循环顺序策略。

解决方法

由于循环策略造成磁盘不均的解决方法如下:
1、数据清理:此方法属于紧急措施:清理掉hdfs中不用的数据
2、数据压缩:手动压缩部分数据,对于HBase可使用GZ压缩方式,能快速有效的降低磁盘使用率
3、数据移盘:手动进行数据的移动,将部分数据由写满的盘移动到其它盘中

主要有三步操作:
1、关闭DataNode节点
2、使用mv命令移动数据,要绝对保证移动后的数据相对目录与移动前一致,如移动前data/1/dfs/dn/current/BP-1788246909-172.23.1.202-1412278461680/current/finalized/subdir0/subdir1/,移动后为data/5/dfs/dn/current/BP-1788246909-172.23.1.202-1412278461680/current/finalized/subdir0/subdir1/
3、重启DataNode
可以参考 https://wiki.apache.org/hadoop/FAQ#On_an_individual_data_node.2C_how_do_you_balance_the_blocks_on_the_disk.3F
4、通过上述步骤后,可以选择切换到可用空间策略上。

总结

经过此次异常情况,我重新梳理了问题的过程,分析薄弱的环节,加强的对Hadoop磁盘的监控,增加对异常的处理手段。同时也对Hadoop的磁盘写入策略进行了调研,了解问题产生的原因,才能更好的解决问题。


本文由【waitig】发表在等英博客
本文固定链接:Hadoop的磁盘写入策略引发的问题
欢迎关注本站官方公众号,每日都有干货分享!
等英博客官方公众号
点赞 (0)分享 (0)