HDFS
HDFS(Hadoop Distributed File System)Hadoop分布式文件系统
一、HDFS特性
- 文件系统,存储文件,通过统一的命名空间目录树来定位文件;
- 分布式,由很多服务器联合起来实现其功能。
- master/slave架构
一般一个 HDFS 集群是有一个 Namenode 和一定数目的Datanode 组成。Namenode 是 HDFS 集群主节点,Datanode 是 HDFS 集群从节点,两种角色各司其职,共同协调完成分布式的文件存储服务。
- 分块存储
HDFS 中的文件在物理上是分块存储(block)的,块的大小可以通过配置参数来规定,默认大小在 hadoop2.x 版本中是 128M。
- 名字空间
用户或者应用程序可以创建目录,然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数现有的文件系统类似:用户可以创建、删除、移动或重命名文件。
- Namenode元数据管理
Namenode 负责维护整个hdfs文件系统的目录树结构,以及每一个文件所对应的 block 块信息(block 的id,及所在的datanode 服务器)。
- Datanode数据存储
文件的各个 block 的具体存储管理由 datanode 节点承担。每一个 block 都可以在多个datanode 上。Datanode 需要定时向 Namenode 汇报自己持有的 block信息。 存储多个副本(副本数量也可以通过参数设置 dfs.replication,默认是 3)
- 副本机制
为了容错,文件的所有 block 都会有副本。每个文件的 block 大小和副本系数都是可配置的。应用程序可以指定某个文件的副本数目。副本系数可以在文件创建的时候指定,也可以在之后改变。
- 一次写入,多次读出
频繁的写入会造成元数据的频繁的更新改变,比较麻烦。
正因为如此,HDFS 适合用来做大数据分析的底层存储服务,并不适合用来做.网盘等应用,因为,修改不方便,延迟大,网络开销大,成本太高。
二、HDFS命令
1、常用的操作命令
#查看根路径下面的文件或者文件夹
hdfs dfs -ls /
#在hdfs上面递归的创建文件夹
hdfs dfs -mkdir -p /xx/xxx
#从本地移动到hdfs
hdfs dfs -moveFromLocal sourceDir(本地磁盘的文件或者文件夹的路径) destDir(hdfs的路径)
#移动 (hdfs上的移动)
hdfs dfs -mv hdfsSourceDir hdfsDestDir
#将本地文件系统的文件或者文件夹放到hdfs上面去
hdfs dfs -put localDir hdfsDir
#查看hdfs的文件内容
hdfs dfs -cat hdfsDir
#拷贝文件或者文件夹
hdfs dfs -cp hdfsSourceDIr hdfsDestDi
#(递归)删除文件或者文件夹
hdfs dfs -rm [-r]
#hdfs的权限管理两个命令:
hdfs dfs -chmod -R 777 /xxx
hdfs dfs -chown -R hadoop:hadoop /xxx
2、高级命令
安全模式
hadoop的安全模式:hadoop集群刚启动的时候,默认处于安全模式30秒,安全模式的时候对外不提供给任何服务
刚开始启动的时候,处于安全模式:检测我们的datanode去了
基准测试
- 测试写入速度
hadoop jar /export/servers/hadoop-2.6.0-cdh5.14.0/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.6.0-cdh5.14.0.jar TestDFSIO -write -nrFiles 10 -fileSize 10MB
写入速度一般在30M/S左右超不多
- 测试读取速度
hadoop jar /export/servers/hadoop-2.6.0-cdh5.14.0/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-2.6.0-cdh5.14.0.jar TestDFSIO -read -nrFiles 10 -fileSize 10MB
一般通过MR读取速度大概在100M/s左右
三、一些概念
- 一台满了,需加多几台,数据究竟存在哪些机器上? 元数据记录(一条元数据信息大概150字节)
- 上传一个比任何一台服务器的磁盘都要大的文件?切块(小文件也需要切块,好存且要计算)
- 如何解决块丢失问题?副本机制
1、分布式文件计算系统
文件系统是一个抽象类,里面有很多的子实现类,例如 hdfs,file:///, ftp文件系统 , webHdfs
把磁盘的真实存放抽象成目录结构给用户看 这就是文件系统(FileSystem)干的
小文件占元数据信息和block,占线程占资源————>合并
2、HDFS的架构图
主从架构
主节点与从节点存在 心跳机制 负载均衡 副本机制
3、副本机制以及block块缓存
hdfs-site.xml
dfs.blocksize
实际中一般比128大
根据CPU 内存 性能设置
- block块缓存:可以将我们的block块缓存到内存当中,我们在执行一些MR计算的时候,可以直接从内存当中获取数据,比较快,特别适用于一些小表join大表的情况
- hdfs的权限验证:采用的是linux类似的权限校验机制,防止好人做错事,不能阻止坏人干干事,hdfs相信你告诉我你是谁,你就是谁
4、hdfs元数据信息fsimage与edits
在hdfs-site.xml当中的配置
dfs.namenode.name.dir:fsimage文件存储的路径
dfs.namenode.edits.dir:edits文件存储的路径
fsimage:(某时间前的元数据信息)namenode中元数据在内存,但要持久化保存在硬盘。————负责开机后加载加载元数据到内存
edits:对元数据操作的日志记录(一段时间)
SencondaryNamenode:复制fsimage + 剪切edits(把以前的edits清空),合并=fsimage.ctp,赶紧覆盖fsimage————复制合并日志
SecondaryNameNode如何辅助namenode管理元数据信息
主要的作用就是合并fsimage与edits
edist出发合并的两个条件,如:
- 条件一 edits文件达到64M
- 条件二 一个小时合并一次
5、hdfs的文件读写过程
hdfs的文件写入过程:
- 第一步:客户端发出请求,请求namneode需要上传数据
- 第二步:namenode检测客户端是或否有权限上传
- 第三步:客户端请求namenode第一个block块上传到哪里去
- 第四步:namenode找n个block块返回给客户端
- 第五步:客户端找datanode建立pipeline管道,主备上传数据,数据都是以packet包的形式通过管道上传到datanode上面去
- 第六步:datanode保存好了之后,给客户端一个ack确认机制,客户端准备上传下一个block块,直到所有的block块上传完成,关闭文件流。
一个packet 64k
读取过程:
- Client向NameNode发出读取请求
- NameNode检测客户端是否有权限读取以及需要读取的文件是否存在(NameNode告诉客户端允许读取数据,NameNode查找元数据信息,告诉客户端block块存储在哪里)
- 客户端获取到block块地址之后,与datanade通信,通过pipeline管道进行数据的传输
- 客户端读取完所有的block块,然后再客户端本地拼接成一个完整的文件
四、hdfs的JavaAPI
1、使用文件系统方式访问数据
public void getFileSystem() throws Exception {
//如果configuration 不做任何配置,获取到的是本地文件系统
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS", "hdfs://node01:8020/");
FileSystem fileSystem = FileSystem.get(configuration);
System.out.println(fileSystem.toString());
}
2、递归遍历文件系统当中的所有文件
/**
* 递归遍历hdfs当中所有的文件路径
*/
@Test
public void getAllHdfsFilePath() throws Exception {
//获取分布式文件系统的客户端
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
//给定我们hdfs的根路径
Path path = new Path("/");
getDirectoryFile(fileSystem,path);
fileSystem.close();
}
/**
* 遍历文件路径
*/
private void getDirectoryFile(FileSystem fileSystem, Path path) throws Exception {
//获取path路径下所有的文件详情列表
FileStatus[] fileStatuses = fileSystem.listStatus(path);
//遍历详情列表
for (FileStatus fileStatus : fileStatuses) {
//如果是文件夹,则递归调用自己
if (fileStatus.isDirectory()) {
getDirectoryFile(fileSystem, fileStatus.getPath());
} else {
//如果是文件,则直接输出文件的绝对路径
System.out.println(fileStatus.getPath());
}
}
}
3、下载文件到本地
/**
* 下载hdfs文件到本地
*/
@Test
public void copyHdfsToLocal()throws Exception{
//获取分布式文件系统的客户端
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
//给定hdfs文件的路径
Path path = new Path("/test/input/install.log");
//使用一个输入流去读取hdfs的文件
FSDataInputStream inputStream = fileSystem.open(path);
//输出流,将我们的数据输出到本地路径下面去
FileOutputStream outputStream = new FileOutputStream(new File("c:\\myinstall.log"));
IOUtils.copy(inputStream,outputStream);
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
//通过copyToLocalFile来将hdfs的文件下载到本地
// fileSystem.copyToLocalFile(new Path("hdfs://node01:8020/test/input/install.log"),new Path("file:///c:\\myinstall2.log"));
fileSystem.close();
}
4、hdfs上创建文件夹
/**
* hdfs上面创建文件夹
*/
@Test
public void createHdfsDir() throws Exception{
//获取分布式文件系统的客户端对象
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
fileSystem.mkdirs(new Path("/abc/bbc/ddd"));
fileSystem.close();
}
5、hdfs文件上传
/**
* hdfs的文件上传
*/
@Test
public void uploadFileToHdfs() throws Exception {
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration());
fileSystem.copyFromLocalFile(false, new Path("file:///d:\\lala.txt"), new Path("/abc/bbb/ccc"));
fileSystem.close();
}
6、权限问题以及伪造用户
/**
* hafs的权限校验机制
*/
@Test
public void hdfsPermission() throws Exception {
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(), "root");
fileSystem.copyToLocalFile(new Path("hdfs://node01:8020/test/input/install.log"), new Path("file:///d:\\core-site.xml"));
fileSystem.close();
}
7、小文件合并
/**
* hdfs在上传小文件的时候进行合并
*/
@Test
public void mergeFile()throws Exception{
//在hdf上面创建一个文件
//获取本地文件,上传到hdfs创建的文件里面去
FileSystem fileSystem = FileSystem.get(new URI("hdfs://node01:8020"), new Configuration(), "root");
FSDataOutputStream fsDataOutputStream = fileSystem.create(new Path("/bigfile.xml"));
//获取本地所有小文件的输入流
//首先获取本地文件系统
LocalFileSystem localFileSystem = FileSystem.getLocal(new Configuration());
FileStatus[] fileStatuses = localFileSystem.listStatus(new Path("file:///F:\\上传小文件合并"));
for (FileStatus fileStatus : fileStatuses) {
Path path = fileStatus.getPath();
FSDataInputStream inputStream = localFileSystem.open(path);
IOUtils.copy(inputStream,fsDataOutputStream);
//通过拷贝,将我们的本地文件上传到hdfs上面去
IOUtils.closeQuietly(inputStream);
}
//关闭输出流
IOUtils.closeQuietly(fsDataOutputStream);
//关闭客户端
fileSystem.close();
localFileSystem.close();
}
-
方案一:从源头解决
-
方案二:sequenceFile
-
方案三:har归档文件
从hdfs下载文件到本地(类似网盘下载)
public class DownFile {
@Test
public void downAllHdfsFile() throws URISyntaxException, IOException {
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://node01:8020"), new Configuration());
Path path = new Path("/abc");
File file = new File("F:\\hdfs\\" + path.getName());
if (!file.exists()) {
file.mkdirs();
}
downHdfsDirectory(file, fileSystem, path);
fileSystem.close();
}
private void downHdfsDirectory(File file, FileSystem fileSystem, Path path) throws IOException {
//获取path路径下所有的文件详情列表
FileStatus[] fileStatuses = fileSystem.listStatus(path);
//遍历详情列表
for (FileStatus fileStatus : fileStatuses) {
//如果是文件夹,则递归调用自己
if (fileStatus.isDirectory()) {
File file1 = new File(file, fileStatus.getPath().getName());
file1.mkdirs();
downHdfsDirectory(file1, fileSystem, fileStatus.getPath());
} else {
Path path1 = fileStatus.getPath();
FSDataInputStream inputStream = fileSystem.open(path1);
FileOutputStream outputStream = new FileOutputStream(new File(file, path1.getName()));
IOUtils.copy(inputStream, outputStream);
IOUtils.closeQuietly(outputStream);
IOUtils.closeQuietly(inputStream);
}
}
}
}