DataNode本地数据存储和管理(二)

2014-11-24 11:52:29 · 作者: · 浏览: 152
存储文件夹(datanode下可以有多个文件夹用于存放数据,命名为subdir0...。因为HDFS规定了一个目录能存放Block的数目(dfs.datanode.numblocks,默认64)。如果数目多了会用创建新的子目录来存放新块。),并拥有update/rollback/finalize等存储文件夹操作,在recoverTransitionRead调用StorageDirectory.analyzeStorage分析文件夹的状态,出错时调用doRecover进行恢复。
-----------------------DataNode本地数据存储管理FSDataset类------------------------------------------
所有和数据块相关的操作,都在FSDataset相关的类中进行处理。
DataNode的存储结构由大到小为卷FSVolume、目录FSDir、文件(block和元数据等)。
DataNodeBlockInfo类存放的是Block在文件系统上的信息:FSVolume所属的卷,File所属的文件,detached是否分离(即是否不与其他文件建立硬链接)。
DataNodeBlockInfo.DetachFile是用于分离文件。在升级update后,previous目录和current目录会通过硬链接指到同一个文件,previous保存的是旧版本文件,当要修改current的文件时,需要将这个文件进行分离(copy-on-write),使得不会影响到previous中的文件,detachFile就是做这个的:在detach目录下创建复制一个相同的文件,然后将这个文件复制到current下,这样current下文件的硬链接就断掉了。
在DataNode中,不需要考虑块属于哪个文件,‘块文件’指的是数据块的文件,而非所属的文件。
由于HDFS规定了一个目录能存放Block的数目,所以一个Storage上存在多个目录。
对应的,FSDataset中用FSVolume来对应一个Storage(一个dfs中可以有多个Storage),FSDir对应一个目录,所有的FSVolume由FSVolumeSet管理,
FSDataset中通过一个FSVolumeSet对象,就可以管理它的所有存储空间。当然这些文件、文件夹都是指存放在current下。
FSDir是一个目录类,包含变量:
[java]
File dir;//该目录所在的文件
int numBlocks = 0;//该目录下的块数,不递归统计
FSDir children[];//该目录下的子目录
int lastChildIdx = 0;//上一次用来存放块的目录在children中的下标
FSDir.addBlock用于将block添加到指定的目录下:
[java]
private File addBlock(Block b, File src, boolean createOk,
boolean resetIdx) throws IOException {
//从将块从tmp(src)目录移动到dest目录下
if (numBlocks < maxBlocksPerDir) {
File dest = new File(dir, b.getBlockName());//e.g. blk_-898210404237475067
File metaData = getMetaFile( src, b );//tmp目录下对应的元数据文件
File newmeta = getMetaFile(dest, b);
if ( ! metaData.renameTo( newmeta ) ||//移动到指定目录下
! src.renameTo( dest ) ) {....}
numBlocks += 1;//success
return dest;
}
//目录下已存放最多块数了,递归找其一个未满的子目录进行存放。 最终会将这个块存放的位置返回去return dest
if (lastChildIdx < 0 && resetIdx) {
//reset so that all children will be checked
lastChildIdx = random.nextInt(children.length);
}
if (lastChildIdx >= 0 && children != null) {
//检测是否还有子目录可供存放此块(子目录下未达到最大存放块数).
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;
}
....
//没找到可用的子目录,新建maxBlocksPerDir个子目录,在将Block 存放到这个子目录下
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);
}
FSDir.getBlockInfo根据dir目录的块实际分布情况建立一颗块树TreeSet(建立过程就是树的遍历)。FSDir.getVolumeMap会为FSDir.getBlockInfo得到的块树建立的哈希表:HashMap volumeMap。FSDataset.volumeMap是本地目录(可包括多个卷)中数据块的一个哈希映射表。
FSDir.clearPath用于在块删除后更新目录下的块数numBlocks。
FSVolume是一个卷,内含:
private FSDir dataDir;//current文件夹,用