HDFS简述

         管理网络中跨多台计算机存储的文件系统称为分布式文件系统,Hadoop自带HDFS(Hadoop Distributed Filesystem)分布式文件系统。java

1、HDFS设计

         HDFS以流式数据访问模式来存储超大文件,运行于商用硬件集群上。node

         超大文件:几百MB、GB、TB,目前已有PB级。web

         流式数据访问:一次写入、屡次读取;数据集一般由数据源生成或从数据源复制而来,接着长时间在此数据集上进行各类分析。apache

         商用硬件:节点故障的概率较高,被设计成可以持续运行且不让用户觉察到明显中断。swift

         低时间延迟的数据访问:HDFS是为高数据吞吐量应用优化的,可能会以提升时间延迟为代价。数组

         大量的小文件:因为namenode将文件系统的元数据存储在内存中,所以该文件系统所能存储的文件总数受限于namenode的内存容量。缓存

         多用户写入,任意修改文件:HDFS文件只支持单个写入者,并且写操做老是以“只添加”方式在文件末尾写数据。不支持多个写入者的操做,也不支持在文件的任意位置进行修改。服务器

 

2、HDFS概念

一、数据块

         每一个磁盘都有默认的数据块大小,这是磁盘进行数据读/写的最小单位。构建于单个磁盘之上的文件系统经过磁盘块来管理该文件系统中的块,该文件系统块的大小能够是磁盘块的整数倍。文件系统块通常为几千字节,而磁盘块通常为512字节。网络

         HDFS也有块的概念,默认为128MB。HDFS上的文件也被分为块大小的多个分块(chunk),做为独立的存储单元,HDFS中小于一个块大小的文件不会占据整个块的空间。app

         HDFS的块比磁盘的块大,目的是为了最小化寻址开销。若是块足够大,从磁盘传输数据的时间会明显大于定位这个块开始位置所需的时间。于是,传输一个由多个块组成的大文件的时间取决于磁盘传输速率。

         对分布式文件系统中的块进行抽象的好处:① 一个文件的大小能够大于网络中任意一个磁盘的容量,文件的全部块并不须要存储在同一个磁盘上;② 大大简化了存储子系统的设计;③ 块适合于数据备份进而提供数据容错能力和提升可用性,将每一个块复制到少数几个物理上相互独立的机器上(默认为3个),能够确保在块、磁盘或机器发生故障后数据不会丢失,一个因损坏或机器故障而丢失的块能够从其余候选地点复制到另外一台能够正常运行的机器上,以保证复本的数量回到正常水平。

二、namenode和datanode

         HDFS集群由两类节点以管理节点-工做节点模式运行,即一个namenode(管理节点)和多个datanode(工做节点)。

namenode管理文件系统的命令空间,它维护着文件系统树及整棵树内全部的文件和目录,这些信息以两个文件形式永久保存在本地磁盘上:命名空间镜像文件和编辑日志文件。namenode也记录着每一个文件中各个块所在的数据节点信息,但它不永久保存块的位置,由于这些信息会在系统启动时根据数据节点信息重建。

  datanode是文件系统的工做节点。它们须要存储并检索数据块,而且按期向namenode发送它们所存储的块的列表。

  没有namenode,文件系统将没法使用。Hadoop提供了两种namenode容错机制:① 备份组成文件系统元数据持久状态的文件,通常配置是将持久状态写入本地磁盘的同时,写入一个远程挂载的网络文件系统(NFS);② 运行一个辅助namenode,但它不能被用做namenode,辅助namenode做用是按期合并并编辑日志与命名空间镜像,以防止编辑日志过大。可是,辅助namenode保存的状态老是滞后于主节点,因此在主节点所有失效时,不免会丢失部分数据。在这种状况下,通常把存储在NFS上的namenode元数据复制到辅助namenode并做为新的主namenode运行。

三、块缓存

  一般datanode从磁盘中读取块,但对于访问频繁的文件,其对应的块可能被显式地缓存在datanode的内存中,以堆外块缓存的形式存在。做业调度器经过在缓存块的datanode上运行任务,能够利用块缓存的优点提升读操做的性能。用户或应用经过在缓存池中增长一个cache directive来告诉namenode须要缓存哪些文件以及存多久。缓存池是一个用于管理缓存权限和资源使用的管理性分组。

四、联邦HDFS

  namenode在内存中保存文件系统中每一个文件和每一个数据块的引用关系,这意味着对于一个拥有大量文件的超大集群来讲,内存将成为限制系统横向扩展的瓶颈。在2.x发行版本中引入的联邦HDFS容许系统经过添加namenode实现扩展,其中每一个namenode管理文件系统命名空间的一部分。

  在联邦环境下,每一个namenode维护一个命名空间卷,由命名空间的元数据和一个数据块池组成,数据块池包含该命名空间下文件的全部数据块。命名空间卷之间是相互独立的,两两之间并不相互通讯,数据块池再也不进行切分。

  要访问联邦HDFS集群,客户端须要使用客户端挂载数据表将文件路径映射到namenode。该功能能够经过ViewFileSystem和viewfs://URI进行配置和管理。

五、HDFS的高可用性

  Hadoop2针对namenode失效恢复的问题增长了HA支持,配置了一对活动-备用namenode。当活动namenode失效,备用namenode就会接管它的任务并开始服务于来自客户端的请求,不会有任何明显的中断。目标实现须要作如下修改:

    1) namenode之间须要经过高可用共享存储实现编辑日志的共享;

    2) datanode须要同时向两个namenode发送数据块处理报告;

    3) 辅助namenode的角色被备用namenode所包含,备用namenode为活动的namenode命名空间设置周期性检查点。

  有两种高可用性共享存储:NFS过滤器或群体日志管理器(QJM)。QJM以一组日志节点的形式运行,每一次编辑必须写入多很多天志节点。

  系统中有一个故障转移控制器的新实体,管理着将活动namenode转移为备用namenode的转换过程。有多种故障转移控制器,但默认的一种是使用了ZooKeeper来确保有且仅有一个活动namenode。每个namenode运行着一个轻量级的故障转移控制器,其工做就是监视宿主namenode是否失效并在namenode失效时进行故障切换。

  但在非平稳故障转移的状况下,没法确切知道失效namenode是否已经中止运行。高可用实现作了更进一步的优化,以确保先前活动的namenode不会执行危害系统并致使系统崩溃的操做,该方法称为规避。

  同一时间QJM仅容许一个namenode向编辑日志中写入数据。规避机制包括:撤销namenode访问共享存储目录的权限、经过远程管理命令屏蔽相应的网络端口。诉诸的最后手段是,经过一个特定的供电单元对相应主机进行断电操做。

 

3、命令行接口

         在设置伪分布式配置时,有两个属性项须要注意。第一项是fs.defaultFS,设置为hdfs://localhost/,用于设置Hadoop的默认文件系统。第二项是dfs.replication,设为1,HDFS就不会按默认设置将文件系统复本设为3.在单独一个datanode上运行时,HDFS没法将块复制到3个datanode上,因此会持续给出复本不足的警告。设置这个属性以后,上述问题就不会再出现了。

         文件系统的基本操做:读取文件、移动文件、删除数据、列出目录等。

         从本地文件系统将一个文件复制到HDFS:

                   hadoop fs –copyFromLocal input/docs/test.txt \ hdfs://localhost/user/tom/test.txt

         其中,能够省略hdfs://localhost,由于该项已经在core-site.xml中指定。也可使用相对路径,并将文件复制到HDFS的home目录中:

                   hadoop fs -copyFromLocal input/docs/test.txt test.txt

         将文件复制回本地文件系统,并检查是否一致:

                   hadoop fs –copyToLocal test.txt test.copy.txt

                   md5 input/docs/test.txt test.copy.txt

         MD5键值相同,代表这个文件在HDFS之旅中得以幸存并保持完整。

         新建目录,查看HDFS文件列表:

                   hadoop fs –mkdir books

                   hadoop fs –ls

         返回结果与Unix命令ls –l输出结果相似,有一些差异。第一列显式文件模式。第2列是这个文件的备份数。第3列和第4列显式文件的所属用户和组别。第5列是文件的大小,以字节为单位,目录为0。第6列和第7列是文件的最后修改日期和时间。第8列是文件或目录的名称。

 

4、Hadoop文件系统

         Java抽象类org.apache.hadoop.fs.FileSystem定义了Hadoop中一个文件系统的客户端接口,而且该抽象类有几个具体实现,其中与Hadoop紧密相关的有Local(file-使用客户端校验和的本地磁盘文件系统)、HDFS(hdfs-Hadoop分布式文件系统)、WebHDFS(Webhdfs-基于HTTP的文件系统)、SecureWebHDFS(swebhdfs-WebHDFS的HTTPS版本)、HAR(har-构建在其余文件系统之上用于文件存档的文件系统)、View(viewfs-针对其余Hadoop文件系统的客户端挂载表,一般用于为联邦namenode建立挂载点)、FTP(ftp-FTP服务器支持的文件系统)、S3(S3a-Amazon S3支持的文件系统)、Azure(wasb-Microsoft Azure支持的文件系统)、Swift(swift-OpenStack Swift支持的文件系统)。

         [上述文件系统中均依照 文件系统(URI方案-描述) 的格式]

         Hadoop通常使用URI方案来选取合适的文件系统实例进行交互。

         Hadoop以Java API的形式提供文件系统访问接口,非Java开发的应用访问HDFS会很不方便。Hadoop提供了其余一些文件系统接口的支持:

         1) HTTP

         HTTP接口比原生的Java客户端要慢,尽可能不要用来传输特大数据。经过HTTP访问HDFS有两种方法:直接访问,HDFS守护进程直接服务于来自客户端的HTTP请求;经过代理访问,客户端一般使用DistributedFileSystem API访问HDFS。

第一种状况中,namenode和datanode内嵌的web服务器做为WebHDFS的端节点运行。文件元数据操做由namenode管理,文件读(写)操做首先被发往namenode,由namenode发送一个HTTP重定向至某个客户端,指示以流方式传输文件数据的目的或源datanode。

第二种方法依靠一个或者多个独立代理服务器经过HTTP访问HDFS。全部到集群的网络通讯都须要通过代理,所以客户端历来不直接访问namenode和datanode。使用代理服务器后可使用更严格的防火墙策略和带宽限制策略。一般状况下都经过代理服务器,实如今不一样数据中心部署的Hadoop集群之间的数据传输,或从外网访问云端运行的Hadoop集群。

  2) C语言

  Hadoop提供了一个名为libhdfs的C语言库,该语言库是Java FileSystem接口类的一个镜像。它使用Java原生接口(JNI)调用Java文件系统客户端。一样还有一个libwebhdfs库,该库使用了WebHDFS接口。

  3) NFS

  使用Hadoop的NFSv3网关将HDFS挂载为本地客户端的文件系统。

  4) FUSE

  用户空间文件系统(FUSE,FileSystem in Userspace)容许将用户空间实现的文件系统做为Unix文件系统进行集成。

 

5、Java接口

一、从Hadoop URL读取数据

         从Hadoop文件系统读取文件,最简单的方法是使用java.net.URL对象打开数据流,从中读取数据。为使Java程序可以识别Hadoop的hdfs URL方案,还须要经过FsUrlStreamHandlerFactory实例调用java.net.URL对象的setURLStreameHandlerFactory()方法。每一个Java虚拟机只能调用一次这个方法,所以一般在静态方法中调用。这个限制意味着若是程序的其余组件已经声明一个URLStreamHandlerFactory实例,你将没法使用这种方法从Hadoop中读取数据。

         public class URLCat {

                   static {

                            URL.setURLStreamHandler(new FsUrlStreamHandlerFactory());

      }

 

      public static void main(String[] args) throws Exception {

              InputStream in = null;

              try {

                       in = new URL(args[0].openStream());

                       IOUtils.copyBytes(in, System.out, 4096, false);

         } finally {

               IOUtils.closeStream(in);

         }

      }

    }

    copyBytes方法最后两个参数,第一个设置用于复制的缓冲区大小,第二个设置复制结束后是否关闭数据流。

二、经过FileSystem API读取数据

  Hadoop文件系统中经过Hadoop Path对象来表明文件,能够将路径视为一个Hadoop文件系统URI。

  1) 获取FileSystem实例

    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

  Configuration对象封装了客户端或服务器的配置,经过设置配置文件读取类路径来实现。

  2) 获取本地文件系统的运行实例

    public static LocalFileSystem getLocal(Configuration conf) throws IOException

  3) 获取文件的输入流

    public FSDataInputStream open(Path f) throws IOException  --默认缓冲大小4KB

    public abstract FSDataInputStream open(Path f, int bufferSize) throws IOException

  FSDataInputStream对象是继承了java.io.DataInputStream的一个特殊类,支持随机访问,能够从流的任意位置读取数据。

三、写入数据

  FileSystem新建文件的方法:给准备建的文件指定一个Path对象,而后返回一个用于写入数据的输出流:

    public FSDataOutputStream create(Path f) throws IOException

  此方法由多个重载版本,容许指定须要强制覆盖现有的文件、文件备份数量、写入文件时所用缓冲区大小、文件块大小以及文件权限。

  另外一种新建文件的方法是使用append()方法在一个现有文件末尾追加数据:

    public FSDataOutputStream append(Path f) throws IOException

  FSDataOutputStream和FSDataInputStream类类似,也有一个查询文件当前位置的方法。但与FSDataInputStream类不一样的是,FSDataOutputStream类不容许在文件中定位。这是由于HDFS只容许对一个已打开的文件顺序写入,或在现有文件的末尾追加数据。

四、目录

    public boolean mkdirs(Path f) throws IOException

  这个方法能够一次性新建全部必要但尚未的父目录。

五、查询文件系统

  1) 文件元数据

    FileSystem的getFileStatus()方法用于获取文件或目录的FileStatus对象。FileStatus封装了文件系统中文件和目录的元数据,包括文件长度、块大小、复本、修改时间、全部者以及权限信息。

  2) 列出文件

         listStatus()方法能够列出目录中的内容:

           public FileStatus[] listStatus(Path f) throws IOException

              public FileStatus[] listStatus(Path f, PathFilter filter) throws IOException

              public FileStatus[] listStatus(Path f[] files) throws IOException

            public FileStatus[] listStatus(Path[] files, PathFilter filter) throws IOException

          它的重载方法容许使用PathFilter来限制匹配的文件和目录。

         3) 文件模式

    能够经过通配来匹配多个文件:

           public FileStatus[] globStatus(Path pathPattern) throws IOException

           public FileStatus[] globStatus(Path pathPattern, PathFilter filter) throws IOException

        globStatus()方法返回路径格式与指定模式匹配的全部FileStatus对象组成的数组,并按路径排序。

         通配符及其含义:

通配符

名称

匹配

*

星号

匹配0或多个字符

?

问号

匹配单一字符

[ab]

字符类

匹配{a, b}集合中的一个字符

[^ab]

非字符类

匹配非{a, b}集合中的一个字符

[a-b]

字符范围

匹配一个在{a, b}范围内的字符(包括ab),a在字典顺序上要小于或等于b

[^a-b]

非字符范围

匹配一个不在{a, b}范围内的字符(包括ab),a在字典顺序上要小于或等于b

{a, b}

或选择

匹配包含a或b中的一个表达式

\c

转义字符

匹配元字符c

         4) PathFilter对象

           PathFilter对象用来排除一个特定的文件。

         5) 删除数据

           使用delete()方法能够永久性删除文件或目录。

             public boolean delete(Path f, boolean recursive) throws IOException

           若是f是一个文件或空目录,那么recursive的值就会被忽略。只有在recursive值为true时,非空目录及其内容才会被删除。

 

6、数据流

一、图解文件读取

   

 

  ① 客户端调用FileSystem对象的open()方法来打开但愿读取的文件,对于HDFS来讲,这个对象是DistributedFileSystem的一个实例。

  ② DistributedFileSystem经过使用远程过程调用(RPC)来调用namenode,以肯定文件起始块的位置。对于每个块,namenode返回存有该块副本的datanode地址,这些datanode根据它们与客户端的距离来排序。DistributedFileSystem类返回一个FSDataInputStream对象给客户端以便读取数据。FSDataInputStream类转而封装DFSInputStream对象,该对象管理着datanode和namenode的I/O。

  ③ 客户端对这个输入流调用read()方法。

  ④ 存储着文件起始几个块的datanode地址的DFSInputStream随即链接距离最近的文件中第一个块所在的datanode。经过对数据流反复调用read()方法,能够将数据从datanode传输到客户端。

  ⑤ 到达块的末端时,DFSInputStream关闭与该datanode的链接,而后寻找下一个块的最佳datanode。客户端从流中读取数据时,块是按照打开DFSInputStream与datanode新建链接的顺序读取的。它也会根据须要询问namenode来检索下一批数据块的datanode位置。

  ⑥ 一旦客户端完成读取,就对FSDataInputStream调用close()方法。

  在读取数据的时候,若是DFSInputStream在与datanode通讯时遇到错误,会尝试从这个块的另一个最邻近datanode读取数据。它也会记住那个故障datanode,以保证之后不会反复读取该节点上后续的块。DFSInputStream也会经过校验和确认从datanode发来的数据是否完整。若是发现有损坏的块,DFSInputStream会试图从其余datanode读取其复本,也会将损坏的块通知给namenode。

二、图解文件写入

   

         ① DistributedFileSystem对象调用create()来新建文件。

         ② DistributedFileSystem对namenode建立一个RPC调用,在文件系统的命名空间中新建一个文件,此时该文件中尚未相应的数据块。

③ DistributedFileSystem向客户端返回一个FSDataOutputStream对象,由此客户端开始写入数据。

         ④ 在客户端写入数据时,DFSOutputStream将它分红一个个的数据包,并写入内部队列,称为“数据队列”。DataStreamer将数据包流式传输到管线中第1个datanode,该datanode存储数据包并将它发送到管线中的第2个datanode。一样,第2个datanode存储该数据包而且发送给第3个datanode(最后一个)。

         ⑤ DFSOutputStream也维护着一个内部数据包来等待datanode的收到确认回执,称为“确认队列”。收到管道中全部datanode确认信息后,该数据包才会从确认队列删除。

         ⑥ 客户端完成数据的写入后,对数据流调用close()。

         ⑦ 该操做将剩余的全部数据包写入datanode管线,并在联系到namenode告知其文件写入完成以前,等待确认。

         Hadoop默认复本布局策略:在运行客户端的节点上放第1个复本(若是客户端运行在集群以外,就随即选择一个节点,不过系统会避免挑选哪些存储太满或太忙的节点);第2个复本放在与第1个不一样且随即另外选择的机架中节点上。第3个复本与第2个复本放在同一个机架上,且随机选择另外一个节点。其余复本放在集群中随机选择的节点上,不过系统会尽可能避免在同一个的机架上放太多复本。一旦选定复本的放置位置,就根据网络拓扑建立一个管线。

三、一致模型

         文件系统的一致模型描述了文件读/写的数据可见性。新建一个文件以后,它能在文件系统的命名空间中当即可见。可是,写入文件的内容不能保证当即可见,即便数据流已经刷新并存储。

当写入的数据超过一个块后,第一个数据块对新的reader就是可见的。以后的块也不例外。总之,当前正在写入的块对其余reader不可见。

HDFS提供了一种强行将全部缓存刷新到datanode中的手段,即对FSDataOutputStream调用hflush()方法。hflush()不保证datanode已经将数据写到磁盘上,仅确保数据在datanode的内存中。为确保数据写入到磁盘上,能够用hsync()替代。

 

7、经过distcp并行复制

         Distcp的一种用法是替代hadoop fs –cp。例如,能够将文件复制到另外一个文件中:

                   hadoop distcp file1 file2

         也能够复制目录:

                   hadoop distcp dir1 dir2

         若是dir2不存在,将新建dir2,目录dir1的内容所有复制到dir2下。能够指定多个源路径,全部源路径下的内容都将被复制到目标路径下。

         若是dir2已经存在,目录dir1将被复制到dir2下,造成目录结构dir2/dir1。若是这不是你所需的,你能够补充使用-overwrite选项,在保持一样的目录结构的同时强制覆盖原文件。你也可使用-update选项,仅更新发生变化的文件。

         distcp是做为一个MapReduce做业来实现的,该复制做业是经过集群中并行运行的map来完成。每一个文件经过一个map进行复制,而且distcp试图为每个map分配大体相等的数据来执行。

         关于distcp的一个经常使用使用实例是在两个HDFS集群间传送数据:

                   hadoop distcp –update –delete –p hdfs://namenode1/foo hdfs://namenode2/foo

         -delete选项使得distcp能够删除目标路径中任意没在源路径出现的文件或目录,-p选项意味着文件状态属性如权限、块大小和复本数被保留。

         若是两个集群运行的是HDFS的不兼容版本,能够将webhdfs协议用于它们之间的distcp。另外一个变种是使用HttpFs代理做为distcp源或目标,这样具备设置防火墙和控制带宽的优势。

相关文章
相关标签/搜索