Hadoop-分布式文件系统概述

来源:互联网 发布:mac怎么重新分区 编辑:程序博客网 时间:2024/06/08 01:17

HDFS(Hadoop Distributed File System)提供了分布式的存储服务,下面描述一下HDFS中的概念和关键原理。

数据块(Block)

数据块是HDFS存储文件的基本单位,块的默认大小为64MB(可以通过dfs.blocksize参数进行修改),MapReduce对输入数据进行分片时多数也是基于单个数据块大小来进行。

那么,数据块为什么会这么大呢?一个最主要的原因是减小磁盘寻址在一个读取中所占资源的比例,例如一次磁盘寻址需要1s,一个10MB的文件的读取时间为500sm,那么磁盘寻址的消耗就占整个任务的2/3,对于一个50M的文件呢,目前磁盘的IO速度大概位30-100M/s,假设一个100M的文件读取时间为2s,那么,磁盘寻址在整个读取任务中的消耗占比为1/3,(以上数据均为本人假设),根据当前磁盘的读取速度和实际测试结果,实际项目组通常会将数据块大小设置在60-150MB之间。

这时就会有人问,那为什么不把数据块大小设置的很大?比如1000MB?这是一个值得思考的问题,这个问题的关键在于谁读取数据,很多情况下是MapReduce(也可能是别的),以MapReduce为例,在输入分片阶段,Map任务数=数据文件大小/数据块大小,也就是说数据块约大,Map任务数据越小,这就会造成MapReduce任务负载过于集中,不能充分利用集群上的计算资源,拖慢任务,其次,集群负载倾斜很容易造成部分节点宕机或假死。

引入数据块的好处可以概括一下三点:
第一、单个文件大小不在局限于一个节点的存储能力,对于一个大文件,会按数据块大小进行分割,并存储在集群中,理论上,整个HDFS文件系统可以存储一个大小为集群最大存储容量的文件。
第二、提供了一种更高层次的抽象。HDFS只处理数据块,而不关心底层的实际存储系统,这样就可以把实际存储系统从HDFS中分离,在实际应用中,除了本地磁盘存储,还可以利用NFS或Amazon的S3作为存储介质。
第三、在数据块的基础上很容易引入复制,来提高系统的容错性和可用性。HDFS对于每个数据块都进行复制备份,默认副本数为3(可以利用dfs.replication参数修改副本数)。

查看hdfs块配置信息>hadoop fsck / -files -blockStatus: HEALTHY Total size:    47579 B Total dirs:    80 Total files:   56 Total symlinks:        0 Total blocks (validated):  41 (avg. block size 1160 B) Minimally replicated blocks:   41 (100.0 %) Over-replicated blocks:    0 (0.0 %) Under-replicated blocks:   18 (43.90244 %) Mis-replicated blocks:     0 (0.0 %) Default replication factor:    1 Average block replication: 1.0 Corrupt blocks:        0 Missing replicas:      36 (46.753246 %) Number of data-nodes:      1 Number of racks:       1FSCK ended at Wed Nov 02 03:42:54 PDT 2016 in 33 milliseconds

NameNode和DataNode

HDFS中有两种类型的节点:NameNode和DataNode。以主从的方式组织,NameNode为Master节点,DataNode为Fellower节点。
这里写图片描述
NameNode存储HDFS的命名空间、系统树结构及文件的元信息,NameNode并不参与实践的文件读写工作。

DataNode负责实际的文件读写任务,并心跳上报状态信息到NameNode,对于一个客户端请求,客户端首先RPC到NameNode获取元数据,在根据原数据RPC到DataNode做实际工作。

NameNode对于一个HDFS集群至关重要,没有NameNode整个集群将无法工作,为了保证NameNode的可用性,NameNode会将集群信息和数据信息写入本地磁盘的fsimage镜像日志和editlog编辑日志,这样当NameNode重启时,可以通过回放日志文件来恢复集群状态。为了防止NameNode节点的物理故障,还可以在另外一台物理集群上部署SecondaryNameNode,它会从NameNode节点复制数据,进行备份,单由于备份是固定频率,所以在故障恢复时,可能会造成部分数据丢失。

HDFS联邦

在传统的HDFS架构中,NameNode存在严重的单点失效问题(当NameNode出现故障,整个HDFS集群不可以),虽然fsimage和eidtlog能在一定程度上包装数据的可用性,但NameNode需要手工重启,这会造成整个集群的不可用,同时对于大型集群,NameNode的冷启动时间会很长。
第二个问题是,由于NameNode的单点运行,数据保存在内存中,当集群规模很大时,NameNode必定会成为整个集群扩展和性能瓶颈。
传统HDFS简易架构如下:
这里写图片描述

为了进一步解决NameNode的单点故障问题,在Hadoop2.x中引入了NameNode联邦机制,整体架构如下:
这里写图片描述
将NameNode进行横向拆分成多个NameNode,每一个NameNode相互独立。
NameNode联邦的优势在于:
第一、提高集群可用性。当一个NameNode挂掉,不会对其他NameNode造成影响,将故障影响最小化。
第二、提高集群扩展性。可以通过增加NameNode的方式来扩大集群可以存储的文件的数量。
第三、提高集群服务性能。一个NameNode的负载分摊到多个NameNode上,能有效提高单个NameNode的服务性能。

HDFS数据流

HDFS作为一个文件系统,对外提供两种服务:读 和 写。
下面描述一下HDFS对于读写请求的处理流程。

读(Read)

首先看一下《Hadoop权威指南》中给出的流程图
这里写图片描述

整个流程描述如下:
1.客户端根据配置信息创建代表HDFS的DistributedFileSystem对象
2.DFS对象RPC到NameNode,获取目标数据的元信息(包括块分布、DataNode地址等)
3.客户端获取指向目标DataNode的流对象FSDataInputStream
4.客户端在FSDIS对象上调用read方法,集群透明的组织分布在不同节点上的数据,并返回给客户端
5.客户端读取数据完成。

写(Write)
首先看一下《Hadoop权威指南》中给出的流程图
这里写图片描述

整个流程描述如下:
1.客户端创建代码集群的对象DistributedFileSystem
2.客户端在DFS对象上RPC到NameNode,创建一个新的文件标识
3.客户端得到一个FSDataOutputStream对象,客户端在该对象上调用write方法写入数据
4.HDFS利用机架感知功能及配置的副本数据创建一个数据流管道
5.被写入的数据在数据流管道中的节点上逐一写入而后返回
6.客户端得到写入成功确认后,关闭写入流,同时RPC到NameNode报告写入成功,NameNode记录本地操作到编辑日志editlog,写入完成

Hadoop-HDFS的整体轮廓就是这样,当然还有很多细节,后面再逐个加以讨论。

HDFS的读写一致模型

HDFS中的一致模型类似于关系数据库中的事务,可以把一个数据块Block理解为一个事务区间,单写入的数据量小于这个事务区间-即块大小时,写入的数据对外是不可见的,当然,也可以在写入的过程中调用FSDataOutputStream对象的sync()或close()方法来强制数据同步刷新到磁盘,下面看一个例子:

/** * 读写一致性. * @author zhangdong * @createtime 2016-11-03 09:28 * @location peking national library *  * HDFS的一致模型可以这样理解:  *  一个数据库是一个事务范围, 当写入数据的大小在一个数据块范围内时,写入数据对外界不可见。 *  可以调用FSDataOutputStream 的 sync方法或close反复来强制将数据刷新到磁盘。 *  * */public class ConsistentModel {    @Test    public void hdfsConsistentModelTest() throws Exception{        //将本地文件写入hffs        String localSrc = "E:\\New_WorkSpace\\eclipse\\bigdata_framework_hadoop\\resources\\log4j.properties";        String hdfsSrc = "hdfs://master:9000/consistentmodel1/1/a.txt";        InputStream in = new BufferedInputStream(new FileInputStream(localSrc));                Configuration conf = new Configuration();        FileSystem fs = FileSystem.get(new URI(hdfsSrc), conf);        Path path = new Path(hdfsSrc);        FSDataOutputStream out = fs.create(path);        //IOUtils.copy(in, out, 1024);        IOUtils.copyBytes(in, out, 1024);        System.out.println(fs.exists(path) +  "已存在");        System.out.println((fs.getFileStatus(path).getLen() == 0) +  "长度为0");        out.sync();        System.out.println((fs.getFileStatus(path).getLen() != 0) + "sync-> 不为0");        IOUtils.closeStream(out);    }}true已存在true长度为0truesync-> 不为0

从输出结果来看,一目了然。
HDFS的这种设计实际上是在性能和一致性上做的取舍,通常不同的应用系统对数据一致性的要求是不同的,在实际场景中可以根据具体的业务类型来把握。单值得注意的是,HDFS虽然对sync()进行了优化,但它仍然会带来很大的开销。

0 0
原创粉丝点击