2020/12/7 sunhaiqi@bonc.com.cn
文章目录
一、HDFS中的数据写入读出原理
1.1、HDFS写数据流程
hadoop fs -put /home/bduser/xxx.txt /animals/tiger/
当我们执行上面这条语句时,集群将执行以下几个步骤(以一台NameNode,三台DataNode的完全分布式集群为例)
- 1.1.1、客户端向NameNode发送请求:上传文件
- 检查该文件是否存在于文件系统,如果存在,则提示该文件已经存在与目标目录下
- 检查该文件上传的目标目录是否存在,如果不存在,则提示没有那个文件或目录
- 1.1.2、NameNode返回消息
- 如果存在1.1中的两种失败情况,将会返回客户端数据上传失败的原因,客户端将其输出至控制台
- 如果不存在1.1中的两种失败情况,将会返回客户端可以上传文件的消息
- 1.1.3、客户端接收到1.2返回的可以上传消息后,再次向NameNode发送请求:应当把数据块发送至哪几个DataNode节点上?
- 1.1.4、NameNode得到1.3的请求会返回几个节点()以及存储位置,例如node01、node02、node03的什么什么路径
- 1.5、客户端得到1.4NameNode返回的信息后将向临近节点发送建立联系的请求,由节点之间一个请求一个的建立起数据通道
- 1.1.6、如果所有节点的数据通道都建立成功,将返回可以上传数据的消息
- 1.1.7、客户端得到可以上传数据的消息之后,将第一个block块放入本地内存缓存中,以packet为单位传送至临近的节点上,该节点传给其临近的节点以此类推完成传数据递。(遵循http数据传输协议)
- 1.1.8、当第一个数据块传输完毕后,将会传输剩余的数据块,重复执行上述步骤。。。
- 1.1.9、当该文件的所有数据块都上传完毕之后,将会在NameNode上创建该文件的元数据
1.2、HDFS读数据流程
1.2.1、客户端向NameNode发送请求:下载文件xxx
1.2.2:、NameNode查询元数据,找到文件块的地址,返回目标的元数据
1.2.3、客户端按照就近原则向其中的一台机器发起读数据块1请求
1.2.4、DataNode将数据传输会客户端
1.2.5、客户端按照2.3、2.4、的步骤重复执行读数据块2…
1.2.5、客户端将以packet为单位接收数据块,放入本地缓存,然后存入目标目录
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JA7y36Ub-1614602217937)(C:\Users\19701\AppData\Roaming\Typora\typora-user-images\1607507914176.png)]

二、HDFS文件系统的一致性
对于一致性,可以分为从客户端和服务端两个不同的视角
从客户端来看,一致性主要指的是多并发访问时更新过的数据如何获取的问题。从服务端来看,则是更新如何复制分布到整个系统,以保证数据最终一致。一致性是因为有并发读写才有的问题,因此在理解一致性的问题时,一定要注意结合考虑并发读写的场景。
2.1、从客户端解读文件系统一致性
从客户端角度,多进程并发访问时,更新过的数据在不同进程如何获取的不同策略,决定了不同的一致性。对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性。
2.2、从服务端解读文件系统一致性
从服务端角度,如何尽快将更新后的数据分布到整个系统,降低达到最终一致性的时间窗口,是提高系统的可用度和用户体验非常重要的方面。对于分布式数据系统:
- 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,并且写入的节点不重叠的话,则会存在写冲突
2.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 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()));
debug调试如下代码
@Test
public void writeFile() throws Exception{
// 1 创建配置信息对象
Configuration configuration = new Configuration();
fs = FileSystem.get(configuration);
// 2 创建文件输出流
Path path = new Path("hdfs://node102:8020/user/bduser/hello.txt");
FSDataOutputStream fos = fs.create(path);
// 3 写数据
fos.write("hello".getBytes());
// 4 一致性刷新
fos.hflush();
fos.close();
}
写入数据时,如果希望数据被其他client立即可见,调用如下方法
FsDataOutputStream. hflush (); //清理客户端缓冲区数据,被其他client立即可见