9.6.3 一致性模型
文件系统的一致性模型描述了文件读/写的可见性。HDFS牺牲了一些POSIX的需求来补偿性能,所以有些操作可能会和传统的文件系统不同。
当创建一个文件时,它在文件系统的命名空间中是可见的,代码如下:
Path p=new Path("p");
fs.create(p);
assertThat(fs.exists(p),is(true));
但是对这个文件的任何写操作不保证是可见的,即使在数据流已经刷新的情况下,文件的长度很长时间也会显示为0:
Path p=new Path("p");
OutputStream out=fs.create(p);
out.write("content".getBytes("UTF-8"));
out.flush();
assertThat(fs.getFileStatus(p).getLen(),is(0L));
一旦一个数据块写入成功,大家提出新的请求就可以看到这个块,而对当前写入的块,大家是看不见的。HDFS提供了所有缓存和DataNode之间的数据强制同步的方法,这个方法是FSDataOutputStream中的sync()函数。当sync()函数返回成功时,HDFS就可以保证此时写入的文件数据是一致的并且对于所有新的用户都是可见的。即使HDFS客户端之间发生冲突,也不会发生数据丢失,代码如下:
Path p=new Path("p");
FSDataOutputStream out=fs.create(p);
out.write("content".getBytes("UTF-8"));
out.flush();
out.sync();
assertThat(fs.getFileStatus(p).getLen(),is(((long)"content".length())));
这个操作类似于UNIX系统中的fsync系统调用,为一个文件描述符提交缓存数据,利用Java API写入本地数据,这样就可以保证看到刷新流并且同步之后的数据,代码如下:
FileOutputStream out=new FileOutputStream(localFile);
out.write("content".getBytes("UTF-8"));
out.flush();//flush to operating system
out.getFD().sync();//sync to disk
assertThat(localFile.length(),is(((long)"content".length())));
在HDFS中关闭一个文件也隐式地执行了sync()函数,代码如下:
Path p=new Path("p");
OutputStream out=fs.create(p);
out.write("content".getBytes("UTF-8"));
out.close();
assertThat(fs.getFileStatus(p).getLen(),is(((long)"content".length())));
下面来了解一致性模型对应用设计的重要性。文件系统的一致性和设计应用程序的方法有关。如果不调用sync(),那么需要做好因客户端或者系统发生故障而丢失部分数据的准备。对大多数应用程序来说,这是不可接受的,所以需要在合适的时刻调用sync(),比如在写入一定量的数据之后。尽管sync()被设计用来最大限度地减少HDFS的负担,但是它仍然有不可忽视的开销,所以需要在数据健壮性和吞吐量之间做好权衡。其中一个好的参考平衡点就是:通过测试应用程序来选择不同sync()频率间性能的最佳平衡点。