欢迎关注Hadoop、Spark、Flink、Hive、Hbase、Flume等大数据资料分享微信公共账号:iteblog_hadoop
  1. 文章总数:978
  2. 浏览总数:11,966,560
  3. 评论:3937
  4. 分类目录:106 个
  5. 注册用户数:6126
  6. 最后更新:2018年12月15日
过往记忆博客公众号iteblog_hadoop
欢迎关注微信公众号:
iteblog_hadoop
大数据技术博客公众号bigdata_ai
大数据猿:
bigdata_ai

HDFS 副本存放磁盘选择策略

HDFS 中,DataNode 将数据块存储到本地文件系统目录中,具体的目录可以通过配置 hdfs-site.xml 里面的 dfs.datanode.data.dir 参数。在典型的安装配置中,一般都会配置多个目录,并且把这些目录分别配置到不同的设备上,比如分别配置到不同的HDD(HDD的全称是Hard Disk Drive)和SSD(全称Solid State Drives,就是我们熟悉的固态硬盘)上。

当我们往 HDFS 上写入新的数据块,DataNode 将会使用 volume 选择策略来为这个块选择存储的地方。通过参数 dfs.datanode.fsdataset.volume.choosing.policy 来设置,这个参数目前支持两种磁盘选择策略

  • round-robin
  • available space

如果想及时了解Spark、Hadoop或者Hbase相关的文章,欢迎关注微信公共帐号:iteblog_hadoop

dfs.datanode.fsdataset.volume.choosing.policy 参数的默认值是 org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy。这两种磁盘选择策略都是对 org.apache.hadoop.hdfs.server.datanode.fsdataset.VolumeChoosingPolicy 接口进行实现,VolumeChoosingPolicy 接口其实就定义了一个函数:chooseVolume 如下:

package org.apache.hadoop.hdfs.server.datanode.fsdataset;

import java.io.IOException;
import java.util.List;

import org.apache.hadoop.classification.InterfaceAudience;

/**
 * This interface specifies the policy for choosing volumes to store replicas.
 */
@InterfaceAudience.Private
public interface VolumeChoosingPolicy<V extends FsVolumeSpi> {

  /**
   * Choose a volume to place a replica,
   * given a list of volumes and the replica size sought for storage.
   * 
   * The implementations of this interface must be thread-safe.
   * 
   * @param volumes - a list of available volumes.
   * @param replicaSize - the size of the replica for which a volume is sought.
   * @return the chosen volume.
   * @throws IOException when disks are unavailable or are full.
   */
  public V chooseVolume(List<V> volumes, long replicaSize) throws IOException;
}

chooseVolume 函数对指定的副本从 volumes 里面选定满足条件的磁盘。下面对 Hadoop 内置的两种磁盘选择策略进行详细的介绍。

round-robin 磁盘选择策略

从名字就可以看出,这种磁盘选择策略是基于轮询的方式,具体的实现类是 org.apache.hadoop.hdfs.server.datanode.fsdataset.RoundRobinVolumeChoosingPolicy。它的实现很简单:

public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
    implements VolumeChoosingPolicy<V> {

  private int curVolume = 0;

  @Override
  public synchronized V chooseVolume(final List<V> volumes, final long blockSize
      ) throws IOException {
    if(volumes.size() < 1) {
      throw new DiskOutOfSpaceException("No more available volumes");
    }
    
    // since volumes could've been removed because of the failure
    // make sure we are not out of bounds
    if(curVolume >= volumes.size()) {
      curVolume = 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) { 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).");
      }
    }
  }
}

volumes 参数其实就是通过 dfs.datanode.data.dir 配置的目录。blockSize 就是咱们副本的大小。RoundRobinVolumeChoosingPolicy 策略先轮询的方式拿到下一个 volume ,如果这个 volume 的可用空间比需要存放的副本大小要大,则直接返回这个 volume 用于存放数据;如果当前 volume 的可用空间不足以存放副本,则以轮询的方式选择下一个 volume,直到找到可用的 volume,如果遍历完所有的 volumes 还是没有找到可以存放下副本的 volume,则抛出 DiskOutOfSpaceException 异常。

从上面的策略可以看出,这种轮询的方式虽然能够保证所有磁盘都能够被使用,但是如果 HDFS 上的文件存在大量的删除操作,可能会导致磁盘数据的分布不均匀,比如有的磁盘存储得很满了,而有的磁盘可能还有很多存储空间没有得到利用。

available space 磁盘选择策略

可用空间磁盘选择策略是从 Hadoop 2.1.0 开始引入的(详情参见:HDFS-1804)。这种策略优先将数据写入具有最大可用空间的磁盘(通过百分比计算的)。在实现上可用空间选择策略内部用到了上面介绍的轮询磁盘选择策略,具体的实现代码在 org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy 类中,核心实现如下:

public synchronized V chooseVolume(List<V> volumes,
    final long replicaSize) throws IOException {
  if (volumes.size() < 1) {
    throw new DiskOutOfSpaceException("No more available volumes");
  }
  
  AvailableSpaceVolumeList volumesWithSpaces =
      new AvailableSpaceVolumeList(volumes);
  
  if (volumesWithSpaces.areAllVolumesWithinFreeSpaceThreshold()) {
    // If they're actually not too far out of whack, fall back on pure round
    // robin.
    V volume = roundRobinPolicyBalanced.chooseVolume(volumes, replicaSize);
    if (LOG.isDebugEnabled()) {
      LOG.debug("All volumes are within the configured free space balance " +
          "threshold. Selecting " + volume + " for write of block size " +
          replicaSize);
    }
    return volume;
  } else {
    V volume = null;
    // If none of the volumes with low free space have enough space for the
    // replica, always try to choose a volume with a lot of free space.
    long mostAvailableAmongLowVolumes = volumesWithSpaces
        .getMostAvailableSpaceAmongVolumesWithLowAvailableSpace();
    
    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;
    if (mostAvailableAmongLowVolumes < replicaSize ||
        RAND.nextFloat() < scaledPreferencePercent) {
      volume = roundRobinPolicyHighAvailable.chooseVolume(
          highAvailableVolumes,
          replicaSize);
      if (LOG.isDebugEnabled()) {
        LOG.debug("Volumes are imbalanced. Selecting " + volume +
            " from high available space volumes for write of block size "
            + replicaSize);
      }
    } else {
      volume = roundRobinPolicyLowAvailable.chooseVolume(
          lowAvailableVolumes,
          replicaSize);
      if (LOG.isDebugEnabled()) {
        LOG.debug("Volumes are imbalanced. Selecting " + volume +
            " from low available space volumes for write of block size "
            + replicaSize);
      }
    }
    return volume;
  }
}

areAllVolumesWithinFreeSpaceThreshold 函数的作用是先计算所有 volumes 的最大可用空间和最小可用空间,然后使用最大可用空间减去最小可用空间得到的结果和 balancedSpaceThreshold(通过 dfs.datanode.available-space-volume-choosing-policy.balanced-space-threshold 参数进行配置,默认值是 10G) 进行比较。

可用空间策略会以下面三种情况进行处理:

  • 如果所有的 volumes 磁盘可用空间都差不多,那么这些磁盘得到的最大可用空间和最小可用空间差值就会很小,这时候就会使用轮询磁盘选择策略来存放副本。
  • 如果 volumes 磁盘可用空间相差比较大,那么可用空间策略会将 volumes 配置中的磁盘按照一定的规则分为 highAvailableVolumeslowAvailableVolumes。具体分配规则是先获取 volumes 配置的磁盘中最小可用空间,加上 balancedSpaceThreshold(10G),然后将磁盘空间大于这个值的 volumes 放到 highAvailableVolumes 里面;小于等于这个值的 volumes 放到 lowAvailableVolumes 里面。

    比如我们拥有5个磁盘组成的 volumes,编号和可用空间分别为 1(1G)、2(50G)、3(25G)、4(5G)、5(30G)。按照上面的规则,这些磁盘的最小可用空间为 1G,然后加上 balancedSpaceThreshold,得到 11G,那么磁盘编号为1、4的磁盘将会放到 lowAvailableVolumes 里面,磁盘编号为2,3和5将会放到 highAvailableVolumes 里面。

    到现在 volumes 里面的磁盘已经都分到 highAvailableVolumeslowAvailableVolumes 里面了。

    • 如果当前副本的大小大于 lowAvailableVolumes 里面所有磁盘最大的可用空间(mostAvailableAmongLowVolumes,在上面例子中,lowAvailableVolumes 里面最大磁盘可用空间为 5G),那么会采用轮询的方式从 highAvailableVolumes 里面获取相关 volumes 来存放副本。
    • 剩下的情况会以 75%(通过 dfs.datanode.available-space-volume-choosing-policy.balanced-space-preference-fraction 参数进行配置,推荐将这个参数设置成 0.5 到 1.0 之间)的概率在 highAvailableVolumes 里面以轮询的方式 volumes 来存放副本;25% 的概率在 lowAvailableVolumes 里面以轮询的方式 volumes 来存放副本。

然而在一个长时间运行的集群中,由于 HDFS 中的大规模文件删除或者通过往 DataNode 中添加新的磁盘仍然会导致同一个 DataNode 中的不同磁盘存储的数据很不均衡。即使你使用的是基于可用空间的策略,卷(volume)不平衡仍可导致较低效率的磁盘I/O。比如所有新增的数据块都会往新增的磁盘上写,在此期间,其他的磁盘会处于空闲状态,这样新的磁盘将会是整个系统的瓶颈。所以在 Hadoop 3.0 引入了磁盘均衡器(diskbalancer),具体请参见 Hadoop 3.0磁盘均衡器(diskbalancer)新功能及使用介绍

本博客文章除特别声明,全部都是原创!
转载本文请加上:转载自过往记忆(https://www.iteblog.com/)
本文链接: 【HDFS 副本存放磁盘选择策略】(https://www.iteblog.com/archives/2341.html)
喜欢 (15)
分享 (0)
发表我的评论
取消评论

表情
本博客评论系统带有自动识别垃圾评论功能,请写一些有意义的评论,谢谢!
(1)个小伙伴在吐槽
  1. hdfs的信息不错
    铁拳阿牛2018-05-28 18:59 回复