Hadoop分布式文件系统(hadoopdistributed filesystem,HDFS)。HDFS是一个高度容错性的系统,适合部署在廉价的机器上。HDFS能提供高吞吐量的数据访问,非常适合大规模数据集上的应用。HDFS可以实现流的形式访问(streaming access)文件系统中的数据。
它是基于流数据模式的访问和处理超大文件。(分布式最大的好处就是其通透性,虽然分布存在不同的datanode上面,但是感觉在一台电脑的本地进行操作)。

特点:

  • 错误检测和快速、自动的恢复是 HDFS的核心架构目标

  • HDFS 以支持大数据集合为目标,一个存储在上面的典型文件大小一般都在千兆至 T字节,一个单一 HDFS实例应该能支撑数以千万计的文件。

  • 主从架构。

  • 文件采用write-one-read-many访问模型(一次写,多次读,不可更新已有内容,但如果需要追加,需要将hdfs-site.xml中的dfs.support.append属性设置为true)

  • 跑在 HDFS上的应用与一般的应用不同,它们主要是以流式读为主,做批量处理;比之关注数据访问的低延迟问题,更关键的在于数据访问的高吞吐量。

HDFS的限制:

  • 适用于一次写入、多次查询的情况,不支持并发写情况,小文件不合适。因为小文件也占用一个块,小文件越多(如100000个1k文件)块越 多,NameNode压力越大。

  • 如果要处理一些用户要求时间比较短的低延迟应用请求,则HDFS不适合。HDFS是为了处理大型数据集分析任务的,主要是为达到高的数据吞吐量而设计的,这就可能要求以高延迟作为代价。

HDFS架构

一个HDFS集群是有一个 Namenode和一定数目的 Datanode组成。 Namenode是一个中心服务器,负责管理文件系统的 namespace和客户端对文件的访问。 Datanode在集群中一般是一个节点一个,负责管理节点上它们附带的存储。在内部,一个文件其实分成一个或多个 block,这些 block存储在 Datanode集合里。 Namenode执行文件系统的 namespace操作,例如打开、关闭、重命名文件和目录,同时决定 block到具体 Datanode节点的映射。 Datanode在 Namenode的指挥下进行 block的创建、删除和复制。
namenode用来管理文件系统的命名空间,其将所有的文件和文件夹的元数据保存在一个文件系统树中。这些信息也会在硬盘上保存成以下文件:命名空间镜像 (namespace image)及修改日志 (edit log) 。其还保存了一个文件包括哪些数据块,分布在哪些数据节点上。

1、NameNode 是master节点,NameNode的作用是 管理文件目录结构,接受用户的操作请求,是管理数据节点的。名字节点维护两套数据, 一套 是文件 目录与数据块之间的关系 , 另一套 是 数据块与节点之间的关系 。 前一套 数据是 静态的 ,是存放在磁盘上的, 通过fsimage和edits文件来维护 ; 后一套 数据是 动态的 ,不持久放到到磁盘的,每当集群启动的时候,会自动建立这些信息,所以一般都放在内存中。

  • fsimage (文件系统镜像):元数据镜像文件。存储某一时段NameNode内存 元数据信息。

  • edits: 操作日志文件。

  • fstime: 保存最近一次checkpoint的时间

2、SecondaryNameNode
HA的一个解决方案。但不支持热备。由于数据操作越多edits文件膨胀越大,但不能让他无限的膨胀下去,所以要把日志过程转换出来 放到fsimage中。由于NameNode要接受用户的操作请求,必须能够快速响应用户请求,为了保证NameNode的快速响应给用户,所以将此项工 作交给了 SecondaryNode ,它从NameNode上 下载元数据信息(fsimage,edits),然后把二者合并,生成新的fsimage,在本地保存,并将其推送到NameNode,同时重置NameNode的edits

3、DataNode 是HDFS中真正存储数据的。按Block存储(每个block大小64M)。

  • 客户端(client)或者元数据信息(namenode)可以向数据节点请求写入或者读出数据块。

  • 其周期性的向元数据节点回报其存储的数据块信息。

数据流

1、读数据
客户端(client)用 FileSystem的 open()函数打开文件,DistributedFileSystem用RPC调namenode,得到文件的数据块信息。
对于每一个数据块,namenode返回保存数据块的数据节点的地址。DistributedFileSystem返回FSDataInputStream 给客户端,用来读取数据。客户端调用 stream的 read()函数开始读取数据。DFSInputStream连接保存此文件第一个数据块的最近的数据节点。Data从数据节点读到客户端 (client),当此数据块读取完毕时, DFSInputStream关闭和此数据节点的连接,然后连接此文件下一个数据块的最近的数据节点。
当客户端读取完毕数据的时候,调用 FSDataInputStream的 close函数。在读取数据的过程中,如果客户端在与数据节点通信出现错误,则尝试连接包含此数据块的下一个数据节点。失败的数据节点将被记录,以后不再连接。

2、写数据
客户端调用 create()来创建文件,DistributedFileSystem 用RPC 调用namenode,在文件系统的命名空间中创建一个新的文件。
namenode首先确定文件原来不存在,并且客户端有创建文件的权限,然后创建新文件。DistributedFileSystem 返回DFSOutputStream ,客户端用于写数据。客户端开始写入数据, DFSOutputStream将数据分成块,写入 data queue。Data queue 由Data Streamer读取,并通知namenode分配数据节点,用来存储数据块 (每块默认复制 3份,也就是说1T数据需要3T存储空间 )。
分配的数据节点放在一个 pipeline里。 Data Streamer 将数据块写入pipeline 中的第一个数据节点。第一个数据节点将数据块发送给第二个数据节点。第二个数据节点将数据发送给第三个数据节点。DFSOutputStream 为发出去的数据块保存了 ack queue,等待 pipeline中的数据节点告知数据已经写入成功。当客户端结束写入数据,则调用 stream的 close函数。此操作将所有的数据块写入 pipeline中的数据节点,并等待 ack queue返回成功。最后通知namenode写入完毕。 如下图:

3、文件复制
副本的存放是 HDFS可靠性和性能的关键。 HDFS采用一种称为 rack-aware的策略来改进数据的可靠性、有效性和网络带宽的利用。

数据传输

  • 1、数据在HDFS上存储的基本单位是Block,默认大小64M

  • 2、数据在Client和DataNode之间传输数据的基本单位是Packet,默认最大为65557B;

  • 3、数据传输Pipeline,宏观上看是Block Pipeline,但是微观上其实是Packet Pipeline。

HDFS命令行:

支持的操作:

  • 创建 复制 移动 和删除文件.

  • 实现管理职责 - chmod, chown, chgrp.

  • 设置复制文件的比率

  • 使用Head, tail, cat查看文件

支持的全部命令说明见:http://hadoop.apache.org/docs...

DFSShell
  HDFS允许用户数据组织成文件和文件夹的方式,它提供一个叫DFSShell的接口,使用户可以和HDFS中的数据交互。

hadoop fs|dfs -<cmd> [args]   

例如:列出HDFS指定目录下的文件: 
hadoop fs -ls <path>  

HDFS路径(称为资源)使用URI格式来表示:scheme://authority/path  scheme协议名:file或hdfs;authority:NameNode主机名+端口号;path:文件路径
hadoop fs -ls hdfs://winstar:9000/user/root  
如果在core-site.xml中有如下配置,则直接使用/user/root(路径)即可。即hadoop fs -ls /user/root 
<configuration>  
  <property>  
    <name>fs.default.name</name>  
    <value>hdfs://winstar:9000</value>  
  </property>  
</configuration> 

HDFS默认工作目录为/user/$USER,$USER表示当前账户的用户名。

常用的命令示例

在user/目录下创建trunk子目录:
hadoop fs -mkdir /user/trunk  

列出user/目录下所有文件和子目录:
hadoop fs -ls /user  

递归列出user/目录下所有文件和子目录
hadoop fs -lsr /user  

将本地目录下test.txt文件上传至HDFS当前目录
hadoop fs -put test.txt .  

将HDFS/user/root/test.txt文件下载至本地当前目录
hadoop fs -get /user/root/test.txt  

查看/user/root/test.txt文件内容
hadoop fs -cat /user/root/test.txt  

查看/user/root/test.txt文件的尾部内容(最后1K)
hadoop fs -tail /user/root/test.txt  

删除/user/root/test.txt文件
hadoop fs -rm /user/root/test.txt  

查看ls命令的帮助文档
hadoop fs -help ls  

使用Java API访问HDFS

APIDOC: http://hadoop.apache.org/docs...

  • Configuration类:该类的对象封装了配置信息,这些配置信息来自core-*.xml;

  • FileSystem类:文件系统类,可使用该类的方法对文件/目录进行操作。一般通过FileSystem的静态方法get获得一个文件系统对象;

  • FSDataInputStream和FSDataOutputStream类:HDFS中的输入输出流。分别通过FileSystem的open方法和create方法获得。

操作文件程序的基本步骤

  • 得到Configuration对象

  • 得到FileSystem对象

  • 进行文件操作

将本地文件上传到HDFS

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.*;

public class CopyFromLocal {
    public static void main(String[] args) throws Exception {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://localhost:9001");
        FileSystem fs = FileSystem.get(conf);
        Path src = new Path("/usr/local/hadoop-2.7.2/README.txt");
        Path dst = new Path("hdfs://localhost:9001/home/");
        fs.copyFromLocalFile(src, dst);
        System.out.println("upload to "+conf.get("fs.default.name"));
        fs.close();
    }
}

将HDFS上的文件传回到本地

        Configuration conf = new Configuration();
        conf.set("fs.defaultFS", "hdfs://localhost:9001");
        FileSystem fs = FileSystem.get(conf);
        Path src = new Path("hdfs://localhost:9001/home/README.txt");
        Path dst = new Path("/home/hadoop/Desktop/");
        fs.copyToLocalFile(src, dst);
        fs.close();

删除文件/目录, 当删除对象为目录时,将第二个参数设为true否则将产生异常

        Path dfs = new Path("hdfs://localhost:9001/home/test.txt");
        System.out.println(fs.delete(dfs,true));
        fs.close();

创建目录

        Path dfs = new Path("hdfs://localhost:9001/home1/");
        System.out.println(fs.mkdirs(dfs));
        fs.close();

在HDFS上创建文件,并写入

        Path dfs = new Path("hdfs://localhost:9001/home/test.txt");
        FSDataOutputStream outputStream = fs.create(dfs);
        byte[] buff = "hello world!".getBytes();
        outputStream.write(buff,0, buff.length);
        outputStream.close();
        fs.close();

读取文件

        Path dst = new Path("hdfs://localhost:9001/home/test.txt");
        if(fs.exists(dst) && !fs.isDirectory(dst)) {
            FSDataInputStream is = fs.open(dst);
            FileStatus stat = fs.getFileStatus(dst);
            byte[] buffer = new byte[(int) stat.getLen()];
            is.read(buffer);
            System.out.println(new String(buffer));
            is.close();
            fs.close();
        } else {
            throw new Exception("fail to read file "+dst.toString());
        }

追加文件内容,注:需要将hdfs-site.xml中的dfs.support.append属性设置为true

<property>
    <name>dfs.support.append</name>
    <value>true</value>
</property>
        Configuration conf = new Configuration();
        //conf.setBoolean( "dfs.support.append", true );
        FileSystem fs = FileSystem.get(conf);
        Path dfs = new Path("hdfs://localhost:9001/home/test.txt");
        FSDataOutputStream outputStream = fs.append(dfs);
        byte[] buff = "test".getBytes();
        outputStream.write(buff);
        outputStream.close();
        fs.close();

参考:

http://hadoop.apache.org/docs...
http://blog.csdn.net/xman_200...
http://www.jdon.com/bigdata/h...
http://www.cnblogs.com/davidw...
http://www.cnblogs.com/laov/p...
https://my.oschina.net/crxy/b...
http://blog.csdn.net/fanxiaob...
http://www.cnblogs.com/finalb...
http://blog.csdn.net/yew1eb/a...


xbynet
1k 声望124 粉丝

不雨花犹落,无风絮自飞