目录
· 概况
· 原理
· HDFS 架构
· 块
· NameNode
· DataNode
· 数据读写
· 容错机制
· 数据完整性
· 操作
· 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的文件系统,解决S3的5GB文件大小限制。 |
2. HDFS特点
a) 适合存储超大文件:存储在HDFS的文件大多在GB、TB级别,甚至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. 文件系统块:块大小是磁盘块大小的整数倍,如ext3为4KB,NTFS为4KB。
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) 分布:第1块64MB,第2块64MB,第3块22MB。
5. 块布局策略
a) 第1个副本:如果HDFS客户端在集群内,默认布局在客户端所在节点;否则随机选择一个节点,但会尽量避免存储太满或太忙的节点。
b) 第2个副本:与第1个副本不同且随机另外机架中的节点。
c) 第3个副本:与第2个副本相同机架且随机选择另外一个节点。
d) 其他副本(副本数>3):集群随机选择节点,但会尽量避免在相同机架上布局太多副本。
e) 由NameNode选择节点。
f) 示意图:副本数为3。
NameNode
1. 职责:HDFS主从(Master/Slave)架构的主角色。
2. NameNode存储的文件
a) fsimage:HDFS元数据的完整快照,每次NameNode启动时,默认加载最新的fsimage。
b) edits:fsimage的编辑日志。
SecondaryNameNode
1. 职责:定期合并fsimage和edits的辅助守护进程。
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方式获取NameNode的fsimage与edits文件(在SecondaryNameNode的current同级目录下可见 temp.check-point或者previous-checkpoint目录,这些目录中存储着从NameNode拷贝来的镜像文件);
c) SecondaryNameNode开始合并获取的上述两个文件,产生一个新的fsimage文件fsimage.ckpt;
d) SecondaryNameNode用HTTP POST方式发送fsimage.ckpt至NameNode;
e) NameNode将fsimage.ckpt与edits.new文件分别重命名为fsimage与edits,然后更新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列表,以便写入下一个块的多个副本。客户端直接连接到列表中第1个DataNode,而该DataNode又连接到第2个DataNode,第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. fsimage与edits:如果NameNode单点部署,fsimage、edits文件损坏时,可手工从SecondaryNameNode定期备份手工恢复。
5. 文件删除:删除并未从NameNode移除,而是存放在/trash目录可随时恢复,直到超过hdfs-site.xml的参数“fs.trash.interval”的值(秒)。
数据完整性
1. 写入时校验:客户端将数据及其校验和发送到一组DataNode组成复制管道,管道最后一个DataNode负责验证校验和,如果错误,客户端便收到ChecksumException。
2. 读取时校验:客户端对比读取数据与DataNode存储的校验和,验证成功后告知DataNode,DataNode记录到验证校验和日志(包括每个块的最后验证时间)。
3. 后台定期校验:每个DataNode后台有一个DataBlockScanner线程,定期验证Data上所有数据块。
4. 块修复:NameNode将块标记为已损坏,之后安排该块的一个副本复制到另一DataNode以达到预设副本数,最后删除已损坏的块。
NameNode HA
1. 场景
a) NameNode的热备(SecondaryNameNode是NameNode的冷备)。
b) 生产环境必备功能。
2. NameNode HA架构
a) 一个NameNode处于主状态(Active),处理客户端和DataNode请求,并把edits写入本地和共享编辑日志(NFS或QJM等)。
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
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。