HDFS的读写流程 —— Hadoop权威指南4

1. 客户端读取HDFS文件的流程

1.1 具体的流程

在这里插入图片描述

  1. 初始化FileSystem,client调用FileSystem对象的open()方法,打开一个HDFS文件。实际,FileSystem对象是一个DistributedFileSystem实例

  2. DistributedFileSystem通过RPC调用NameNode,获取一批文件block的位置列表。其中,每个block的副本所在的DataNode,是按照它们与客户端的距离排序的

  3. DistributedFileSystem向客户端返回一个FSDataInputStream对象(open()方法的返回结果),用于数据的读取。FSDataInputStream中包含了DFSInputStream对象,它管理着DataNode和NameNode的IO

    以上对应图中的步骤1和2

  4. 客户端调用FSDataInputStream对象的read()方法,实际:存有block位置的DFSInputStream会连接最近的DataNode,对该DataNode反复执行read(),从而将数据从DataNode传到client。

    DFSInputStream发现DataNode故障,会记住该DataNode并从最近的另一个DataNode读取副本,以后保证不会反复从该DataNode读取block
    DFSInputStream会对读取到的数据做checksum,如果发现数据损坏,会从其他DataNode读取副本并向NameNode通知损坏的block信息

  5. DFSInputStream完成block的读取,会关闭与该DataNode的连接。接着,创建与下一个block的最佳DataNode的连接,继续进行block的读取。

  6. DFSInputStream中存储的block位置可能是文件的部分block,它会按照需要从NameNode获取下一批block的位置信息。

    以上对应步骤3、4和5

  7. client读取数据完毕,则调用FSDataInputStream对象的close()方法关闭输入流

1.2 架构优势
  • client从NameNode获取block的位置(多副本,排序后的DataNode地址),然后直接与DataNode连接读取数据,这样的设计允许HDFS扩展到大量的并发client
  • NameNode负责响应获取block位置的请求,而不负责响应数据请求,极大减轻了NameNode的压力。避免客户端增长后,NameNode称为系统瓶颈。
1.3 Hadoop对节点距离的描述
  • Hadoop集群的节点,可能位于同一机架、同一机房不同机架、不同机房
  • HDFS读写文件block时,client只有读取最近的DataNode才能提高数据读写的速度
  • 因此,如何定义节点间的距离非常重要 —— 使用带宽描述两个节点之间的距离
  • 针对以下场景,带宽将依次递减:
    ① 同一节点上的两个进程
    ② 同一机架上的不同节点
    ③ 同一数据中心(也就是机房)的不同机架
    ④ 不同数据中心
  • 两个节点间的距离,是它们到最近共同祖先的距离之和
  • 因此,图示中的节点距离就非常清楚了:
    ① 节点1上的两个进程的距离为0
    ② 节点1和节点2的距离为2
    ③ 节点1和节点3的距离为4
    ④ 节点1和节点4的距离为6
    在这里插入图片描述
  • 注意:
    Hadoop无法自动发现网络拓扑结构,需要进行手动配置。一般情况下,默认集群同一数据中心的同一机架上

2. HDFS数据写入流程

2.1 HDFS中block、packet、chunk
  • 之前有说过,HDFS中数据存储单位是block,从Hadoop 2.7.3开始,block的默认大小从64MB变成128MB
  • client向DataNode、以及DataNode的pipeline之间,数据传输的第二大单位是packet,为64 kb
  • client向DataNode、以及DataNode的pipeline之间,数据校验的基本单位是chunk,默认为512 byte
  • 每个chunk通过校验后,会带上4 byte的校验信息。因此,实际写入packet的chunk大小为516 byte
2.2 client的数据三层缓存
  • 上传的文件,是按block进行切分的。例如,一个300MB的文件,将会被切分成128MB、128Mb、44MB三个块

写过程会涉及chunk、packet、DataQueue/AckQueue这样的三层缓存

  1. 数据流入DFSOutputStream时,会先写入一个chunk大小的缓冲区。当数据写满一个chunk时,或遇到强制的flush()操作时,会计算校验和(checksum)
  2. 通过数据校验的chunk会加上校验和,一起写入packet中。当packet被chunk填满时,packet会发送到DataQueue中
  3. DataStreamer负责将DataQueue中的packet发送到最佳的DataNode中。同时,由于packet此时未确认写入成功,因此会被移动到AckQueue中等待写入确认。
  4. 当收到足够的确认应答(ack)后,ResponseProcessor会将packet从AckQueue中移除;否则, 会将其恢复到DataQueue的最前端,以保证没有packet丢失。因此,写入成功的packet在DataQueue和AckQueue中,应该都是不存在的。
    在这里插入图片描述
2.3 HDFS的写过程

在这里插入图片描述

  1. 初始化FileSystem对象,实际是一个DistributedFileSystem实例。

  2. clinet调用DistributedFileSystemcreate()方法,创建一个HDFS文件。DistributedFileSystem会通过RPC调用NameNode,进行文件创建的检查:client的权限检查、是否已存在该文件等。

  3. 通过检查后,NameNode会向EditLog写入一条新建文件的记录(WAL),然后DistributedFileSystem会向client返回一个FSDataOutputStream对象;否则,文件创建失败并向client抛出IOException异常

  4. FSDataOutputStream对象中包含了DFSOutputStream对象,DFSOutputStream负责管理DataNode和NameNode的IO

    对应图中的步骤1、2

  5. client调用FSDataOutputStream对象的write()方法向DataNode写入数据。实际,DFSOutputStream会将数据划分成一个个packet,先将packet存如DataQueue中。

  6. DataStreamer负责管理DataQueue,它挑选出一组适合存储副本的DataNode,并以此来要求NameNode分配适合新的block。这组DataNode之间会形成pipeline,packet通过pipeline进行传输

    • DataStreamer将packet从DataQueue发送至第一个DataNode
    • 第一个DataNode将packet发送给第二个DataNode
    • 第二个DataNode将packet发送给第三个DataNode,从而实现packet的三副本存储
  7. 同时,DataStreamer还会将未确认成功的packet移入AckQueue中,只有收到三个DataNode的ack packet后,ResponseProcessor才会将packet从AckQueue中移除

    对应步骤3、4、5

  8. 如果写入期间某个DataNode故障:

    • 首先会关闭pipeline,将AckQueue中所有packet都放回到DataQueue中了,以保证故障DataNode的下游DataNode不会出现数据丢失;
    • 存储在正常DataNode中的block会被做标记,并发送给NameNode,以便故障DataNode恢复时能删除不完整的block
    • 在剩下的两个DataNode中重新建立pipeline,余下的数据会继续写入正常的DataNode。
    • NameNode在观察到副本数不够时,会在另一个DataNode上创建新的副本
    • block成功写入的副本数满足最小副本数dfs.namenode.replication.min,默认为1),则认为写入成功。后续通过异步的副本复制,来达到副本数要求。

    故障情况的处理

  9. client完成数据写入,调用DistributedFileSystemclose()方法,关闭数据流

  10. DistributedFileSystem会调用通知NameNode文件写入完成前,等待确认。实际,只需要等待block满足最小副本数,就可以确认写入成功。

    对应图中的步骤6、7

3. 其他知识

3.1 关于副本放置策略
  • HDFS在进行写入时,会尽量不在一个机架放置过多的副本
  • 以三副本为例:
    ① 如果client本身就是一个DataNode,则将副本1写入本地;否则,在集群中随机选择一个节点。都记为 r 1 / d 1 r1/d1 r1/d1
    ② 第二个副本放置在随机选择的、不同机架的DataNode上,记为 r 2 / d 2 r2/d2 r2/d2
    ③ 第三个副本放置在上一副本相同的机架、随机选择的DataNode上,记为 r 2 / d 3 r2/d3 r2/d3
3.2 关于hflush()
  • 文件系统的一致性模型:文件读/写数据的可见性
  • HDFS为了性能,牺牲了一些POSIX要求:当前正在写入的block,对其他reader不可见

HDFS提供了将缓存中的数据刷新到DataNode的方法:

  1. hflush()
    ① 在Hadoop 1.x中叫做sync(),可以保证将数据写入DataNode的内存,但不保证数据写入磁盘 —— 存在断电数据丢失的风险
    ② 保证文件中到目前为止写入的数据均到达所有DataNode的pipeline中,并且对所有新的reader可见 (其实,自己并不是很理解 😂 )
  2. hsync() :可以保证将数据写入到DataNode的磁盘,在Hadoop 1.x中未提供
  3. hflush()hsysnc()的使用都会带来额外的开销,如果想要数据写入的稳定性,需要以适合的频率调用 hflush()hsysnc()方法
3.3 关于distcp
  • Hadoop中自带一个应用程序distcp,可以将大量文件复制到Hadoop中,或者从Hadoop3文件系统中复制大量文件
  • hadoop fs -cp的替代,可以实现文件/目录的复制
  • 实质: distcp是一个MR作业,只有map任务,没有reduce任务
  • 一般map任务的个数为20,但可以通过-m参数进行指定。
  • 如果指定一个map任务,则文件的所有block的第一个副本都会存储到map对应DataNode上

4. 总结

  • HDFS的读流程:

    1. open()方法 + get block locations,获取block的副本位置,返回FSDataInputStream,内含DFSDataStream
    2. 调用FSDataInputStreamread()方法,实际由DFSDataStream与最佳DataNode创建连接,不断调用read()读取数据;
    3. 结束一个block的读取,DFSDataStream与下一个block的最佳DataNode连接连接,继续进行数据读取
    4. 必要时,DFSDataStream从NameNode处获取下一批block的位置
    5. 调用FSDataInputStreamclose()方法,关闭输入流
  • HDFS读流程中的故障:

    1. DataNode故障,记住该DataNode,切换到最邻近的、有相同副本的DataNode
    2. 读数据时进行checksum,发现block损坏,切换到另一副本继续读。同时向NameNode上报损坏的block
  • HDFS的三种数据块:数据存储单位block(128 MB)、数据传输单位packet(64 kb)、数据校验单位chunk(512 byte + 4 byte的校验信息

  • HDFS基于chunk、packet、DataQueue/AckQueue的三层缓存

    1. chunk写满,校验后写入packet
    2. packet写满,放到DataQueue
    3. DataStreamer将DataQueue中的packet发送给DataNode,并在未确认写入ok时,将packet存入AckQueue
    4. 收到足够的应答,ResponseProcessor将packet从AckQueue移除;否则,移回DataQueue中,重新写入
  • HDFS的写流程

    1. create() + create, NameNode进行权限、是否已存在等校验;通过校验,向EditLog新增一条记录(WAL)并返回FSDataOutputStream,内含DFSOutputStream;否则,抛出IOException异常
    2. 调用FSDataOutputStreamwrite()方法,实际由DFSOutputStream将数据分成一个个packet放入DataQueue
    3. DataStreamer负责选取一组DataNode进行数据写入,这些DataNode之间会建立pipeline,packet通过pipeline进行传输
    4. 为确认写入ok的packet放入AckQueue,只有收到足够的ack packet,才会从AckQueue中移除
    5. 调用FSDataOutputStreamclose()方法,关闭写入流;NameNode执行complete,等待文件写入完成前,确认成功写入
  • HDFS写流程故障:

    1. 删除pipeline、将AckQueue中的所有packet移动到DataQueue的前端,确保数据不会丢失
    2. 标记当前block,并向NameNode上报信息,等到故障DataNode恢复,删除不完整的block
    3. 在正常的DataNode之间重新建立pipeline,继续进行数据写入
    4. NameNode发现副本数不足,在其他DataNode创建新的副本(异步副本复制?)
    5. 写入成功:只要满足最小副本数,即可认为写入成功
  • 副本的放置策略:随机选择,尽量不在一个机架放置过多的副本

  • hflush()hsync()方法:以适合的频率调用,保证写入数据对新的reader的可见性

  • ditscp:实质是只有map任务的MR作业,可以通过-m指定map任务数

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
  本书从hadoop的缘起开始,由浅入深,结合理论和实践,全方位地介绍hado叩这一高性能处理海量数据集的理想工具。全书共14章,3个附录,涉及的主题包括:haddoop简介:mapreduce简介:hadoop分布式文件系统;hadoop的i/o、mapreduce应用程序开发;mapreduce的工作机制:mapreduce的类型和格式;mapreduce的特性:如何安装hadoop集群,如何管理hadoop;pig简介:hbase简介:zookeeper简介,最后还提供了丰富的案例分析。   本书是hadoop权威参考,程序员可从中探索如何分析海量数据集,管理员可以从中了解如何安装与运行hadoop集群。   什么是谷歌帝国的基石?mapreduce算法是也!apache hadoop架构作为mapreduce算法的一种开源应用,是应对海量数据的理想工具。项目负责人tomwhite透过本书详细阐述了如何使用hadoop构建可靠、可伸缩的分布式系统,程序员可从中探索如何分析海量数据集,管理员可以从中了解如何安装和运行hadoop集群。   本书结合丰富的案例来展示如何用hadoop解决特殊问题,它将帮助您:    ·使用hadoop分布式文件系统(hdfs)来存储海量数据集,   通过mapreduce对这些数据集运行分布式计算    ·熟悉hadoop的数据和ilo构件,用于压缩、数据集成、序列化和持久处理    ·洞悉编~mapreduce实际应用时的常见陷阱和高级特性    ·设计、构建和管理一个专用的hadoop集群或在云上运行hadoop    ·使用高级查询语言pig来处理大规模数据    ·利用hadoop数据库hbase来保存和处理结构化/半结构化数据    ·学会使用zookeeper来构建分布式系统   如果您拥有海量数据,无论是gb级还是pb级,hadoop都将是您的完美解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值