關於hadoop中datanode節點不同的dfs.data.dir之間數據均衡問題


問題:集群中的存儲數據增大,導致datanode的空間都快占滿了(以前的dfs.data.dir=/data/hdfs/dfs/data),機器的硬盤監控程序不停的報警 。

         給每台機器加了一倍的存儲硬盤(新的dfs.data.dir=/data/hdfs/dfs/data,/data/hdfs/dfs/data2    新的硬盤掛載在/data/hdfs/dfs/data2),但是現在的問題來了,以前裝數據的那塊盤還是滿的,仍然在報警,怎么把數據均衡到這兩塊盤上面 ??

 

解決:移動其中一個文件夾的數據(其實也就是block)到另外一個文件加 。

原理:hdfs中文件的inode文件樹信息以及每個文件對應的block信息存儲在NN中的,每個block存儲在那幾台DN機器上是在集群啟動時候,由每台datanode根據自身所擁有的block上報上去的。 datanode在啟動時候,也是掃描自身dfs.data.dir的各個文件夾下的current(前提是這個目錄的VERSION等必要信息文件時存在的,這個目錄是合法的)目錄,然后將下面存在block信息(有哪些block,存在那個文件夾下)上報到NN (詳細參見DataNode的FSDataset代碼)。

操作:

1、停止集群 。

2、修改dfs.data.dir的配置 。

3、啟動集群(先只啟動hdfs),該步的目的是:讓DataNode去格式化/data/hdfs/dfs/data2,填充其中的一些系統信息文件(例如:current  ,current/VERSION,detach ,storage等)。

5、使用http://namenodeAddress:50070/fsck來檢查文件系統,並記錄下結果,以便與修改后進行fsck比較,看文件系統是否健全 。

4、停止集群 。

5、進入/data/hdfs/dfs/data/current 目錄,將其中一些較大的子文件夾(如果是系統生成的名字一般是subdir**)mv到/data/hdfs/dfs/data2/current 下 。

6、啟動集群 (先是hdfs,好做檢查) 。

7、再次執行http://namenodeAddress:50070/fsck命令,將結果與前一次比較,沒出問題的話,應該是一樣的 。

 

PS:在dev機器上做了相應的實驗,做之前將dev的數據文件備份,現在啟動dev的集群,已經跑了一段時間,並沒有出現問題 。

源代碼部分:

   在DataNode中,每個dfs.data.dir的文件夾對應的是一個FSDir類,而其文件下的每一個子文件夾對應一個FSDir ,每次DataNode啟動時,會對每一個dfs.data.dir的文件啟動一個FSDir對象,讓后其在構造函數中會統計該FSDir下存儲哪些block ,也既是每個block存在那個文件夾只有在DataNode啟動時掃描自己的dfs.data.dir是才知道,這也是本次修改的原理 。

    public FSDir(File dir) 
      throws IOException {
      this.dir = dir;
      this.children = null;
      //文件夾不存在則創建文件夾
      //不會覆蓋或刪除已經存在的文件夾,為手工移動block提供了便利性
      if (!dir.exists()) {                                                                                                                                                                                                
        if (!dir.mkdirs()) {
          throw new IOException("Mkdirs failed to create " + 
                                dir.toString());
        }
      } else {
        File[] files = dir.listFiles();
        int numChildren = 0;
        //自檢查文件夾個數(子FSDir的個數)和文件(block個數)
        for (int idx = 0; idx < files.length; idx++) {
          if (files[idx].isDirectory()) {
            numChildren++;
          } else if (Block.isBlockFilename(files[idx])) {
            numBlocks++;
          }
        }
        //為其每個子文件夾創建一個FSDir對象 。
        if (numChildren > 0) {
          children = new FSDir[numChildren];
          int curdir = 0;
          for (int idx = 0; idx < files.length; idx++) {
            if (files[idx].isDirectory()) {
              children[curdir] = new FSDir(files[idx]);
              curdir++;
            }
          }
        }
      }
    }

 

其往FSDir中添加block的代碼:

/**
     * //所作的事情:將在tmp中的block文件以及其meta文件mv(重命名)到current中
     * 第一次調用的形式是:先調用 File file = addBlock(b, src, false, false) ,再調用addBlock(b, src, true, true)      
     * boolean createOk :  是否需要在子FSDir中創建子子FSDir,先不創建的添加,若是不行(子目錄存放的block也滿了),再允許子目錄創建子子目錄 。
     * boolean resetIdx : 當存儲需要在子目錄中時候,每次存儲完都有一個lastChildIdx表明上次存在哪個文件夾下:設置為true表示下次會在隨機選一個,false表示下次還接着上次的來存
     */
    private File addBlock(Block b, File src, boolean createOk, 
                          boolean resetIdx) throws IOException {
      //本層目錄未滿,這直接將block添加在本目錄
      if (numBlocks < maxBlocksPerDir) {
        File dest = new File(dir, b.getBlockName());
        File metaData = getMetaFile( src, b );
        File newmeta = getMetaFile(dest, b);
        if ( ! metaData.renameTo( newmeta ) ||
            ! src.renameTo( dest ) ) {
          throw new IOException( "could not move files for " + b +
                                 " from tmp to " + 
                                 dest.getAbsolutePath() );
        }
        if (DataNode.LOG.isDebugEnabled()) {
          DataNode.LOG.debug("addBlock: Moved " + metaData + " to " + newmeta);
          DataNode.LOG.debug("addBlock: Moved " + src + " to " + dest);
        }

        numBlocks += 1;
        return dest;
      }
      //需要存到子目錄下 ,隨即從新選擇一個開始子目錄
      //通過這個resetIdx來平均各個子FSDir的存儲不均衡 。
      if (lastChildIdx < 0 && resetIdx) {
        //reset so that all children will be checked
        lastChildIdx = random.nextInt(children.length);              
      }
      //從上面隨即的lastChildIdx開始找一個能添加
      if (lastChildIdx >= 0 && children != null) {
        //Check if any child-tree has room for a block.
        for (int i=0; i < children.length; i++) {
          int idx = (lastChildIdx + i)%children.length;
          //這一塊的添加既是先試着用  子目錄中不創建子子目錄的方式 添加 。
          File file = children[idx].addBlock(b, src, false, resetIdx);
          if (file != null) {
            lastChildIdx = idx;
            return file; 
          }
        } 
        lastChildIdx = -1;
      }
            
      if (!createOk) {
        return null;
      }
      //如果沒有子文件夾才這么做,當有手工移動來的子文件時候造成的結果是這一層目錄只有手工移動過來的那幾個文件夾 ~~
//若是沒有手工成分,DataNode會一次性建立maxBlocksPerDir個子文件夾 。
//maxBlocksPerDir由dfs.datanode.numblocks確定,默認是64 ,含義是每個FSDir中最多存儲多少個block ,最多存儲多少個子FSDir
if (children == null || children.length == 0) { children = new FSDir[maxBlocksPerDir]; for (int idx = 0; idx < maxBlocksPerDir; idx++) { children[idx] = new FSDir(new File(dir, DataStorage.BLOCK_SUBDIR_PREFIX+idx)); } } //now pick a child randomly for creating a new set of subdirs. lastChildIdx = random.nextInt(children.length); return children[ lastChildIdx ].addBlock(b, src, true, false); }
maxBlocksPerDir由dfs.datanode.numblocks確定,默認是64 ,含義是每個FSDir中最多存儲多少個block ,最多存儲多少個子FSDir

貼出以上代碼主要是剛開始怕DataNode有一套自己的機制,會把你添加進去的文件(夾)清除掉,或者是把不符合規則的子文件夾(一般的是當建立子文件夾是會一次性建立subdir0--subdir63 這64個文件夾,代碼見第二部分代碼)清除掉 ,但是后來看過代碼后發現也沒這些邏輯 。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM