Hadoop分布式文件系统HDFS

1. 简介

简单来说,HDFS:

  • 分块存储超大文件,默认每块64M,文件大小高达PB级别
  • 最适合一次写入、多次读写模式
  • 流式读写数据,高吞吐量,但高延迟
  • 可运行在普通商用机器上,多副本存储,能动态处理机器失效情况,可靠性高
  • 多用户读写,只能写到文件末尾
  • 採用Master/Slaves架构,对应于NameNode和DataNode,NameNode管理文件系统的命名空间,DataNode存放数据

2. 网页查看文件系统

访问http://192.168.56.102:50070/dfshealth.jsp以查看HDFS情况,包括容量、节点数等信息。可以在页面中点击Browse the filesystem查看整个文件系统的目录和内容。该页面还可以查看DataNode的运行情况,十分方便。

不足:不能上传文件或追加文件。

3. 工具查看文件系统

下载mucommander,下载绿色版就可以了,添加的时候选择HTTP,进去之后才能选择HDFS。

4. 命令行操作文件系统

关于HDFS的文件路径

文件系统分为绝对路径和相对路径。绝对路径以“/”开头。相对路径相对的是/user/{username},如input对应的是/user/{username}/input

如果给定路径下没有hdfs系统或对应的文件,则会出现“No such file or directory”的错误提示。

常用命令

获得帮助:

hadoop fs -help

创建文件夹:

hadoop fs -mkdir 文件夹名称 #如果是相对路径,那麽该文件夹放在HDFS路径/user/${username}/下面
hadoop fs -mkdir input  #创建一个/user/${username}/input的文件夹
hadoop fs -mkdir /hello/world #创建一个/hello/world的文件夹

显示文件列表:

hadoop fs -ls /
hadoop fs -ls /hello/
hadoop fs -ls hdfs://192.168.56.102:9000/user/hadoop/ #显示远程机器hdfs目录

文件列表结果类似Linux的ls -l命令结果。

递归显示文件或文件夹下所有子文件夹及文件:

hadoop fs -lsr /

将本地文件复制到HDFS:

hadoop fs -copyFromLocal 本地路径 HDFS路径
#示例:
echo helloworld > hi.txt
hadoop fs -copyFromLocal hi.txt /hello/world 

本地文件可以是单个文件或整个目录。

查看文件内容:

hadoop fs -cat /hello/world/hi.txt

将HDFS的文件複製到本地:

hadoop fs -copyToLocal HDFS路径 本地路径 

两个HDFS间複製文件:

hadoop distcp hdfs://namenode1/foo hdfs://namenode2/bar
#不同的hdfs版本之间可能会出现兼容问题,可使用hftp协议,将地址写为:hftp://namenode1:50070/foo

删除文件或文件夹:

hadoop fs -rm -skipTrash HDFS路径 # 删除文件
hadoop fs -rmr -skipTrash HDFS路径 # 删除文件夹

归档若干文件(将多个文件(文件夹)打包至一个文件):

hadoop archive -archiveName 归档名称.har 归档文件夹1 归档文件夹2 归档文件夹3 归档存放位置

注意:归档文件夹可以1个或多个。完成后,在归档存放位置下降出现归档名称.har的文件夹。查看归档内容:

hadoop fs -lsr har:///user/hadoop/test.har # har://后面必须给出绝对路径
hadoop fs -cat har:///user/hadoop/test/test2.har/user/hadoop/input/file02 # 查看文件内容

可以看出,har像是支援har格式的文件系统,通过har协议可以执行HDFS一致的操作。

其它操作参见这裡

5. 编程操作文件系统

这裡以java编程语言为例,仅列出代码,关于编译和运行,详见hadoop:ide。

输出文件内容例子FileSystemCat.java:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;

/**
* 2011年11月16日 上午11:26:23
*/
public class FileSystemCat {

    public static void main(String[] args) throws IOException {
        if (args.length != 1) {
            System.out.println("usage: hadoop FileSystemCat URI");
            return;
        }
        // 所有文件系统的操作接口均通过FileSystem类提供
        FileSystem fs = FileSystem.get(new Configuration());
        FSDataInputStream in = null;
        try {
            in = fs.open(new Path(args[0]));
            // 读取所有数据并输出,4K是缓存
            IOUtils.copyBytes(in, System.out, 4096, false);
            // 获得当前的文件偏移位置pos
            System.out.println("pos:" + in.getPos());

            // 重新seek到文件开头位置
            in.seek(0);
            // 重新读取所有数据
            IOUtils.copyBytes(in, System.out, 4096, false);
        } finally {
            IOUtils.closeStream(in);
        }
    }
}

注意:seek()方法消耗的资源较大,应尽量减少使用。

上传文件例子,同样适用FileSystem提供的方法FileCopyWithProgress.java:

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;

/**
 * 2011年11月16日 上午11:26:23
 */
public class FileCopyWithProgress {

    public static void main(String[] args) throws IOException {
        if (args.length != 2) {
            System.out.println("usage: FileCopyWithProgress localURI hdfsURI");
            return;
        }

        String src = args[0];
        String dst = args[1];
        InputStream in = null;
        FSDataOutputStream out = null;
        try {
            // 读取本地文件内容
            in = new BufferedInputStream(new FileInputStream(src));

            Configuration conf = new Configuration();
            FileSystem fs = FileSystem.get(conf);
            // 创建到HDFS的输出流
            out = fs.create(new Path(dst), new Progressable() {
                // 每1%调用一次这个方法
                @Override
                public void progress() {
                    System.out.print('.');
                }
            });

            // 将输入流的数据拷贝到输出流
            IOUtils.copyBytes(in, out, 4096, false);
        } finally {
            IOUtils.closeStream(in);
            IOUtils.closeStream(out);
        }
    }

}

注意:如果父目录不存在,create会自动创建的,因此不需要事先mkdir。

文件一致性 FSDataOutputStream的sync方法强制将数据写到磁盘上,以保证新的读者可以读取到。调用close方法会自动调用sync方法。

如果需要向某一文件追加数据,则调用fs.append方法获得一个FSDataOutputStream对象。如果需要删除某一文件,则调用fs.delete方法。

获得文件信息GetFileStatus.java:

import java.io.IOException;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;


/**
 * 2012-01-15 16:13
 */
public class GetFileStatus {

    public static void main(String[] args) throws IOException {
        if (args.length != 1) {
            System.out.println("usage: GetFileStatus URI");
            return;
        }
        String uri = args[0];

        Configuration conf = new Configuration();
        FileSystem fs = FileSystem.get(conf);

        // 获得某一文件的信息
        FileStatus fst = fs.getFileStatus(new Path(uri));
        System.out.println(fst.getPath());
        System.out.println(fst.getOwner());

    }
}

注意:还可以通过fs.listStatus方法获得某一文件夹下所有的文件信息,返回文件信息列表;通过fs.globStatus方法支持匹配符方式获得文件信息列表。

文档更新时间: 2018-11-10 18:58   作者:nick