背景:
hdfs文件系统有很多小文件,这些小文件会定期合并到大文件中,合并完成之后要删除这些小文件。但是有可能当前其他人正在读取这些小文件,此时如果将这些小文件删除,会导致当前用户无法正确获取到文件。
这一点hdfs做的有点恶心:最初我的理解是如果有线程A正在读取这个小文件,其他线程B删除这个小文件时应该会失败(至少应该要抛出异常)。但是测试发现不是这样的。实际的结果是:线程B默默的将小文件直接删除了,导致线程A只下载了这个文件的一部分,然后就卡在这里不动了。
针对这个问题的一个解决方案是:小文件被合并到大文件之后,保留30分钟后再删除,具体做法是通过FileStatus中的accesstime来获取上次的访问时间,如果当前时间减去accesstime超过了30分钟才真正的删除这个小文件。(也就是给30分钟的缓冲时间。小文件合并到到文件之后,以后新的请求都是从大文件中下载数据)
但是问题又出现了。
@Test
public void fileStatusByRead() {
// modification_time=1426037170715; access_time=1426037170138
String desPath = "hdfs://******//8000000014268147637640-793038384000000014260371637637117";
FSDataInputStream in = null;
OutputStream outputStream = null;
// 初始化主集群
HDFSSystem hdfsSystem;
try {
hdfsSystem = new HDFSSystem();
FileStatus fileStatus = hdfsSystem.getFileStatus(new Path(desPath));
System.out.println("+++1:" + fileStatus.toString());
Thread.sleep(82 * 1000);
in = hdfsSystem.open(new Path(desPath));
long copySize = 8;
outputStream = new FileOutputStream("D:\\input\\ttttt1.txt");
// 读取数据
IOUtils.copyBytes(in, outputStream, copySize, true);
fileStatus = hdfsSystem.getFileStatus(new Path(desPath));
System.out.println("+++2:" + fileStatus.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
测试发现System.out.println("+++1:" + fileStatus.toString()); 和System.out.println("+++2:" + fileStatus.toString());打印的时间居然一模一样
解决方法:
修改hdfs-site.xml中的
<property>
<name>dfs.namenode.accesstime.precision</name>
<value>3600000</value>
<description>The access time for HDFS file is precise upto this value.
The default value is 1 hour. Setting a value of 0 disables
access times for HDFS.
</description>
</property>
将默认的一小时改为10分钟。 但是这回带来一定的性能消耗。理想的解决办法是将文件的保留时间修改为1个小时或者2个小时。