HDFS中的数据都是分块存储的,默认块大小为64M,这个值可以从hdfs配置文件中更改dfs.blocksize。这样分快处理的好处是可以增加读取数据的吞吐量,因为可以同时从读取文件的不同分块。
一、数据写入
在客户端想HDFS写数据的过程中,主要分为下面几个过程:
- 客户端将数据缓存到本地的一个临时文件中;
- 当这个本地的临时文件到达HDFS中的块大小限制时,客户端访问Namenode,Namenode将文件的名字插入到HDFS命名空间中,并且为其分配相应的存储位置;
- Namenode与分配好的Datanode进行沟通,确定存储位置可用,然后将这些存储位置信息返回给客户端;
- 客户端将本地的临时文件传输到Datanode中;
- 当写文件结束,临时文件关闭时,会将已有的临时数据传输到Datanode中,并告知Namenode写数据完成;
- Namenode将该文件改变为持久的一致性状态,也就事将该操作记录到日志EditLog中。如果此时Namenode宕掉,那么文件信息丢失。
上面的过程主要特点是写入数据先缓存到本地,在达到块大小限制时才与Datanode通信进行传输。这样的好处在于避免在客户写数据的过程中持续占用网络带宽,这对于处理多用户大量数据的写入是非常关键的。
二、数据备份
数据的写入同时伴随这数据块的备份,过程如下:
- 在客户端临时数据达到一个块时,与Namenode通信,得到一组Datanode地址,这些Datanode就是用来存储该数据块的;
- 客户端首先将该数据块发送到一个Datanode上,Datanode在接受时是以4kb为单位进行,我们把这些小单位称为缓存页(参考了Linux管道文件的说法);
- 对于第一个接到数据的Datanode,它把缓存页中的数据写入自己的文件系统,另一方面,它又将这些缓存页传送给下一个Datanode;
- 重复3的过程,第二个Datanode又将缓存页存储在本地文件系统,同时将它传送给第三个Datanode;
- 如果HDFS中的备份数目设置为3,那么第三个Datanode就只需要将缓存页存储即可。
上面的过程中,数据块从客户端流向第一个Datanode,然后再流向第二个,从第二个再到第三个,整个是一个流水线过程,中间不会有停顿。所以HDFS将它称为Replication Pipelining。
为什么不采取客户端同时向多个Datanode写数据的方法呢?其实从Pipelining这个称呼上就可以猜到,客户端和Datanode采用的缓存文件都是管道文件,即只支持一次读取。
三、 数据删除
HDFS中的数据删除也是比较有特点的,并不是直接删除,而是先放在一个类似回收站的地方(/trash),可供恢复。
对于用户或者应用程序想要删除的文件,HDFS会将它重命名并移动到/trash中,当过了一定的生命期限以后,HDFS才会将它从文件系统中删除,并由Namenode修改相关的元数据信息。并且只有到这个时候,Datanode上相关的磁盘空间才能节省出来,也就是说,当用户要求删除某个文件以后,并不能马上看出HDFS存储空间的增加,得等到一定的时间周期以后(现在默认为6小时)。
对于备份数据,有时候也会需要删除,比如用户根据需要下调了Replicaion的个数,那么多余的数据备份就会在下次Beatheart联系中完成删除,对于接受到删除操作的Datanode来说,它要删除的备份块也是先放入/trash中,然后过一定时间后才删除。因此在磁盘空间的查看上,也会有一定的延时。
那么如何立即彻底删除文件呢,可以利用HDFS提供的Shell命令:bin/Hadoop dfs expunge清空/trash。
关于HDFS打开、读取、写入文件的操作,觉先博文 Hadoop学习总结之二:HDFS读写过程解析 给出了很好的说明,可供参考。
上面写的内容,基本上是来自HDFS官方文档,然后稍微加上一些自己的看法,很多地方也没有从Hadoop源码中去求证。如果有不对的地方,还请高手指正,不胜感激!