HDFS中atime与mtime解析

先来了解下Linux中atime和mtime区别:
atime:access time即访问时间
mtime:modify time即修改时间,这里指文件内容的修改。(经常和atime与mtime一起谈到的还有ctime,这里不展开,有兴趣的可以goolge)

这里需要注意的是有的系统可能为了性能上的优化,atime并不是实时更新,此时查看atime并没有得到想要的效果。

Linux atime修改策略与mount有关,可选的值有noatimerelatimestrictatime

  • noatime
    atime不会被更新,即使修改了文件内容
  • relatime
  1. 如果一个文件的atime比ctime或mtime更早,此时你去读取了该文件,atime才会被更新为当前时间。
  2. atime比现在早一天,那么atime在文件读取时会被更新
  • strictatime
    atime在文件每次被读取时,都能够被更新

HDFS中atime和mtime

了解了Linux中的atime与mtime,我们来了解下HDFS中的这两个值的变化规则。

在看代码之前,先想下atime和mtime有可能在哪些地方会修改,

hdfs底层api对文件都有哪些操作?无非就是读写两种操作,读肯定修改的是atime,写修改的是mtime,是否修改atime还得确认。
这里我们还漏掉一种操作,那就是mv。

先来看下atime

atime

去年写过一篇文章HDFS read解析(一)之Open文件流介绍HDFS读操作流程,这里就不再累赘了,直接贴出关键代码。(有兴趣的同学可以自行查看)

这里还是简单说下读的流程:客户端向NN发起一个读请求,NN将相关的block信息返回给客户端,客户端再与对应的DN建立连接读取信息
在这个过程中,先与NN交互然后再与DN交互,那么每个文件的atime相关元数据信息都存在NN中,那么atime相关的修改也肯定发生在与NN交互的这个过程中。

从之前的文章中可知入口函数是FSNamesystem.getBlockLocations,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
LocatedBlocks getBlockLocations(String clientMachine, String srcArg,
long offset, long length) throws IOException {
...
if (res.updateAccessTime()) {
String src = srcArg;
writeLock();
final long now = now();
try {
final INodesInPath iip = dir.resolvePath(pc, src);
src = iip.getPath();
INode inode = iip.getLastINode();
// 再次判断是否可以更新atime
boolean updateAccessTime = inode != null &&
now > inode.getAccessTime() + getAccessTimePrecision();
if (!isInSafeMode() && updateAccessTime) {
// 设置atime
boolean changed = FSDirAttrOp.setTimes(dir,
inode, -1, now, false, iip.getLatestSnapshotId());
if (changed) {
getEditLog().logTimes(src, -1, now);
}
}
} catch (Throwable e) {
LOG.warn("Failed to update the access time of " + src, e);
} finally {
writeUnlock(operationName);
}
}
...
}

res.updateAccessTime()决定了是否更新atime,其值是在getBlockLocationsInt中赋值的,代码如下:

1
2
3
boolean updateAccessTime = isAccessTimeSupported() && !isInSafeMode()
&& !iip.isSnapshot()
&& now > inode.getAccessTime() + getAccessTimePrecision();

其中关键的因素是isAccessTimeSupported()getAccessTimePrecision(),这个两个方法都与accessTimePrecision有关,此值是由dfs.namenode.accesstime.precision设置的,默认是3600000。
当此值大于0,isAccessTimeSupported()返回true,getAccessTimePrecision()得到的值是dfs.namenode.accesstime.precision的值。

从上述代码中可以看出更新atime的一个条件是两次读取间隔相隔dfs.namenode.accesstime.precision秒,默认是1小时。

这里遗留两个问题
1、新建文件时atime如何赋值
2、修改文件内容时atime如何赋值

关于这个两个问题我在下一节在写流程中解答。请继续向下看

mtime

同样去年也写过一篇关于写的文章HDFS write解析介绍HDFS读操作流程,这里就不再累赘了,直接贴出关键代码。(有兴趣的同学可以自行查看)

写相关的操作包括create、close和append
写文件有两种方式,一种是调用create(Path)方法,另一种是调用append(Path)方法

create

通过调用create(Path),最终会调用FSDirectory.addFile方法,在此方法中会new一个INodeFile,此时会设置mtime和atime为同一个值,代码如下:

1
2
3
4
5
6
7
8
9
10
INodesInPath addFile(INodesInPath existing, String localName, PermissionStatus
permissions, short replication, long preferredBlockSize,
String clientName, String clientMachine)
throws FileAlreadyExistsException, QuotaExceededException,
UnresolvedLinkException, SnapshotAccessControlException, AclException {
long modTime = now();
INodeFile newNode = newINodeFile(allocateNewInodeId(), permissions, modTime,
modTime, replication, preferredBlockSize);
...
}

有打开一个文件就有关闭一个文件,接下来看下关闭文件时atime和mtime会有什么变化。

close

closeFile()finalizeINodeFileUnderConstruction中调用,在此方法中会设置mtime,看下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void finalizeINodeFileUnderConstruction(String src,
INodeFile pendingFile, int latestSnapshot) throws IOException {
...
pendingFile.toCompleteFile(now());
...
closeFile(src, pendingFile);
...
}

// INodeFile.java
public INodeFile toCompleteFile(long mtime) {
Preconditions.checkState(isUnderConstruction(),
"file is no longer under construction");
FileUnderConstructionFeature uc = getFileUnderConstructionFeature();
if (uc != null) {
assertAllBlocksComplete();
removeFeature(uc);
this.setModificationTime(mtime);
}
return this;
}

从代码中可以看出,close文件时只对mtime进行了修改。

append

append只是打开一个文件流,并不会修改mtime或者atime,只是在close的时候修改mtime

结论

atime

1、两次读间隔大于默认的1小时时,更新atime。默认间隔通过dfs.namenode.accesstime.precision控制。
2、新建一个文件时atime赋值为当前时间(注意,当关闭一个文件时atime不会修改)

mtime

1、新建一个文件时mtime赋值为当前时间(同时会修改atime)
2、关闭一个文件时mtime赋值为当前时间(此时并不会修改atime)

您的肯定,是我装逼的最大的动力!