HDFS-Datanode磁盘选择策略分析

本文深入探讨Hadoop中DataNode的两种磁盘选择策略:轮询策略与可用空间策略,分析其优缺点及适用场景,揭示磁盘数据不平衡问题及其解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Hadoop技术体系中,hdfs是重要的技术之一,而真实的数据都存储在datanode节点之上,DataNode 将数据块存储到本地文件系统目录中,而每个datanode节点可以配置多个存储目录(可以是不同类型的数据硬盘),hdfs-site.xml (dfs.datanode.data.dir 参数)。

一般的hadoop集群datanode节点会配置多块数据盘,当我们往 HDFS 上写入新的数据块,DataNode 将会使用 volume 选择策略来为数据块选择存储的磁盘目录。目前有两种volume选择策略:

  • round-robin  (default)

  • available space

遇到的问题:

由于hadoop集群规模一般比较大,且需要长期维护,所以会涉及到很多流程以及操作,例如:扩容新服务器、定期更换坏盘、下线服务器、删除历史数据等等。 所以会造成节点间的数据不平衡,以及datanode节点上多个磁盘之间的不平衡问题。

1. 节点间的数据不平衡,可以通过hdfs 本身的balancer工具进行数据平衡;

2. datanode节点上多个磁盘之间数据不平衡,Hadoop 3.0 引入了磁盘均衡器(diskbalancer)。

这里先抛出一个问题:为什么datanode本身的磁盘选择策略没有很好的解决这些数据不平衡问题呢?

 

下面分析下datanode磁盘选择相关的源码:

hdfs-site.xml: 配置项 dfs.datanode.fsdataset.volume.choosing.policy

org.apache.hadoop.hdfs.server.datanode.fsdataset.RoundRobinVolumeChoosingPolicy   (default)

org.apache.hadoop.hdfs.server.datanode.fsdataset.AvailableSpaceVolumeChoosingPolicy
 

A. RoundRobinVolumeChoosingPolicy:



/**
 * Choose volumes in round-robin order.
 */
public class RoundRobinVolumeChoosingPolicy<V extends FsVolumeSpi>
    implements VolumeChoosingPolicy<V> {
  public static final Log LOG = LogFactory.getLog(RoundRobinVolumeChoosingPolicy.class);

  private int curVolume = 0;

  @Override
  public synchronized V chooseVolume(final List<V> volumes, 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();
      // 可用空间大于数据块,直接返回volume
      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).");
      }
    }
  }
}

可见,这种轮询的实现目的也是为了数据均衡,这种轮询的方式虽然能够保证所有磁盘都能够被使用,但是由于这种算法实现只是按照block数量进行轮询选择,而没有考虑到每次存储的block大小,如果每次存储的block大小相差很大,也会造成磁盘数据不均衡;另外如果HDFS 上的文件存在大量的删除操作,也可能会导致磁盘数据的分布不均匀。 

看下第二种实现方式.

B. AvailableSpaceVolumeChoosingPolicy:

/**
 * A DN volume choosing policy which takes into account the amount of free
 * space on each of the available volumes when considering where to assign a
 * new replica allocation. By default this policy prefers assigning replicas to
 * those volumes with more available free space, so as to over time balance the
 * available space of all the volumes within a DN.
 */
public class AvailableSpaceVolumeChoosingPolicy<V extends FsVolumeSpi>
    implements VolumeChoosingPolicy<V>, Configurable {

  
  // 加载并初始化配置  (省略)
  .................
 
  // 用于需要平衡磁盘的轮询磁盘选择策略
  private final VolumeChoosingPolicy<V> roundRobinPolicyBalanced =
      new RoundRobinVolumeChoosingPolicy<V>();
  // 用于可用空间高的磁盘的轮询磁盘选择策略
  private final VolumeChoosingPolicy<V> roundRobinPolicyHighAvailable =
      new RoundRobinVolumeChoosingPolicy<V>();
  // 用于可用空间低的磁盘的轮询磁盘选择策略
  private final VolumeChoosingPolicy<V> roundRobinPolicyLowAvailable =
      new RoundRobinVolumeChoosingPolicy<V>();

  @Override
  public synchronized V chooseVolume(List<V> volumes,
      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;
      // 如果没有一个低自由空间的体积有足够的空间存储副本时,总是尽量选择有大量空闲空间的卷。
      // 从低剩余磁盘列表中选取最大可用空间(磁盘可用)
      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 ||
          random.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;
    }
  }
  

 

高\低可用空间磁盘列表调用逻辑:

  /**
   * Used to keep track of the list of volumes we're choosing from.
   */
  private class AvailableSpaceVolumeList {
    
    // 省略
    ................

    /**
     * @return the maximum amount of space available across volumes with low space.
     */
    public long getMostAvailableSpaceAmongVolumesWithLowAvailableSpace() {
      long mostAvailable = Long.MIN_VALUE;
      for (AvailableSpaceVolumePair volume : getVolumesWithLowAvailableSpace()) {
        mostAvailable = Math.max(mostAvailable, volume.getAvailable());
      }
      return mostAvailable;
    }
    
    /**
     * @return the list of volumes with relatively low available space.
     */
    public List<AvailableSpaceVolumePair> getVolumesWithLowAvailableSpace() {
      long leastAvailable = getLeastAvailableSpace();
      List<AvailableSpaceVolumePair> ret = new ArrayList<AvailableSpaceVolumePair>();
      for (AvailableSpaceVolumePair volume : volumes) {
        // 可用空间小于 (最小可用空间+平衡阀值)
        if (volume.getAvailable() <= leastAvailable + balancedSpaceThreshold) {
          ret.add(volume);
        }
      }
      return ret;
    }
    
    /**
     * @return the list of volumes with a lot of available space.
     */
    public List<AvailableSpaceVolumePair> getVolumesWithHighAvailableSpace() {
      long leastAvailable = getLeastAvailableSpace();
      List<AvailableSpaceVolumePair> ret = new ArrayList<AvailableSpaceVolumePair>();
      for (AvailableSpaceVolumePair volume : volumes) {
        // 可用空间大于 (最小可用空间+平衡阀值)
        if (volume.getAvailable() > leastAvailable + balancedSpaceThreshold) {
          ret.add(volume);
        }
      }
      return ret;
    }
    
  }

可见,可用空间策略设计原理是根据配置平衡阀值划分磁盘分为两类列表:高可用空间磁盘列表、低可用空间列表,通过随机数概率,会相应较高概率选择高可用空间列表中的磁盘;

 

分析到这里,感觉可用空间策略可以很好的解决磁盘数据不平衡问题,为什么datanode应用的默认策略还是基于轮询磁盘选择策略呢?

长期运行的集群中会遇到这样一种场景,hdfs所有的datanode节点磁盘使用率很高,已达90%以上,这时在一个datanode节点更换一个磁盘,如果采用可用空间策略,新增的数据块高概率都会往更换的新盘上写入,其他磁盘处于空闲状态,就会导致较低的磁盘IO效率,磁盘IO可能会成为整个集群的瓶颈 ;

 

总结:

经过对源码的分析,发现datanode的磁盘选择策略都在一定程度上保证了磁盘使用的均衡,但是都存在一定的问题,需要针对与不同的集群情况进行不断变换策略使用;
 

 

 

 

<think>我们面临的问题是Hadoop集群中DataNode无法启动。根据引用[1][2][3][4][5],最常见的原因是NameNode和DataNode的clusterID不一致(通常由于多次格式化NameNode导致)或数据目录残留文件。以下是针对Hadoop2.x和3.x的完整解决方案:###步骤1:停止所有Hadoop服务```bash#Hadoop2.x$HADOOP_HOME/sbin/stop-all.sh#Hadoop3.x$HADOOP_HOME/sbin/stop-dfs.sh$HADOOP_HOME/sbin/stop-yarn.sh```###步骤2:定位关键配置文件检查`hdfs-site.xml`获取数据存储路径:```xml<!--NameNode元数据目录--><property><name>dfs.namenode.name.dir</name><value>/path/to/namenode</value></property><!--DataNode数据目录--><property><name>dfs.datanode.data.dir</name><value>/path/to/datanode</value></property>```###步骤3:解决clusterID不一致问题(核心步骤)####3.1在NameNode节点查看clusterID```bashcat/path/to/namenode/current/VERSION```输出示例:```namespaceID=123456789clusterID=CID-12345678-90ab-cdef-1234-567890abcdef#记录此ID```####3.2在所有DataNode节点修改clusterID```bash#进入DataNode数据目录cd/path/to/datanode/current#备份原VERSION文件cpVERSIONVERSION.bak#将clusterID替换为NameNode的clusterIDsed-i's/clusterID=.*/clusterID=CID-12345678-90ab-cdef-1234-567890abcdef/'VERSION```###步骤4:清理残留数据(可选)若步骤3无效,需彻底清理数据目录(**注意:会删除HDFS所有数据!**):```bash#在所有节点执行rm-rf/path/to/namenode/*rm-rf/path/to/datanode/*rm-rf/tmp/hadoop*#清理临时目录#重新格式化NameNode(仅在NameNode节点执行)hdfsnamenode-format```###步骤5:重新启动集群```bash#Hadoop2.x$HADOOP_HOME/sbin/start-all.sh#Hadoop3.x$HADOOP_HOME/sbin/start-dfs.sh$HADOOP_HOME/sbin/start-yarn.sh```###步骤6:验证启动状态```bash#检查Java进程jps|grep-E'NameNode|DataNode|SecondaryNameNode'#检查WebUIhttp://<namenode_ip>:9870(Hadoop3.x)或50070(Hadoop2.x)#检查节点报告hdfsdfsadmin-report```###常见问题排查表|现象|解决方案||------|----------||`IncompatibleclusterIDs`|严格按步骤3同步clusterID||`Cannotlockstorage`|删除`in_use.lock`文件:`rm/path/to/datanode/current/in_use.lock`[^5]||端口冲突|检查`hdfs-site.xml`中`dfs.datanode.address`配置端口是否被占用||磁盘空间不足|`df-h`检查并清理磁盘||权限问题|用Hadoop运行用户执行:`sudochown-Rhadoop_user:hadoop_group/data/path`|>**关键预防措施**:>1.避免重复格式化NameNode(格式化前备份元数据)>2.配置`hdfs-site.xml`中的`dfs.namenode.name.dir`和`dfs.datanode.data.dir`为持久化路径>3.使用相同用户启动所有服务(推荐创建专用`hadoop`用户)[^2]§§相关问题§§1.HadoopNameNode格式化操作的注意事项有哪些?2.如何在不丢失数据的情况下修复HDFS集群?3.Hadoop集群添加新DataNode的正确步骤是什么?4.如何监控HDFSDataNode磁盘健康状态?</think>### Hadoop DataNode 无法启动的完整解决方案(2.x/3.x版本) 根据引用[1][2][3][4][5],DataNode无法启动的核心问题是**NameNode和DataNode的clusterID不一致**,通常由多次格式化NameNode导致。以下是系统化的解决方案: --- #### 第一步:停止所有服务 ```bash # Hadoop 2.x $HADOOP_HOME/sbin/stop-all.sh # Hadoop 3.x $HADOOP_HOME/sbin/stop-dfs.sh $HADOOP_HOME/sbin/stop-yarn.sh ``` #### 第二步:定位关键目录 1. **查看配置文件**: ```bash cat $HADOOP_HOME/etc/hadoop/hdfs-site.xml ``` 定位以下配置项: ```xml <!-- NameNode元数据目录 --> <property> <name>dfs.namenode.name.dir</name> <value>/path/to/namenode</value> </property> <!-- DataNode数据目录 --> <property> <name>dfs.datanode.data.dir</name> <value>/path/to/datanode</value> </property> ``` #### 第三步:解决clusterID不一致问题(核心步骤) 1. **在NameNode节点查看clusterID**: ```bash cat /path/to/namenode/current/VERSION ``` 记录输出中的 `clusterID` 值(例如 `clusterID=CID-12345678-90ab-cdef-1234-567890abcdef`) 2. **在所有DataNode节点修改clusterID**: ```bash # 进入DataNode数据目录 cd /path/to/datanode/current # 备份原文件 cp VERSION VERSION.bak # 替换clusterID为NameNode的值 sed -i 's/clusterID=.*/clusterID=CID-12345678-90ab-cdef-1234-567890abcdef/' VERSION ``` #### 第四步:清理残留数据(可选) 如果第三步无效,执行彻底清理: ```bash # 在所有节点执行 rm -rf /path/to/namenode/* rm -rf /path/to/datanode/* rm -rf /tmp/hadoop* # 清理临时目录 # 重新格式化NameNode(仅在NameNode节点执行) hdfs namenode -format ``` #### 第五步:重启集群并验证 ```bash # Hadoop 2.x $HADOOP_HOME/sbin/start-all.sh # Hadoop 3.x $HADOOP_HOME/sbin/start-dfs.sh $HADOOP_HOME/sbin/start-yarn.sh # 检查DataNode状态 jps | grep DataNode hdfs dfsadmin -report ``` --- ### 常见问题排查表 | 现象 | 解决方案 | |------|----------| | `Incompatible clusterIDs` | 严格按第三步同步clusterID [^3][^4] | | `Cannot lock storage` | 删除`in_use.lock`文件:`rm /path/to/datanode/current/in_use.lock`[^5] | | Web UI不显示DataNode | 清理所有节点的`dfs.data.dir`和`dfs.name.dir`目录 [^2] | | 端口冲突 | 检查`hdfs-site.xml`中`dfs.datanode.address`配置的端口 | | 磁盘空间不足 | `df -h`检查并清理数据目录 | > **关键预防措施**: > 1. **避免重复格式化**:格式化NameNode前备份元数据 > 2. **配置持久化路径**:在`hdfs-site.xml`中设置稳定的存储路径 > 3. **统一用户权限**:确保所有节点使用相同用户启动服务 --- ### 版本差异说明 | 项目 | Hadoop 2.x | Hadoop 3.x | |------|------------|------------| | 停止服务 | `stop-all.sh` | `stop-dfs.sh` + `stop-yarn.sh` | | Web UI端口 | 50070 | 9870 | | 关键配置文件 | `hdfs-site.xml` | `hdfs-site.xml` + `workers`文件 | 通过上述步骤,90%以上的DataNode启动问题可得到解决。如仍失败,请检查日志: ```bash tail -n 100 $HADOOP_HOME/logs/hadoop-*-datanode-*.log ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值