共计 7800 个字符,预计需要花费 20 分钟才能阅读完成。
学习完 Hadoop 权威指南有一段时间了,现在再回顾和总结一下 HDFS 的知识点。
1、HDFS 的设计
HDFS 是什么:HDFS 即 Hadoop 分布式文件系统(Hadoop Distributed Filesystem),以 流式数据访问模式 来存储超大文件,运行于商用硬件集群上,是管理网络中跨多台计算机存储的文件系统。
HDFS 不适合用在:要求低时间延迟数据访问的应用,存储大量的小文件,多用户写入,任意修改文件。
2、HDFS 的概念
HDFS 数据块:HDFS 上的文件被划分为块大小的多个分块,作为独立的存储单元,称为数据块,默认大小是 64MB。
使用数据块的好处是:
- 一个文件的大小可以大于网络中任意一个磁盘的容量。文件的所有块不需要存储在同一个磁盘上,因此它们可以利用集群上的任意一个磁盘进行存储。
- 简化了存储子系统的设计,将存储子系统控制单元设置为块,可简化存储管理,同时元数据就不需要和块一同存储,用一个单独的系统就可以管理这些块的元数据。
- 数据块适合用于数据备份进而提供数据容错能力和提高可用性。
查看块信息
HDFS 的三个节点:Namenode,Datanode,Secondary Namenode
Namenode:HDFS 的守护进程,用来管理文件系统的命名空间,负责记录文件是如何分割成数据块,以及这些数据块分别被存储到那些数据节点上,它的主要功能是对内存及 IO 进行集中管理。
Datanode:文件系统的工作节点,根据需要存储和检索数据块,并且定期向 namenode 发送他们所存储的块的列表。
Secondary Namenode:辅助后台程序,与 NameNode 进行通信,以便定期保存 HDFS 元数据的快照。
HDFS Federation(联邦 HDFS):
通过添加 namenode 实现扩展,其中每个 namenode 管理文件系统命名空间中的一部分。每个 namenode 维护一个命名空间卷,包括命名空间的源数据和该命名空间下的文件的所有数据块的数据块池。
HDFS 的高可用性(High-Availability)
Hadoop 的 2.x 发行版本在 HDFS 中增加了对高可用性(HA)的支持。在这一实现中,配置了一对活动 - 备用(active-standby)namenode。当活动 namenode 失效,备用 namenode 就会接管它的任务并开始服务于来自客户端的请求,不会有明显的中断。
架构的实现包括:
- namenode 之间通过高可用的共享存储实现编辑日志的共享。
- datanode 同时向两个 namenode 发送数据块处理报告。
- 客户端使用特定的机制来处理 namenode 的失效问题,这一机制对用户是透明的。
故障转移控制器:管理着将活动 namenode 转移给备用 namenode 的转换过程,基于 ZooKeeper 并由此确保有且仅有一个活动 namenode。每一个 namenode 运行着一个轻量级的故障转移控制器,其工作就是监视宿主 namenode 是否失效并在 namenode 失效时进行故障切换。
3、命令行接口
两个属性项:fs.default.name 用来设置 Hadoop 的默认文件系统,设置 hdfs URL 则是配置 HDFS 为 Hadoop 的默认文件系统。dfs.replication 设置文件系统块的副本个数
文件系统的基本操作:hadoop fs -help 可以获取所有的命令及其解释
常用的有:
- hadoop fs -ls / 列出 hdfs 文件系统根目录下的目录和文件
- hadoop fs -copyFromLocal <local path> <hdfs path> 从本地文件系统将一个文件复制到 HDFS
- hadoop fs -rm -r <hdfs dir or file> 删除文件或文件夹及文件夹下的文件
- hadoop fs -mkdir <hdfs dir> 在 hdfs 中新建文件夹
HDFS 的文件访问权限:只读权限(r),写入权限(w),可执行权限(x)
4、Hadoop 文件系统
Hadoop 有一个抽象的文件系统概念,HDFS 只是其中的一个实现。Java 抽象接口 org.apache.hadoop.fs.FileSystem 定义了 Hadoop 中的一个文件系统接口。该抽象类实现 HDFS 的具体实现是 hdfs.DistributedFileSystem
5、Java 接口
最简单的从 Hadoop URL 读取数据(这里在 Eclipse 上连接 HDFS 编译运行)
package filesystem;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.hadoop.fs.FsUrlStreamHandlerFactory;
import org.apache.hadoop.io.IOUtils;
public class URLCat {static {URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
}
public static void main(String[] args) throws MalformedURLException, IOException {InputStream in = null;
String input = "hdfs://192.168.92.138:9000/user/test.txt";
try {in = new URL(input).openStream();
IOUtils.copyBytes(in, System.out, 4096,false);
}finally {IOUtils.closeStream(in);
}
}
}
这里调用 Hadoop 的 IOUtils 类,在输入流和输出流之间复制数据(in 和 System.out)最后两个参数用于第一个设置复制的缓冲区大小,第二个设置结束后是否关闭数据流。
还可以通过 FileSystem API 读取数据
代码如下:
package filesystem;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
public class FileSystemCat {public static void main(String[] args) throws IOException {String uri = "hdfs://192.168.92.136:9000/user/test.txt";
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri),conf);
InputStream in = null;
try {in = fs.open(new Path(uri));
IOUtils.copyBytes(in, System.out, 1024,false);
}finally {IOUtils.closeStream(in);
}
}
}
这里调用 open()函数来获取文件的输入流,FileSystem 的 get()方法获取 FileSystem 实例。
使用 FileSystem API 写入数据
代码如下:
package filesystem;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.util.Progressable;
public class FileCopyWithProgress {public static void main(String[] args) throws Exception {String localSrc = "E:\\share\\input\\2007_12_1.txt";
String dst = "hdfs://192.168.92.136:9000/user/logs/2008_10_2.txt";
InputStream in = new BufferedInputStream(new FileInputStream(localSrc));
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(dst),conf);
OutputStream out = fs.create(new Path(dst),new Progressable() {public void progress() {System.out.print("*");
}
});
IOUtils.copyBytes(in, out, 1024,true);
}
}
FileSystem 的 create()方法用于新建文件, 返回 FSDataOutputStream 对象。Progressable()用于传递回掉窗口,可以用来把数据写入 datanode 的进度通知给应用。
使用 FileSystem API 删除数据
代码如下:
package filesystem;
import java.net.URI;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
public class FileDelete {public static void main(String[] args) throws Exception{String uri = "hdfs://192.168.92.136:9000/user/1400.txt";
Configuration conf = new Configuration();
FileSystem fs = FileSystem.get(URI.create(uri),conf);
fs.delete(new Path(uri));
}
}
使用 delete()方法来永久性删除文件或目录。
FileSystem 的其它一些方法:
- public boolean mkdirs(Path f) throws IOException 用来创建目录,创建成功返回 true。
- public FileStatus getFileStates(Path f) throws FIleNotFoundException 用来获取文件或目录的 FileStatus 对象。
- public FileStatus[] listStatus(Path f)throws IOException 列出目录中的内容
- public FileStatus[] globStatu(Path pathPattern) throws IOException 返回与其路径匹配于指定模式的所有文件的 FileStatus 对象数组,并按路径排序
6、数据流
HDFS 读取文件过程:
过程描述:
(1)客户端调用 FileSyste 对象的 open()方法在分布式文件系统中 打开要读取的文件。
(2)分布式文件系统通过使用 RPC(远程过程调用)来调用 namenode,确定文件起始块的位置。
(3)分布式文件系统的 DistributedFileSystem 类返回一个支持文件定位的输入流 FSDataInputStream 对象,FSDataInputStream 对象接着封装 DFSInputStream 对象(存储着文件起始几个块的 datanode 地址 ),客户端对这个输入流调用 read() 方法。
(4)DFSInputStream 连接距离最近的 datanode,通过反复调用 read 方法,将数据从 datanode 传输到客户端。
(5)到达块的末端时,DFSInputStream 关闭与该 datanode 的连接,寻找下一个块的最佳 datanode。
(6)客户端完成读取,对 FSDataInputStream 调用 close()方法 关闭连接。
HDFS 文件写入的过程:
过程描述:
写文件过程分析:
(1)客户端通过对 DistributedFileSystem 对象调用 create()函数来 新建文件。
(2)分布式文件系统对 namenod 创建一个 RPC 调用,在文件系统的 命名空间中新建一个文件。
(3)Namenode 对新建文件进行检查无误后,分布式文件系统返回给客户端一个 FSDataOutputStream 对象,FSDataOutputStream 对象封装一个 DFSoutPutstream 对象,负责处理 namenode 和 datanode 之间的通信,客户端开始写入数据。
(4)FSDataOutputStream 将 数据分成一个一个的数据包,写入内部队列“数据队列”,DataStreamer 负责将数据包依次流式传输到由一组 namenode 构成的管线中。
(5)DFSOutputStream 维护着确认队列来等待 datanode 收到确认回执,收到管道中所有 datanode 确认后,数据包从确认队列删除。
(6)客户端完成数据的写入 ,对数据流调用 close() 方法。
(7)namenode 确认完成。
namenode 如何选择在那个 datanode 存储复本?
需要对可靠性,写入带宽和读取带宽进行权衡。默认布局是:在运行客户端的节点上放第一个复本(如果客户端运行在集群之外,则在避免挑选存储太满或太忙的节点的情况下随机选择一个节点。)第二个复本放在与第一个不同且随机另外选择的机架中节点上。第三个复本与第二个复本放在同一个机架上,且随机选择另一个节点。其它复本放在集群中随机选择的节点中,尽量避免在同一个机架上放太多复本。
一个复本个数为 3 的集群放置位置如图:
HDFS 一致性:HDFS 在写数据务必要保证数据的一致性与持久性,目前 HDFS 提供的两种两个保证数据一致性的方法 hsync() 方法和 hflush()方法。
hflush: 保证 flush 的数据被新的 reader 读到,但是不保证数据被 datanode 持久化。
hsync: 与 hflush 几乎一样,不同的是 hsync 保证数据被 datanode 持久化。
7、通过 Flume 和 Sqoop 导入数据
可以考虑使用一些现成的工具将数据导入。
Apache Fluem 是一个将 大规模流数据 导入 HDFS 的工具。典型应用是从另外一个系统中收集日志数据并实现在 HDFS 中的聚集操作以便用于后期的分析操作。
Apache Sqoop 用来将数据从 结构化存储设备 批量导入 HDFS 中,例如关系数据库。Sqoop 应用场景是组织将白天生产的数据库中的数据在晚间导入 Hive 数据仓库中进行分析。
8、通过 distcp 并行复制
distcp 分布式复制程序,它从 Hadoop 文件系统间复制大量数据,也可以将大量的数据复制到 Hadoop。
典型应用场景是在 HDFS 集群之间传输数据。
% hadoop distcp hdfs://namenode1/foo hdfs://namenode2/bar
9、Hadoop 存档
HDFS 中每个文件均按块方式存储,每个块的元数据存储在 namenode 的内存中,因此 Hadoop 存储小文件会非常低效。因为大量的小文件会耗尽 namenode 中的大部分内存。Hadoop 的存档文件或 HAR 文件,将文件存入 HDFS 块,减少 namenode 内存使用,允许对文件进行透明地访问。
Hadoop 存档是通过 archive 工具 根据一组文件创建而来的。运行 archive 指令:
% hadoop archive -archiveName files.har /my/files /my
列出 HAR 文件的组成部分:
% hadoop fs -ls /my/files.har
files.har 是存档文件的名称,这句指令存储 HDFS 下 /my/files 中的文件。
HAR 文件的组成部分:两个索引文件以及部分文件的集合。
存档的不足:
新建一个存档文件会创建原始文件的一个副本,因此需要与要存档的文件容量相同大小的磁盘空间。
一旦存档文件,不能从中增加或删除文件。
Hadoop2.3-HA 高可用集群环境搭建 http://www.linuxidc.com/Linux/2017-03/142155.htm
Hadoop 项目之基于 CentOS7 的 Cloudera 5.10.1(CDH)的安装部署 http://www.linuxidc.com/Linux/2017-04/143095.htm
Hadoop2.7.2 集群搭建详解(高可用)http://www.linuxidc.com/Linux/2017-03/142052.htm
使用 Ambari 来部署 Hadoop 集群(搭建内网 HDP 源)http://www.linuxidc.com/Linux/2017-03/142136.htm
Ubuntu 14.04 下 Hadoop 集群安装 http://www.linuxidc.com/Linux/2017-02/140783.htm
CentOS 6.7 安装 Hadoop 2.7.2 http://www.linuxidc.com/Linux/2017-08/146232.htm
Ubuntu 16.04 上构建分布式 Hadoop-2.7.3 集群 http://www.linuxidc.com/Linux/2017-07/145503.htm
CentOS 7 下 Hadoop 2.6.4 分布式集群环境搭建 http://www.linuxidc.com/Linux/2017-06/144932.htm
Hadoop2.7.3+Spark2.1.0 完全分布式集群搭建过程 http://www.linuxidc.com/Linux/2017-06/144926.htm
更多 Hadoop 相关信息见Hadoop 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=13
本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-08/146386.htm