問題:叢集中的存儲資料增大,導緻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()) { thrownew 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++; } elseif (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 ) ) { thrownew 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) { returnnull; } //如果沒有子檔案夾才這麼做,當有手工移動來的子檔案時候造成的結果是這一層目錄隻有手工移動過來的那幾個檔案夾 ~~
//若是沒有手工成分,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個檔案夾,代碼見第二部分代碼)清除掉 ,但是後來看過代碼後發現也沒這些邏輯 。
轉載:http://music.573114.com/Blog/Html/394B/200880.html