HSFS
概念
HDFS(Hadoop DIstributed File System)为分布式文件管理系统中的一种,通过目录树来定位文件,由很多服务器联合起来实现期功能
适用场景:适合一次写入,多次读出,且不支持文件修改。适合用来做数据分析,不适合用来做网盘应用。
优缺点
- 优点
- 高容错性
- 自动保存多个副本
- 某一个副本丢失后可以自动恢复
- 适合处理大数据
- 数据规模:能够处理的数据规模达GB、TB甚至PB
- 文件规模:能够处理百万规模以上的文件数量
- 可以构建在廉价机器上,通过多副本机制提高可靠性
- 高容错性
- 缺点
- 不适合低延时的数据访问
- 无法高效地对大量小文件进行存储
- 存储大量小文件会占用NameNode大量内存在存储文件目录和块信息
- 小文件的寻址时间大于读取时间,违反了HDFS的设计目标
- 不支持并发写入、文件随机修改
- 一个文件不允许多线程同时写
- 仅支持数据追加,不支持文件随机修改
组成架构
- NameNode:集群文件的管理者
- 管理HDFS的名称空间
- 配置副本策略
- 管理数据块映射信息
- 处理客户端读写请求
- DataNode:执行NameNode下达的指令
- 存储实际的数据块
- 执行数据块的读写操作
- Client
- 文件切分:文件上传HDFS的时候,Client将文件切成一个一个Block,然后进行上传
- 与NameNode交互,获取文件位置信息
- 与DataNode交互,读取或写入数据
- Client提供一些命令来管理HDFS,比如NameNode格式化
- Client通过一些命令来访问HDFS,比如对HDFS进行增删改查
- Secondary NameNode:并非NameNode的热备,当NameNode挂掉的时候,并不能马上替换NameNode并提供服务
- 辅助NameNode,分担工作量,比如定期合并Fsimage和Edits,并推送给NameNode
- 在紧急情况下,可辅助恢复NameNode
文件快大小(面试重点)
-
默认block大小为128M或256M
-
为什么block不能太小也不能太大?
(1)设置太小,会增加寻址时间
(2)设置太大,传输数据的时间会明显大于寻址时间,导致在处理这块数据的时候会非常慢
-
block大小主要取决于磁盘传输速度
Shell操作
基本语法
bin/hadoop fs 具体命令 或
bin/hdfs dfs 具体命令
#dfs是fs的实现类
开发重点
API操作
获取文件系统,并进行特殊配置
Configuration configuration = new Configuration();
configuration.set("dfs.replication", "2");//设置副本数
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "root");
文件上传
fs.copyFromLocalFile(new Path("e:/banzhang.txt"), new Path("/banzhang.txt"));
文件下载
fs.copyToLocalFile(false, new Path("/banzhang.txt"), new Path("e:/banhua.txt"), true);//第二个参数为是否递归
删除
fs.delete(new Path("/0508/"), true);
修改文件名
fs.rename(new Path("/banzhang.txt"), new Path("/banhua.txt"));
文件详情查看
RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/"), true);//第二个参数为是否递归
while (listFiles.hasNext()) {
LocatedFileStatus fileStatus = listFiles.next();
//查看文件名称、权限、长度信息
System.out.println(fileStatus.getPath().getName());//名称
System.out.println(fileStatus.getPermission());//权限
System.out.println(fileStatus.getLen());//长度
//获取块信息
BlockLocation[] blockLocations = fileStatus.getBlockLocations();
for(BlockLocation blockLocation : blockLocations) {
String[] hosts = blockLocation.getHosts();
for (String host : hosts) {
System.out.println(host);
}
}
}
判断是文件还是文件夹
FileStatus[] listStatus = fs.listStatus(new Path("/"));
for (FileStatus fileStatus : listStatus) {
if (fileStatus.isFile()) {
//文件
System.out.println("f:"+fileStatus.getPath().getName());
}else {
//文件夹
System.out.println("d:"+fileStatus.getPath().getName());
}
}
I/O流操作
文件上传
//创建输入流
FileInputStream fis = new FileInputStream(new File("d:/test2.txt"));
//获取输出流
FSDataOutputStream fos = fs.create(new Path("/测试2.txt"));
//流对拷
IOUtils.copyBytes(fis, fos, conf);
文件下载
//创建输入流
FSDataInputStream fis = fs.open(new Path("/测试2.txt"));
//获取输出流
FileOutputStream fos = new FileOutputStream(new File("d:/test4.txt"));
//流的对拷
IOUtils.copyBytes(fis, fos, configuration);
定位文件读取
public void readFileSeek1() throws IOException, InterruptedException, URISyntaxException{
//获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "root");
//获取输入流
FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.2.tar.gz"));
//创建输出流
FileOutputStream fos = new FileOutputStream(new File("d:/hadoop-2.7.2.tar.gz.part1"));
//流的拷贝(只拷贝128M)
byte[] buf = new byte[1024];
for(int i =0 ; i < 1024 * 128; i++){
fis.read(buf);
fos.write(buf);
}
// 5关闭资源
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
fs.close();
}
//定位文件读取 下载第二块
@Test
public void readFileSeek2() throws IOException, InterruptedException, URISyntaxException{
//获取文件系统
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(new URI("hdfs://hadoop102:9000"), configuration, "root");
//打开输入流
FSDataInputStream fis = fs.open(new Path("/hadoop-2.7.2.tar.gz"));
//设置指定读取的起点
fis.seek(1024*1024*128);
//创建输出流
FileOutputStream fos = new FileOutputStream(new File("d:/hadoop-2.7.2.tar.gz.part2"));
//流的对拷
IOUtils.copyBytes(fis, fos, configuration);
//关闭资源
IOUtils.closeStream(fis);
IOUtils.closeStream(fos);
}
HDFS数据流
写数据流
1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
2)NameNode返回是否可以上传。
3)客户端请求第一个 Block上传到哪几个DataNode服务器上。
4)NameNode返回3个DataNode节点,分别为dn1、dn2、dn3。
5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
6)dn1、dn2、dn3逐级应答客户端。
7)客户端开始往dn1上传第一个Block(先从磁盘读取数据放到一个本地内存缓存),以Packet为单位,dn1收到一个Packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
8)当一个Block传输完成之后,客户端再次请求NameNode上传第二个Block的服务器。(重复执行3-7步)。
网络拓扑-节点距离计算
节点距离:两个节点到达最近的共同祖先的距离总和
副本节点选择
第一个节点:在Client所处的节点上,如果在集群外随机选择一个
第二个节点:与第一个副本位于相同机架,随机节点
第三个节点:不同机架,随机节点
读数据流程
1)客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。