問題:集羣中的存儲數據增大,導致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
附件爲hadoop權威指南