对于一致性,可以分为从客户端和服务端两个不同的视角。
从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。一致性是因为有并发读写才有的问题,因此在理解一致性的问题时,一定要注意结合考虑并发读写的场景。
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性
从服务端角度,如何尽快将更新后的数据分布到整个系统,降低达到最终一致性的时间窗口,是提高系统的可用度和用户体验非常重要的方面。对于分布式数据系统:
- N — 数据复制的份数
- W — 更新数据时需要保证写完成的节点数
- R — 读取数据的时候需要读取的节点数
如果W+R>N,写的节点和读的节点重叠,则是强一致性。例如对于典型的一主一备同步复制的关系型数据库,N=2,W=2,R=1,则不管读的是主库还是备库的数据,都是一致的。
如果W+R<=N,则是弱一致性。例如对于一主一备异步复制的关系型数据库,N=2,W=1,R=1,则如果读的是备库,就可能无法读取主库已经更新过的数据,所以是弱一致性。
对于分布式系统,为了保证高可用性,一般设置N>=3。不同的N,W,R组合,是在可用性和一致性之间取一个平衡,以适应不同的应用场景。
- 如果N=W,R=1,任何一个写节点失效,都会导致写失败,因此可用性会降低,但是由于数据分布的N个节点是同步写入的,因此可以保证强一致性。
- 如果N=R,W=1,只需要一个节点写入成功即可,写性能和可用性都比较高。但是读取其他节点的进程可能不能获取更新后的数据,因此是弱一致性。这种情况下,如果W<(N+1)/2,并且写入的节点不重叠的话,则会存在写冲突
文件系统的一致性模型描述了文件读/写的可见性。
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 FileOutStream(localFile); out.write("content".getBytes("UTF-8")); out.flush(); // flush to operatig system out.getFD().sync(); // sync to disk assertThat(fs.getFileStatus(p),getLen(),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()频率间的最佳平衡点。
本文转自大数据躺过的坑博客园博客,原文链接:http://www.cnblogs.com/zlslch/p/5140724.html,如需转载请自行联系原作者