HDFS笔记——技术点汇总

目录

· 概况

· 原理

    · HDFS 架构

    · 

    · NameNode

    · SecondaryNameNode

    · fsimage与edits合并

    · DataNode

    · 数据读写

    · 容错机制

    · 数据完整性

    · NameNode HA

    · NameNode Federation

    · HDFS Snapshots

· 操作

· API


 

概况

1. 文件系统抽象类FileSystem

    a) 源码

1 public abstract class FileSystem extends Configured implements Closeable {
2     // ...
3 }

    b) 实现类

文件系统

URI方案

Java实现

定义

Local

file

org.apache.hadoop.fs.LocalFileSystem

已使用客户端校验的本地文件系统。未使用校验的本地磁盘文件系统由RawLocalFileSystem实现。

HDFS

hdfs

org.apache.hadoop.hdfs.DistributedFileSystem

Hadoop分布式文件系统

HFTP

hftp

org.apache.hadoop.hdfs.web.HftpFileSystem

支持通过HTTP方式以只读方式访问HDFS,通常和distcp命令结合使用。

HSFTP

hsftp

org.apache.hadoop.hdfs.web.HsftpFileSystem

支持通过HTTPS方式以只读方式访问HDFS

HAR

har

org.apache.hadoop.fs.HarFileSystem

构建在Hadoop文件系统之上,对文件归档。Hadoop归档文件主要用来减少NameNode内存使用。

FTP

ftp

org.apache.hadoop.fs.ftp.FtpFileSystem

FTP服务器支持的文件系统。

S3(原生)

s3n

org.apache.hadoop.fs.s3native.NativeS3FileSystem

基于Amazon S3的文件系统。

S3(基于块)

s3

org.apache.hadoop.fs.s3.S3FileSystem

基于Amazon S3的文件系统,解决S35GB文件大小限制。

2. HDFS特点

    a) 适合存储超大文件:存储在HDFS的文件大多在GBTB级别,甚至PB级别。

    b) 运行于廉价硬件之上:设计时已考虑集群规模足够大时,节点故障是常态。HDFS无需运行在高可靠且昂贵的服务器,普通的PC服务器即可。

    c) 流式数据访问:HDFS认为一次写入、多次读取时最高效的访问模式。数据集生成后,会长时间在此数据集上进行各种分析,每次分析都将设计该数据集的大部分甚至全部数据。

3. HDFS缺点

    a) 实时数据访问弱:HDFS针对数据吞吐量做了优化,而牺牲了读取效率,无法做到秒级或毫秒级响应。

    b) 大量小文件:HDFS启动时,NameNode将全部元数据加载到内存,而一般一个HDFS文件、目录和数据块的存储信息约150字节,因此文件个数受限于NameNode节点内存。过多小文件很快达到上限。

    c) 多用户写入,任意修改文件:HDFS文件同时只能有一个写入者,且写操作总在文件末。

原理

HDFS 架构

1. 架构图

2. 守护进程

名称

集群中数目

作用

NameNode

1(默认)

存储文件系统元数据,存储文件与数据块映射,并提供文件系统全景图

SecondaryNameNode

1

备份NameNode数据,并负责镜像与NameNode日志数据合并

DataNode

多个(至少1个)

存储块数据

1. 文件系统块:块大小是磁盘块大小的整数倍,如ext34KBNTFS4KB

2. HDFS

    a) HDFS文件:被划分为块大小的多个分块。

    b) 默认大小:64MB

    c) 较大原因:最小化寻址开销(块足够大,从磁盘传输数据块的时间明显大于定位块开始位置所需的时间)。

    d) 配置:hdfs-site.xml的参数“dfs.block.size”。

3. 副本

    a) 含义:每个HDFS块在集群中保存的份数,默认为3

    b) 效果:值越高,冗余性越好,占用存储越多。

    c) 配置:hdfs-site.xml的参数“dfs.replication”。

4. 块分布示例

    a) 环境:文件大小150MB,块大小64MB,副本数2

    b) 分布:第164MB,第264MB,第322MB

5. 块布局策略

    a) 第1个副本:如果HDFS客户端在集群内,默认布局在客户端所在节点;否则随机选择一个节点,但会尽量避免存储太满或太忙的节点。

    b) 第2个副本:与第1个副本不同且随机另外机架中的节点。

    c) 第3个副本:与第2个副本相同机架且随机选择另外一个节点。

    d) 其他副本(副本数>3):集群随机选择节点,但会尽量避免在相同机架上布局太多副本。

    e) 由NameNode选择节点。

    f) 示意图:副本数为3

 

NameNode

1. 职责:HDFS主从(Master/Slave)架构的主角色。

2. NameNode存储的文件

    a) fsimageHDFS元数据的完整快照,每次NameNode启动时,默认加载最新的fsimage

    b) editsfsimage的编辑日志。

SecondaryNameNode

1. 职责:定期合并fsimageedits的辅助守护进程。

2. 部署:生产环境一般单独部署在一台服务器。

fsimage与edits合并

1. 不直接更新fsimage的原因:fsimage是一个大型文件,如果频繁执行写操作,会使系统运行极慢。

2. 定期合并的原因

    a) 随时间推移,edits越来越大,一旦发生故障,回滚时间非常长。

    b) 如果由NameNode合并,则NameNode可能无法提供足够资源为集群服务,所以由SecondaryNameNode合并。

3. 定期合并的过程

 

    a) SecondaryNameNode通知NameNode准备提交edits文件,此时NameNode产生edits.new

    b) SecondaryNameNode通过HTTP GET方式获取NameNodefsimageedits文件(在SecondaryNameNodecurrent同级目录下可见 temp.check-point或者previous-checkpoint目录,这些目录中存储着从NameNode拷贝来的镜像文件);

    c) SecondaryNameNode开始合并获取的上述两个文件,产生一个新的fsimage文件fsimage.ckpt

    d) SecondaryNameNodeHTTP POST方式发送fsimage.ckptNameNode

    e) NameNodefsimage.ckptedits.new文件分别重命名为fsimageedits,然后更新fstime,整个checkpoint过程结束。

4. 定期合并的默认时机

    a) 每小时一次;

    b) 或当NameNode edits文件达到默认的64MB时。

DataNode

1. 职责:HDFS主从(Master/Slave)架构的从角色。

2. 块文件:每个块都是一个文件,默认位于参数“dfs.data.dir”目录的current目录下,文件名blk_blkID

3. 上报:DataNode启动时,向NameNode上报块信息(Block Report)。

数据读写

1. 文件读取过程

 

    a) HDFS对客户端身份验证,两种方式:通过信任的客户端,由其指定用户名;诸如Kerberos等强制验证机制。

    b) 客户端告知NameNode要读取的文件。当文件存在且用户有访问权限时,NameNode告知客户端该文件第1个块的标号以及保存该块的DataNode列表(按DataNode与客户端距离排序)。

    c) 客户端直接访问最适合的DataNode读取块,该过程一直重复直到文件所有块读取完成或客户端主动关闭文件流。

    d) 特殊情况,客户端是DataNode时,将从本地DataNode读取数据。

2. 文件写入过程

    a) 客户端发送请求,打开一个要写入的文件。如果客户端有写入权限,则请求被送达NameNode,并建立该文件的元数据,但该文件元数据未和任何数据库关联。

    b) 客户端收到“打开文件成功”响应后开始将数据写入流,数据被自动拆分成数据包,并将数据包保存在内存队列。

    c) 客户端一个独立线程从队列读取数据包,并向NameNode请求一组DataNode列表,以便写入下一个块的多个副本。客户端直接连接到列表中第1DataNode,而该DataNode又连接到第2DataNode,第2个又连接到第3各,如此建立块的复制管道。各DataNode都会确认收到的数据包成功写入磁盘。客户端维护着一个列表,记录由尚未收到确认信息的数据包。

    d) 当块被写入一组DataNode后,客户端重新向NameNode申请下一组DataNode

    e) 最终,全部数据包写入,关闭数据管道并通知NameNode写操作完成。

容错机制

1. 心跳机制:当NameNode未收到DataNode心跳包时,NameNode认为该DataNode上的数据无效。新的IO操作将不会派发给该DataNode;如果块副本数小于hdfs-site.xml的参数“dfs.replication.min”,则开始自动复制新副本到其他DataNode节点。

2. 块完整性校验:HDFS记录文件所有块的校验和,当确认校验和不一致时,会从其他DataNode节点获取块的副本。

3. 集群负载均衡:节点失效或增加可能导致数据分布不均,当某个DataNode空闲空间大于临界值时,HDFS自动从其他DataNode迁移数据保持平衡。

4. fsimageedits:如果NameNode单点部署,fsimageedits文件损坏时,可手工从SecondaryNameNode定期备份手工恢复。

5. 文件删除:删除并未从NameNode移除,而是存放在/trash目录可随时恢复,直到超过hdfs-site.xml的参数“fs.trash.interval”的值(秒)。

数据完整性

1. 写入时校验:客户端将数据及其校验和发送到一组DataNode组成复制管道,管道最后一个DataNode负责验证校验和,如果错误,客户端便收到ChecksumException

2. 读取时校验:客户端对比读取数据与DataNode存储的校验和,验证成功后告知DataNodeDataNode记录到验证校验和日志(包括每个块的最后验证时间)。

3. 后台定期校验:每个DataNode后台有一个DataBlockScanner线程,定期验证Data上所有数据块。

4. 块修复:NameNode将块标记为已损坏,之后安排该块的一个副本复制到另一DataNode以达到预设副本数,最后删除已损坏的块。

NameNode HA

1. 场景

    a) NameNode的热备(SecondaryNameNodeNameNode的冷备)。

    b) 生产环境必备功能。

2. NameNode HA架构

    a) 一个NameNode处于主状态(Active),处理客户端和DataNode请求,并把edits写入本地和共享编辑日志(NFSQJM等)。

    b) 另一个NameNode处于从状态(StandBy),启动时加载fsimage文件,然后周期性从共享编辑日志获取edits,保持与主NameNode同步。

    c) 为实现主节点宕机后从节点迅速提供服务,DataNode需同时向两个NameNode汇报(Block Report)。

NameNode Federation

1. 解决问题:解决NameNode伸缩性、隔离性,以及单NameNode性能方面的问题。

2. 场景:集群规模在1000台以下时,几乎无需NameNode Federation

 

HDFS Snapshots

1. 原理:HDFS Snapshots是一个只读的基于时间点的文件系统副本,快照可以时整个文件系统也可以是其中一部分。

2. 场景:常用来作为数据备份,防止用户误操作和容灾。

操作

1. HDFS文件系统命令

命令

功能

举例

hadoop dfs -ls <path>

列出文件或目录内容

hadoop dfs -ls /

hadoop dfs -lsr <path>

递归列出目录内容

hadoop dfs -lsr /

hadoop dfs -df <path>

查看目录使用情况

hadoop dfs -df /

hadoop dfs -du <path>

显示目录中所有文件及目录的大小

hadoop dfs -du /

hadoop dfs -dus <path>

显示目录总大小

hadoop dfs -dus /

hadoop dfs -count [-q] <path>

显示目录下目录数及文件数,格式为“目录数 文件数 大小 文件名”,-q指查看文件索引

hadoop dfs -count /

hadoop dfs -mv <src> <dst>

HDFS文件移动到目标目录

hadoop dfs -mv /user/hadoop/a.txt /user/test

hadoop dfs -rm [-skipTrash] <path>

HDFS文件移动到回收站,-skipTrash指直接删除

hadoop dfs -rm /text.txt

hadoop dfs -rmr [-skipTrash] <path>

HDFS目录移动到回收站,-skipTrash指直接删除

hadoop dfs -rmr /text

hadoop dfs -expunge

清空回收站

hadoop dfs -expunge

hadoop dfs -put <localsrc> ... <dst>

将本地文件上传到HDFS目录

hadoop dfs -put /home/hadoop/test.txt /user/hadoop

hadoop dfs -copyFromLocal <localsrc> ... <dst>

类似-put

hadoop dfs -copyFromLocal /home/hadoop/test.txt /user/hadoop

hadoop dfs -moveFromLocal <localsrc> ... <dst>

将本地文件移动到HDFS目录

hadoop dfs -moveFromLocal /home/hadoop/test.txt /user/hadoop

hadoop dfs -get [-ignoreCrc] [-crc] <src> <localdst>

HDFS文件下载到本地目录,-ignoreCrc指忽略CRC校验失败,-crc指下载文件及CRC信息

hadoop dfs -get /user/hadoop/a.txt /home/hadoop

hadoop dfs -getmerge <src> <localdst> [addnl]

HDFS目录下所有文件按文件名排序合并成一个文件下载到本地目录,addnl指每个文件结尾添加一个换行符

hadoop dfs -getmerge /user/test /home/hadoop/o

hadoop dfs -cat <src>

浏览HDFS文件内容

hadoop dfs -cat /user/hadoop/test.txt

hadoop dfs -text <src>

以文本方式浏览HDFS文件

hadoop dfs -text /user/test.txt

hadoop dfs -copyToLocal [-ignoreCrc] [-crc] <src> <localdst>

类似-get

hadoop dfs -copyToLocal /user/hadoop/a.txt /home/hadoop

hadoop dfs -moveToLocal [-crc] <src> <localdst>

HDFS文件移动到本地目录

hadoop dfs -moveToLocal /user/hadoop/a.txt /home/hadoop

hadoop dfs -mkdir <path>

创建HDFS目录

hadoop dfs -mkdir /user/test

hadoop dfs -setrep [-R] [-w] <rep> <path/file>

设置文件副本数,-R指递归执行

hadoop dfs -setrep 5 -R /user/test

hadoop dfs -touchz <path>

创建0字节的HDFS空文件

hadoop dfs -touchz /user/hadoop/test

hadoop dfs -test -[ezd] <path>

检查HDFS文件,-e指检查存在(存在返回0),-z指检查0字节(是返回0),-d指检查目录(是返回1,否返回0

hadoop dfs -test -e /user/test.txt

hadoop dfs -stat [format] <path>

显示HDFS文件或目录统计信息

hadoop dfs -stat /user/test

hadoop dfs -tail [-f] <file>

浏览HDFS文件最后1KB内容,-f指随文件内容更新而更新

hadoop dfs -tail -f /user/test.txt

hadoop dfs -chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...

修改HDFS文件权限,-R指递归执行

hadoop dfs -chmod -R +r /user/test

hadoop dfs -chown [-R] [OWNER][:[GROUP]] PATH...

修改HDFS文件所属用户,-R指递归执行

hadoop dfs -chown -R hadoop:hadoop /user/test

hadoop dfs -chgrp [-R] GROUP PATH...

修改HDFS文件所属组别,-R指递归执行

hadoop dfs -chgrp -R hadoop /user/test

hadoop dfs -help

显示所有dfs命令帮助

hadoop dfs -help

2. HDFS其他命令参考官方文档

API

1. HDFS客户端要求:

    a) 配置文件hdfs-site.xml

    b) Maven依赖

1 <dependency>
2     <groupId>org.apache.hadoop</groupId>
3     <artifactId>hadoop-client</artifactId>
4     <version>2.6.5</version>
5 </dependency>

2. 示例执行方法

hadoop jar hdfs-test.jar hdfs.ReadFile

3. 读取文件

 1 package hdfs;
 2 
 3 import java.io.IOException;
 4 import java.io.InputStream;
 5 
 6 import org.apache.hadoop.conf.Configuration;
 7 import org.apache.hadoop.fs.FileSystem;
 8 import org.apache.hadoop.fs.Path;
 9 import org.apache.hadoop.io.IOUtils;
10 
11 public class ReadFile {
12 
13     public static void main(String[] args) throws IOException {
14         String uri = "hdfs://centos1:9000/test/README.txt";
15         Configuration conf = new Configuration();
16         
17         FileSystem fs = null;
18         InputStream in = null;
19         try {
20             fs = FileSystem.get(conf);
21             in = fs.open(new Path(uri));
22             IOUtils.copyBytes(in, System.out, 4096, false);
23         } finally {
24             IOUtils.closeStream(in);
25             if (fs != null) {
26                 fs.close();
27             }
28         }
29     }
30 
31 }

4. 写入文件

 1 package hdfs;
 2 
 3 import java.io.BufferedInputStream;
 4 import java.io.FileInputStream;
 5 import java.io.IOException;
 6 import java.io.InputStream;
 7 import java.io.OutputStream;
 8 
 9 import org.apache.hadoop.conf.Configuration;
10 import org.apache.hadoop.fs.FileSystem;
11 import org.apache.hadoop.fs.Path;
12 import org.apache.hadoop.io.IOUtils;
13 
14 public class WriteFile {
15 
16     public static void main(String[] args) throws IOException {
17         String source = "/opt/app/hadoop-2.6.5/NOTICE.txt";
18         String destination = "hdfs://centos1:9000/test/NOTICE.txt";
19         Configuration conf = new Configuration();
20         
21         InputStream in = null;
22         FileSystem fs = null;
23         OutputStream out = null;
24         try {
25             in = new BufferedInputStream(new FileInputStream(source));
26             fs = FileSystem.get(conf);
27             out = fs.create(new Path(destination));
28             IOUtils.copyBytes(in, out, 4096, true);
29         } finally {
30             IOUtils.closeStream(out);
31             if (fs != null) {
32                 fs.close();
33             }
34             IOUtils.closeStream(in);
35         }
36     }
37 
38 }

5. 创建目录

 1 package hdfs;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.conf.Configuration;
 6 import org.apache.hadoop.fs.FileSystem;
 7 import org.apache.hadoop.fs.Path;
 8 
 9 public class CreateDirectory {
10 
11     public static void main(String[] args) throws IOException {
12         String uri = "hdfs://centos1:9000/test/testdir";
13         Configuration conf = new Configuration();
14         
15         FileSystem fs = null;
16         try {
17             fs = FileSystem.get(conf);
18             Path dir = new Path(uri);
19             fs.mkdirs(dir);
20         } finally {
21             if (fs != null) {
22                 fs.close();
23             }
24         }
25     }
26 
27 }

6. 删除目录

 1 package hdfs;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.conf.Configuration;
 6 import org.apache.hadoop.fs.FileSystem;
 7 import org.apache.hadoop.fs.Path;
 8 
 9 public class RemoveDirectory {
10 
11     public static void main(String[] args) throws IOException {
12         String uri = "hdfs://centos1:9000/test/testdir";
13         Configuration conf = new Configuration();
14         
15         FileSystem fs = null;
16         try {
17             fs = FileSystem.get(conf);
18             Path dir = new Path(uri);
19             boolean deleted = fs.delete(dir, true);    // 递归删除,也可删除文件
20             System.out.println(deleted);
21         } finally {
22             if (fs != null) {
23                 fs.close();
24             }
25         }
26     }
27 
28 }

7. 检查文件存在

 1 package hdfs;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.conf.Configuration;
 6 import org.apache.hadoop.fs.FileSystem;
 7 import org.apache.hadoop.fs.Path;
 8 
 9 public class CheckFileExistence {
10 
11     public static void main(String[] args) throws IOException {
12         String uri = "hdfs://centos1:9000/test/NOTICE.txt";
13         Configuration conf = new Configuration();
14         
15         FileSystem fs = null;
16         try {
17             fs = FileSystem.get(conf);
18             Path file = new Path(uri);
19             boolean exists = fs.exists(file);
20             System.out.println(exists);
21         } finally {
22             if (fs != null) {
23                 fs.close();
24             }
25         }
26     }
27 
28 }

8. 列出子目录和文件

 1 package hdfs;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.hadoop.conf.Configuration;
 6 import org.apache.hadoop.fs.FileStatus;
 7 import org.apache.hadoop.fs.FileSystem;
 8 import org.apache.hadoop.fs.Path;
 9 
10 public class ListFiles {
11 
12     public static void main(String[] args) throws IOException {
13         String uri = "hdfs://centos1:9000/test";
14         Configuration conf = new Configuration();
15         
16         FileSystem fs = null;
17         try {
18             fs = FileSystem.get(conf);
19             Path dir = new Path(uri);
20             FileStatus[] files = fs.listStatus(dir);
21             for (FileStatus file : files) {
22                 System.out.println(file.getPath());
23             }
24         } finally {
25             if (fs != null) {
26                 fs.close();
27             }
28         }
29     }
30 
31 }

9. 获取块位置信息

 1 package hdfs;
 2 
 3 import java.io.IOException;
 4 
 5 import org.apache.commons.lang.StringUtils;
 6 import org.apache.hadoop.conf.Configuration;
 7 import org.apache.hadoop.fs.BlockLocation;
 8 import org.apache.hadoop.fs.FileStatus;
 9 import org.apache.hadoop.fs.FileSystem;
10 import org.apache.hadoop.fs.Path;
11 
12 public class ObtainFileInfo {
13 
14     public static void main(String[] args) throws IOException {
15         String uri = "hdfs://centos1:9000/test/NOTICE.txt";
16         Configuration conf = new Configuration();
17         
18         FileSystem fs = null;
19         try {
20             fs = FileSystem.get(conf);
21             Path file = new Path(uri);
22             FileStatus fileStatus = fs.getFileStatus(file);
23             BlockLocation[] blockLocations = fs.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());
24             for (int index = 0, length = blockLocations.length; index < length; index++) {
25                 System.out.println("block" + index + " " + StringUtils.join(blockLocations[index].getHosts(), ","));
26             }
27         } finally {
28             if (fs != null) {
29                 fs.close();
30             }
31         }
32     }
33 
34 }

作者:netoxi
出处:http://www.cnblogs.com/netoxi
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。

原文地址:https://www.cnblogs.com/netoxi/p/7223085.html