一、HDFS基本概念
HDFS全称是Hadoop Distributed System。HDFS是为以流的方式存取大文件而设计的。适用于几百MB,GB以及TB,并写一次读多次的场合。而对于低延时数据访问、大量小文件、同时写和任意的文件修改,则并不是十分适合。
目前HDFS支持的使用接口除了Java的还有,Thrift、C、FUSE、WebDAV、HTTP等。HDFS是以block-sized chunk组织其文件内容的,默认的block大小为64MB,对于不足64MB的文件,其会占用一个block,但实际上不用占用实际硬盘上的64MB,这可以说是HDFS是在文件系统之上架设的一个中间层。之所以将默认的block大小设置为64MB这么大,是因为block-sized对于文件定位很有帮助,同时大文件更使传输的时间远大于文件寻找的时间,这样可以最大化地减少文件定位的时间在整个文件获取总时间中的比例。
二、HDFS设计原则
HDFS是Google的GFS(Google File System)的开源实现。具有以下五个基本目标:
1、硬件错误是常态而不是错误。HDFS一般运行在普通的硬件上,所以硬件错误是一种很正常的情况。所以在HDFS中,错误的检测并快速自动恢复是HDFS的最核心的设计目标。
2、流式数据访问。运行在HDFS上的应用主要是以批量处理为主,而不是用户交互式事务,以流式数据读取为多。
3、大规模数据集。HDFS中典型的文件大小要达到GB或者是TB级。
4、简单一致性原则。HDFS的应用程序一般对文件的操作时一次写入、多次读出的模式。文件一经创建、写入、关闭后,一般文件内容再发生改变。这种简单的一致性原则,使高吞吐量的数据访问成为可能。
5、数据就近原则。HDFS提供接口,以便应用程序将自身的执行代码移动到数据节点上来执行。采用这种方式的原因主要是:移动计算比移动数据更加划算。相比与HDFS中的大数据/大文件,移动计算的代码相比与移动数据更加划算,采用这种方式可以提供宽带的利用率,增加系统吞吐量,减少网络的堵塞程度。
三、HDFS的体系结构
构成HDFS主要是Namenode(master)和一系列的Datanode(workers)。
Namenode是管理HDFS的目录树和相关的文件元数据,这些信息是以"namespaceimage"和"edit log"两个文件形式存放在本地磁盘,但是这些文件是在HDFS每次重启的时候重新构造出来的。
Datanode则是存取文件实际内容的节点,Datanodes会定时地将block的列表汇报给Namenode。
由于Namenode是元数据存放的节点,如果Namenode挂了那么HDFS就没法正常运行,因此一般使用将元数据持久存储在本地或远程的机器上,或者使用secondary namenode来定期同步Namenode的元数据信息,secondary namenode有点类似于MySQL的Master/Salves中的Slave,"edit log"就类似"bin log"。如果Namenode出现了故障,一般会将原Namenode中持久化的元数据拷贝到secondary namenode中,使secondary namenode作为新的Namenode运行起来。
HDFS是一个主从结构(master/slave)。如图所示:
四、HDFS可靠性保障措施
HDFS的主要设计目标之一是在故障情况下,要保障数据存储的可靠性。
HDFS具备了完善的冗余备份和故障恢复机制。一般通过dfs.replication设置备份份数,默认3。
1、冗余备份。将数据写入到多个DataNode节点上,当其中某些节点宕机后,还可以从其他节点获取数据并复制到其他节点,使备份数达到设置值。dfs.replication设置备份数。
2、副本存放。HDFS采用机架感知(Rack-aware)的策略来改进数据的可靠性、可用性和网络宽带的利用率。当复制因子为3时,HDFS的副本存放策略是:第一个副本放到同一机架的另一个节点(执行在集群中)/随机一个节点(执行在集群外)。第二个副本放到本地机架的其他任意节点。第三个副本放在其他机架的任意节点。这种策略可以防止整个机架失效时的数据丢失,也可以充分利用到机架内的高宽带特效。
3、心跳检测。NameNode会周期性的从集群中的每一个DataNode上接收心跳包和块报告,NameNode根据这些报告验证映射和其他文件系统元数据。当NameNode没法接收到DataNode节点的心跳报告后,NameNode会将该DataNode标记为宕机,NameNode不会再给该DataNode节点发送任何IO操作。同时DataNode的宕机也可能导致数据的复制。一般引发重新复制副本有多重原因:DataNode不可用、数据副本损坏、DataNode上的磁盘错误或者复制因子增大。
4、安全模式。在HDFS系统的时候,会先经过一个完全模式,在这个模式中,是不允许数据块的写操作。NameNode会检测DataNode上的数据块副本数没有达到最小副本数,那么就会进入完全模式,并开始副本的复制,只有当副本数大于最小副本数的时候,那么会自动的离开安全模式。DataNode节点有效比例:dfs.safemode.threshold.pct(默认0.999f),所以说当DataNode节点丢失达到1-0.999f后,会进入安全模式。
5、数据完整性检测。HDFS实现了对HDFS文件内容的校验和检测(CRC循环校验码),在写入数据文件的时候,也会将数据块的校验和写入到一个隐藏文件中()。当客户端获取文件后,它会检查从DataNode节点获取的数据库对应的校验和是否和隐藏文件中的校验和一致,如果不一致,那么客户端就会认为该数据库有损坏,将从其他DataNode节点上获取数据块,并报告NameNode节点该DataNode节点的数据块信息。
6、回收站。HDFS中删除的文件先会保存到一个文件夹中(/trash),方便数据的恢复。当删除的时间超过设置的时间阀后(默认6小时),HDFS会将数据块彻底删除。
7、映像文件和事务日志。这两种数据是HDFS中的核心数据结构。
8、快照。
五、数据存储操作
1、数据存储: block
默认数据块大小为128MB,可配置。若文件大小不到128MB,则单独存成一个block。
为何数据块如此之大? 数据传输时间超过寻道时间(高吞吐率)
一个文件存储方式? 按大小被切分成若干个block,存储到不同节点上,默认情况下每个block有三个副本。
2、数据存储: staging
HDFSclient上传数据到HDFS时,首先,在本地缓存数据,当数据达到一个block大小时,请求NameNode分配一个block。 NameNode会把block所在的DataNode的地址告诉HDFS client。 HDFS client会直接和DataNode通信,把数据写到DataNode节点一个block文件中。
六、写入数据
1.初始化FileSystem,客户端调用create()来创建文件。
2.FileSystem用RPC调用元数据节点,在文件系统的命名空间中创建一个新的文件,元数据节点首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。
3.FileSystem返回DFSOutputStream,客户端用于写数据,客户端开始写入数据。
4.DFSOutputStream将数据分成块,写入data queue。data queue由Data Streamer读取,并通知元数据节点分配数据节点,用来存储数据块(每块默认复制3块)。分配的数据节点放在一个pipeline里。Data Streamer将数据块写入pipeline中的第一个数据节点。第一个数据节点将数据块发送给第二个数据节点。第二个数据节点将数据发送给第三个数据节点。
5.DFSOutputStream为发出去的数据块保存了ack queue,等待pipeline中的数据节点告知数据已经写入成功。
6.当客户端结束写入数据,则调用stream的close函数。此操作将所有的数据块写入pipeline中的数据节点,并等待ack queue返回成功。最后通知元数据节点写入完毕。
7.如果数据节点在写入的过程中失败,关闭pipeline,将ack queue中的数据块放入data queue的开始,当前的数据块在已经写入的数据节点中被元数据节点赋予新的标示,则错误节点重启后能够察觉其数据块是过时的,会被删除。失败的数据节点从pipeline中移除,另外的数据块则写入pipeline中的另外两个数据节点。元数据节点则被通知此数据块是复制块数不足,将来会再创建第三份备份。
8.如果在写的过程中某个datanode发生错误,会采取以下几步:
1)pipeline被关闭掉;
2)为了防止防止丢包ack quene里的packet会同步到data quene里;
3)把产生错误的datanode上当前在写但未完成的block删掉;
4)block剩下的部分被写到剩下的两个正常的datanode中;
5)namenode找到另外的datanode去创建这个块的复制。当然,这些操作对客户端来说是无感知的。
Java代码
Configuration conf = new Configuration(); FileSystem fs = FileSystem.get(conf); Path file = new Path("demo.txt"); FSDataOutputStream outStream =fs.create(file); outStream.writeUTF("Welcome to HDFSJava API!!!"); outStream.close();
写入过程图片:
七、读取过程
1.初始化FileSystem,然后客户端(client)用FileSystem的open()函数打开文件。
2.FileSystem用RPC调用元数据节点,得到文件的数据块信息,对于每一个数据块,元数据节点返回保存数据块的数据节点的地址。
3.FileSystem返回FSDataInputStream给客户端,用来读取数据,客户端调用stream的read()函数开始读取数据。
4.DFSInputStream连接保存此文件第一个数据块的最近的数据节点,data从数据节点读到客户端(client)
5.当此数据块读取完毕时,DFSInputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点。
6.当客户端读取完毕数据的时候,调用FSDataInputStream的close函数。
7.在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点。
8. 失败的数据节点将被记录,以后不再连接。
Java代码
Configurationconf = new Configuration(); FileSystemfs = FileSystem.get(conf); Pathfile = new Path("demo.txt"); FSDataInputStreaminStream = fs.open(file); Stringdata = inStream.readUTF(); System.out.println(data); inStream.close();
读取文件过程图片:
本文出自 “王威” 博客,请务必保留此出处http://jaydenwang.blog.51cto.com/6033165/1842812
原文地址:http://jaydenwang.blog.51cto.com/6033165/1842812