深入理解HDFS
本文详细介绍了HDFS中的许多概念,对于理解Hadoop分布式文件系统很有帮助。
1. 介绍
在现代的企业环境中,单机容量往往无法存储大量数据,需要跨机器存储。统一管理分布在集群上的文件系统称为分布式文件系统。而一旦在系统中引入网络,就不可避免地引入了所有网络编程的复杂性,例如挑战之一是如果保证在节点不可用的时候数据不丢失。
传统的网络文件系统(NFS)虽然也称为分布式文件系统,但是其存在一些限制。由于NFS中,文件是存储在单机上,因此无法提供可靠性保证,当很多客户端同时访问NFS Server时,很容易造成服务器压力,造成性能瓶颈。
HDFS,是Hadoop Distributed File System的简称,是Hadoop抽象文件系统的一种实现。Hadoop抽象文件系统可以与本地系统、Amazon S3等集成,甚至可以通过Web协议(webhsfs)来操作。HDFS的文件分布在集群机器上,同时提供副本进行容错及可靠性保证。
2. HDFS设计原则
2.1 设计目标
- 存储非常大的文件:这里非常大指的是几百M、G、或者TB级别。实际应用中已有很多集群存储的数据达到PB级别。
- 采用流式的数据访问方式:最有效的数据处理模式是一次写入、多次读取。数据集经常从数据源生成或者拷贝一次,然后在其上做很多分析工作。
- 运行于商业硬件上:Hadoop不需要特别贵的、reliable的机器,可运行于普通商用机器。商用机器不代表低端机器,在集群中节点失败率是比较高的,HDFS的目标是确保集群在节点失败的时候不会让用户感觉到明显的中断。
2.2 HDFS不适合的应用类型
- 低延时的数据访问:对延时要求在毫秒级别的应用,不适合采用HDFS。HDFS是为高吞吐数据传输设计的,因此可能牺牲延时。HBase更适合低延时的数据访问。
- 大量小文件:文件的元数据保存在NameNode的内存中,整个文件系统的文件数量会受限于NameNode的内存大小。一个文件/目录/文件块一般占有150字节的元数据内存空间。
- 多方读写,需要任意的文件修改:HDFS采用追加(append-only)的方式写入数据。不支持文件任意offset的修改,不支持多个写入器(writer)。
3. HDFS核心概念
3.1 Blocks
HDFS的Block块比一般单机文件系统大得多,默认为128M。HDFS的文件被拆分成block-sized的chunk,chunk作为独立单元存储。比Block小的文件不会占用整个Block,只会占据实际大小。
HDFS的Block为什么这么大? 是为了最小化查找(seek)时间,控制定位文件与传输文件所用的时间比例。假设定位到Block所需的时间为10ms,磁盘传输速度为100M/s。如果要将定位到Block所用时间占传输时间的比例控制1%,则Block大小需要约100M。
Block抽象的好处:
- block的拆分使得单个文件大小可以大于整个磁盘的容量
- Block的抽象也简化了存储系统
- Block作为容错和高可用机制中的副本单元
3.2 Namenode & Datanode
整个HDFS集群由Namenode和Datanode构成master-worker(主从)模式。Namenode负责构建命名空间,管理文件的元数据等,而Datanode负责实际存储数据,负责读写工作。
Namenode 存放文件系统树及所有文件、目录的元数据。元数据持久化为2种形式:namespace image 和 edit log。
Datanode 负责存储和提取Block,读写请求可能来自namenode,也可能直接来自客户端。数据节点周期性向Namenode汇报自己节点上所存储的Block相关信息。
3.3 HDFS Federation
我们知道NameNode的内存会制约文件数量,HDFS Federation提供了一种横向扩展NameNode的方式。在Federation模式中,每个NameNode管理命名空间的一部分。
每个NameNode管理一个namespace volumn,所有volumn构成文件系统的元数据。各NameNode之间是独立的,一个节点的失败不会导致其他节点管理的文件不可用。
3.4 HDFS HA
在HDFS集群中,NameNode是单点故障(SPOF)。HA方案中配置两个NameNode,分别处于Active和Standby状态。
HA主要实现逻辑:
- 主备需共享edit log存储:QJM(quorum journal manager)是专门为HDFS的HA实现而设计的,用来提供高可用的edit log
- DataNode需要同时往主备发送Block Report
- 客户端需要配置failover模式(对用户透明)
- Standby替代Secondary NameNode
4. 命令行接口
HDFS提供了各种交互方式,例如通过Java API、HTTP、shell命令行。
hadoop fs -copyFromLocal # 从本地复制文件到HDFS
hadoop fs mkdir # 创建目录
hadoop fs -ls # 列出文件列表
Hadoop中,文件和目录的权限类似于POSIX模型,包括读、写、执行3种权限。
5. Hadoop文件系统
Hadoop的文件系统概念是抽象的,HDFS只是其中的一种实现。其他实现包括:
- Local:对本地文件系统的抽象
- har:Hadoop体系下的压缩文件
- viewfs:在客户端屏蔽多个Namenode的底层细节
- WebHDFS/SWebHDFS:通过HTTP提供文件操作接口
- s3a/azure:云服务平台实现
6. Java接口
6.1 读操作
使用FileSystem API读取数据:
// 获取FileSystem实例
public static FileSystem get(Configuration conf) throws IOException
public static FileSystem get(URI uri, Configuration conf) throws IOException
// 调用FileSystem的open方法获取输入流
public FSDataInputStream open(Path f) throws IOException
// 使用FSDataInputStream进行数据操作
6.2 写数据
String localSrc = args[0];
String dst = args[1];
InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(dst), conf);
OutputStream out = fs.create(new Path(dst), new Progressable() {
public void progress() {
System.out.print(".");
}
});
IOUtils.copyBytes(in, out, 4096, true);
6.3 目录操作
使用mkdirs()方法会自动创建没有的上级目录。HDFS中元数据封装在FileStatus类中,包括长度、block size、replications等。
6.4 删除数据
public boolean delete(Path f, boolean recursive) throws IOException;
7. 数据流(读写流程)
7.1 读文件
- 客户端传递一个文件Path给FileSystem的open方法
- DFS采用RPC远程获取文件最开始的几个block的datanode地址
- 客户端使用FSDataInputStream对象读取数据
- DFSInputStream连接持有第一个block的、最近的节点读取数据
- 第一个block读取完毕后,寻找下一个block的最佳datanode
- 数据读取完毕,客户端调用close方法关闭流对象
7.2 写文件
- 客户端调用DistributedFileSystem的create方法
- DistributedFileSystem远程RPC调用Namenode创建一个新文件
- DFSOutputStream分解为packets,写入数据队列
- DataStreamer请求Namenode分配新的block存放的数据节点
- DFSOutputStream维护ack queue队列,等待datanode确认
- 数据写入完毕,客户端close输出流
7.3 副本存放策略
- 第一个副本放在客户端相同的机器上
- 第二个副本随机放在不同于第一个副本的机架上
- 第三个副本放在跟第二个副本同一机架上,但是不同的节点上