HDFS上传下载
1.HDFS文件块
HDFS中的文件在物理上是分块存储(block),块的大小可以通过配置参数 dfs.blocksize
来规定,默认大小在hadoop2.x版本中是128M,老版本中是64M。
1.1.详解128M
文件块的大小有两处矛盾点:
块的容量设置的小
容量设置的小的话,块的数量就会变多,这会导致:
-
块的数量增多导致块的寻址变慢.
-
每一个块在NameNode的内存里都会存储一份元数据,块的数量增多会增加NameNode内存压力。
块的容量设置的大
容量设置的大的话,每一个块的传输时间就会变长,这会导致:
- 网络不稳定时,重传成本较大(块越大重传的就越多)。
- hdfs存储的数据是给mapreduce任务使用的,块的容量变大,map任务的加载时间就会变长,不利于map任务失败后的重试执行。
1.2.如何设置合适的块大小
最终经过经验和统计分析,得到两个数据:
- hdfs平均寻址时间为10ms。
- 寻址时间占传输时间1%时,效果最好。
所以传输时间为10ms*100=1s,也就是说block块的大小设置为磁盘一秒的读写量最好。
大部分磁盘的读写速度为100M/s左右,block大小默认为最接近100M/s的128M/s(要取2进制整数)。
也就是说如果你的磁盘是高I/O盘,比如读写速度在200M/s,那么你可以修改你的block块大小为256M,通过修改参数 dfs.blocksize
实现。
2.HDFS上传文件
2.1.上传流程
简单说一下hdfs客户端是如何上传文件的。
举个栗子:现在需要通过客户端向HDFS上传一个文件,在HDFS的路径是**/log/audit_4_17.zip**,文件大小为200M。
block块大小为128M,所以需要拆分成两个block上传:block1(128M),block2(72M)
副本数为3,文件**/log/audit_4_17.zip**会存储3份
1)客户端向namenode请求上传文件。
2)namenode检查目标文件是否已存在,父目录是否存在, 客户端是否有权限…, 检查通过返回允许上传。
3)客户端申请上传第一个 block。
4)namenode返回3个datanode节点的地址,分别为DataNode1、DataNode2、DataNode3。
5)客户端向DataNode1申请上传数据,DataNode1收到后申请会继续调用DataNode2,然后DataNode2调用DataNode3,将这个通信管道建立完成
6)DataNode1、DataNode2、DataNode3逐级应答客户端
7)客户端开始往dn1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,DataNode1收到一个packet就会传给DataNode2,DataNode2传给DataNode3;每个DataNode同步一个packet成功后,会返回一个ack packet,并放入ack queue
8)DataNode1向NameNode响应block1传输成功
8)当一个block传输完成之后,客户端再次请求namenode上传第二个block。(重复执行3-8步)
2.2.block和packet
block
文件存储在HDFS中时需要分块,这个块就是block,一般为128MB,它是HDFS的存储单元。
packet
向HDFS传输block时需要将block拆分成若干个packet包去传输,packet是client和DataNode1,众DataNode之间传输的数据单元,默认大小64KB。
拆分的目的是为了并行同步,DataNode1收到packet1后,可以立刻向DataNode2同步packet1
2.3.数据同步Pipline
回顾上传流程
当packet成功接收后,接收方返回确认消息至发送方,即:
- DataNode1接收并成功写入后,它会向下一个DataNode(例如DataNode2)发送数据。
- DataNode2在接收并成功写入后,会向DataNode1发送确认消息(一个ACK)。
- DataNode1在收到DataNode2的ACK后,会向客户端发送一个ACK。这意味着,只有当所有的DataNode都成功接收并写入packet,客户端才会收到含有所有确认信息的ACK。
为此HDFS在DataNode设计了两个线程,对于DataNode1而言:一个线程用于接收数据并写入本地,一个线程用于接收DataNode2返回的ack信息。这使得数据的传输和确认得以并发执行。
Ack Queue负责存储ack消息,是一个阻塞队列,存储的是packet。当pipeline中的所有datanode都表示已经收到的时候,ack quene才会把对应的packet包移除掉。
这个机制确保每个packet的传输都得以成功,而且传输失败后也能方便重传。
2.4.传输失败后的重传
如果在写的过程中某个datanode发生错误,会采取以下几步:
- pipeline被关闭掉;
- 为了防止防止丢包ack quene里的packet会同步到data quene里;
- 把产生错误的datanode上当前在写但未完成的block删掉;
- block剩下的部分被写到剩下的两个正常的datanode中;
- namenode找到另外的datanode去创建这个块的复制。当然,这些操作对客户端来说是无感知的。
2.5.机架感知
HDFS存储文件时,为了保证可靠性,会复制文件的block,存储到多个节点中,以保障可靠性。
HDFS是如何规划block应该落在哪个DataNode的呢。
举个栗子,现在有两个集,每个集群有两个机架,每个机架有2节点。现在有个block1需要存储,HDFS会把他安排在哪些节点呢?
低版本
第一个副本在client所处的节点上。如果客户端在集群外,随机选一个。
第二个副本和第一个副本位于不相同机架的随机节点上。
第三个副本和第二个副本位于相同机架,节点随机。
高版本
第一个副本在client所处的节点上。如果客户端在集群外,随机选一个。
第二个副本和第一个副本位于相同机架,随机节点。
第三个副本位于不同机架,随机节点。
3.HDFS读取数据流
假设要从HDFS读取文件,文件路径为/log/audit_4_17.zip,流程如下:
1)客户端向namenode请求下载文件,namenode通过查询元数据,找到文件块所在的datanode地址。
2)挑选一台datanode(就近原则,然后随机)服务器,请求读取数据。
3)datanode开始传输数据给客户端(从磁盘里面读取数据放入流,以packet为单位来做校验)。
4)客户端以packet为单位接收,先在本地缓存,然后写入目标文件。