传统的网络文件系统(NFS)也被称为分布式文件系统,但由于其存在一些限制。NFS的文件存在单机上,因此无法保证可靠性。 当很多客户端访问NFS server时,可能会造成服务器压力。 同时在对NFS中的文件进行操作时,需要先将文件同步到本地,在修改同步到服务器之前,其他客户端时不可见的。所以说,在某 种程度上,NFS不是一种典型的分布式系统。
HDFS是指被设计城市和运行在通用硬件上的分布式文件系统。它是一个高容错性的系统,适合部署在廉价的机器上。它可以 提供高吞吐量的数据访问,非常适合大规模数据集上的应用。 HDFS的文件分布在集群机器上,同时提供副本进行容错及可靠性保证。所以,客户端的直接操作时分布在集群各个机器上的, 没有单点性能压力。HDFS设计原则 设计目标
- 存储非常大的文件:达到G或者T级别(实际应用中的许多的存储集群存储的数据达到PB级别)。采用流式的数据访问方式:理想的处理模式为一次写入,多次读取。分析时是从数据源拷贝一份数据集,所以读取整个数据集所需的时间比读取第一天产生的时延更重要。运行在商业软件上:Hadoop对硬件的要求不高。但在集群中,节点失败率太高会导致用户明显卡顿。
- 需要低时延的数据访问:相比于HDFS来说,Hbase更适合低时延的需求。大量小文件:存储大量小文件时,会导致性能下降。多方读写,需要任意的文件修改:HDFS采用追加的方式写入数据。不支持任意offset的修改。不支持多个写入器。
磁盘block块
硬盘最底层的读写IO操作,一次是一个扇区512字节,在大量读写文件时,以扇区为单位肯定又慢又消耗性能,所以硬盘使用了一个叫逻辑块的概念。
逻辑块由磁盘驱动器负责维护和操作,并非是扇区那样物理划分的。一个逻辑块可能包含一个或多个扇区,每个逻辑块都有唯一地址。一次读写一个逻辑块,磁盘控制器将逻辑块翻译成对应扇区,并读写数据。
文件系统block块
Linux操作系统中,通过文件系统提供了一个称为块的读写单元(其大小一般为1k/2k/4k)。由文件系统层次维护。
理论上,block的拆分是的单个文件可以大于整个磁盘的容量,理论上,单个文件可以占据集群中所有机器的磁盘。也简化了存储系统。
block(块)出现后,文件系统层面上的读写操作性能大大提高,也减少了碎片。但可能会造成空间浪费(如1k大小的文件可能占用一个block)。存储大量小文件时,会造成大量的空间浪费。
但相对缺点,其优点足够明显。
HDFS的Block默认大小为128M,HDFS的文件被拆分为block-sized的chunk,chunk作为独立存储单元。
比Block小的文件不会占用整个Block,只会占用实际大小(如:1M的文件实际占用大小为1M)。
Block设置为128M的原因为:
- 最小化查找时间,控制定位文件与传输文件所用的时间比例。如果Block设置过大时,可能会使得整体效率很低。
整个HDFS集群由Namenode和Datanode构成master-work(主从)模式。一个HDFS cluster包含一个NameNode和若干的DataNode。
Namenode负责构建命名空间、管理文件的元数据等
Datanode负责实际存储数据,负责读写工作
Namenode存放文件系统树及所有文件、目录的元数据持久化为两种形式:
- namespcae imageedit log
在HDFS中,Namenode可能成为集群的单点故障,Namenode不可用时,整个文件系统就不可用了。
此故障解决机制:
- 备份持久化元数据
将文件系统的元数据同时写入多个文件系统。备份操作都是同步的、原子的。Secondary Namenode
Secondary节点定期合并主Namenode的namespace image和edit log。可用于在Namenode完全崩溃时恢复数据。
数据节点负责存储和提取Block,读写请求可能来自namenode,也可能直接来自客户端。数据节点周期性向Namenode汇报自己节点上所存储的Block相关信息。
Block CachingDataNode通常直接从磁盘读取数据,但是频繁使用的Block可以在内存中缓存。默认情况下,一个Block只有一个数据节点会缓存。但是可以针对每个文件可以个性化配置。
可以利用缓存来提升性能。
HDFS FederationNameNode的内存会限制文件数量,HDFS提供了一种横向扩展NameNode的方式。在Federation模式下,每个NameNode管理命名空间的一部分。例如某个NameNode管理/A目录下的文件,另一个NameNode管理/B目录下的文件。
HDFS High AvailabilityHDFS集群中,NameNode是最主要的单点故障点。元数据同时写入多个文件系统以及Second NameNode定期检查可以防止数据丢失,但并不提高可用性。
其原因在于NameNode是唯一对文件元数据和file-block映射负责的地方。他出现问题之后,包括MapReduce在内的作业都无法进行读写。
HA方案- 主备需共享edit log存储。DataNode需要同时往主备发送Block Report。客户端需要配置failover模式。Standly替代Secondary NameNode。
HDFS是Hadoop的一种实现。Hadoop还提供了其他的实现。
Local是对本地文件系统的抽象。
WebHDFS和SecureWebHDFS通过HTTP提供的文件接口来实现。
HAR是Hadoop体系下的压缩文件,在文件很多时,可以压缩成为一个大文件,有效减少元数据的数量。
View用来在客户端屏蔽多个Namenode的底层实现。
FTP通过FTP协议实现,对文件的操作转化为FTP协议
S3是对Amazon云服务提供的存储系统的实现。
Azure是微软的云服务平台实现。
java.net.URL类提供了资源定位的统一抽象。如hdfs schema
InputStream in = null;
try {
in = new URL("hdfs://master/user/hadoop").openStream();
} finally {
IOUtils.closeStream(in);
}
使用FileSystem API读取数据
- 首先获取FileSystem实例,一般使用静态get方法
public static FileSystem get(Configuration conf) throws IOException public static FileSystem get(URI uri , Configuration conf) throws IOException public static FileSystem get(URI uri , Configuration conf,String user) throws IOException
如果是本地文件,通过getLocal获取本地文件系统对象
public static LocalFileSystem getLocal(Configuration conf) thrown IOException
- 调用FileSystem的open方法获取一个输入流
public FSDataInputStream open(Path f) throws IOException public abstarct FSDataInputStream open(Path f , int bufferSize) throws IOException
- 使用FSDataInputStream进行数据操作
public class FSDataInputStream extends DataInputStream
implements Seekable, PositionedReadable,
ByteBufferReadable, HasFileDescriptor, CanSetDropBehind, CanSetReadahead,
HasEnhancedByteBufferAccess
随机读取通过Seekable接口定义(操作开销大)
public interface Seekable {
void seek(long pos) throws IOException;
long getPos() throws IOException;
}
部分读取通过PositionedReadable接口定义:
public interface PositionedReadable{
public int read(long pistion ,byte[] buffer,int offser , int length) throws IOException;
public int readFully(long pistion ,byte[] buffer,int offser , int length) throws IOException;
public int readFully(long pistion ,byte[] buffer) throws IOException;
}
写数据
在HDFS中,文件使用FileSystem类的create方法及其重载形式来创建,create方法返回一个输出流FSDataOutputStream,可以调用返回输出流的getPos方法查看当前文件的位移,但是不能进行seek操作,HDFS仅支持追加操作。
如下例:
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 vid progress(){
System.out.print(.);
}
});
IOUtils.copyBytes(in , out, 4096,true);
操作目录
使用mkdir()方法,会自动创建没有的上级目录。使用FileSystem提供的getFileStatus方法获取FileStatus。exists()方法判断文件或者目录是否存在;
public abstract FileStatus[] listStatus(Path f) throws FileNotFoundException, IOException;
Path是个文件的时候,返回长度为1的数组。FileUtil提供的stat2Paths方法用于将FileStatus转化为Path对象。
globStatus则使用通配符对文件路径进行匹配:
public FileStatus[] globStatus(Path pathPattern) throws IOException
PathFilter用于自定义文件名过滤,不能根据文件属性进行过滤,类似于java.io.FileFilter。例如下面这个例子排除到给定正则表达式的文件:
public interfacePathFilter{
boolean accept(Path path);
}
删除数据
使用FileSystem的delete()方法
public boolean delete(Path f , boolean recursive) throws IOException;关于转载
允许非商业用途转载,注明出处和链接即可。
参考文献深入理解Hadoop HDFS【一篇就够】
Hadoop分布式文件系统:架构和设计
HDFS 百度百科
因本人知识的深度和广度有限,所以本文可能存在谬误或有争议的地方。如有发现此类问题或者校对问题,烦请评论区留言或私信。



