大数据生态圈学习--HDFS分布式文件系统
HDFS介绍
HDFS 是 Hadoop Distribute File System 的简称,意为:Hadoop 分布式文件系统。是 Hadoop 核心组件之一,作为最底层的分布式存储服务而存在。
可以存储海量数据。
hdfs的特性:
master/slave架构:主从架构
namenode:主节点,主要用于存储元数据,处理用户的请求
datanode:从节点,主要用于存储数据,说白了就是出磁盘的
分块存储:把一个大的文件,化成一个个的小的block块,在2.x当中一个block默认是128M大小
统一的命名空间:对外提供统一的文件访问地址:hdfs://node01:8020
datanode数据存储:出磁盘的,用于存储文件数据
副本机制:比如1280M的文件需要拆成10个block块,每个blcok有三个副本
一次写入,多次读取:hdfs文件系统,适用于频繁的读取,不适合频繁的写入 改变文件涉及元数据的改变
HDFS的命令行使用
1.ls查看文件
hdfs dfs -ls /user/hadoop/file1
2.lsr递归查看文件
Usage: hdfs dfs -lsr
相当于 ls -R
3.mkdir创建文件夹
hdfs dfs -mkdir /user/hadoop/dir1 /user/hadoop/dir2
hdfs dfs -mkdir hdfs://nn1.example.com/user/hadoop/dir hdfs://nn2.example.com/user/hadoop/dir
4.moveFromLocal上传
hdfs dfs -moveFromLocal 1.txt 指定的路径
移动本地磁盘的文件到hdfs目录中,移动后会删除源文件
5.moveTolocal下载
hdfs dfs -moveFromLocal 2.txt 1.txt
将hdfs上的文件下载到本地磁盘
好像这个命令还没实现不能用
6.mv hdfs中文件的移动
hdfs dfs -mv /user/hadoop/file1 /user/hadoop/file2
hdfs dfs -mv hdfs://nn.example.com/file1 hdfs://nn.example.com/file2 hdfs://nn.example.com/file3 hdfs://nn.example.com/dir1
7.put 从本地文件复制到hdfs上
hdfs dfs -put localfile /user/hadoop/hadoopfile
hdfs dfs -put localfile1 localfile2 /user/hadoop/hadoopdir
hdfs dfs -put localfile hdfs://nn.example.com/hadoop/hadoopfile
hdfs dfs -put - hdfs://nn.example.com/hadoop/hadoopfile Reads the input from stdin.
8.appendToFile 追加一个或多个本地文件到hdfs上
hdfs dfs -appendToFile localfile /user/hadoop/hadoopfile
hdfs dfs -appendToFile localfile1 localfile2 /user/hadoop/hadoopfile
hdfs dfs -appendToFile localfile hdfs://nn.example.com/hadoop/hadoopfile
hdfs dfs -appendToFile - hdfs://nn.example.com/hadoop/hadoopfile Reads the input from stdin.
9.cat 查看内容
hdfs dfs -cat hdfs://nn1.example.com/file1
hdfs://nn2.example.com/file2
hdfs dfs -cat file:///file3 /user/hadoop/file4
10.cp 复制文件(夹),可以覆盖,可以保留原有权限信息
hdfs dfs -cp /user/hadoop/file1 /user/hadoop/file2
hdfs dfs -cp /user/hadoop/file1 /user/hadoop/file2 /user/hadoop/dir
11.rm 删除文件或者文件夹
hdfs dfs -rm -r 递归删除
12.chmod 修改权限
hdfs dfs -chmod -R 777 /XXXX
13.chown 修改文件的用户所属组
hdfs dfs -chown -R hadoop:hadoop /xxx
14.expunge 清空回收站
15.HDFS文件限额配置
数量限额
hdfs dfs -mkdir -p /user/root/lisi #创建hdfs文件夹
hdfs dfsadmin -setQuota 2 lisi # 给该文件夹下面设置最多上传两个文件,上传文件,发现只能上传一个文件
hdfs dfsadmin -clrQuota /user/root/lisi # 清除文件数量限制
空间大小限额
hdfs dfsadmin -setSpaceQuota 4k /user/root/lisi # 限制空间大小4KB
hdfs dfs -put /export/softwares/zookeeper-3.4.5-cdh5.14.0.tar.gz /user/root/lisi
#上传超过4Kb的文件大小上去提示文件超过限额
hdfs dfsadmin -clrSpaceQuota /user/root/lisi #清除空间限额
查看hdfs文件限额数量
hdfs dfs -count -q -h /user/root/lisi
hadoop的基准测试
实际生产环境当中,hadoop的环境搭建完成之后,第一件事情就是进行压力测试,测试我们的集群的读取和写入速度,测试我们的网络带宽是否足够等一些基准测试,hadoop自带的测试文件
测试写入速度
向HDFS文件系统中写入数据,10个文件,每个文件10MB,文件存放到
/benchmarks/TestDFSIO中
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
查看写入结果
hdfs dfs -text /benchmarks/TestDFSIO/io_write/part-00000
测试读取速度
测试hdfs的读取文件性能
在HDFS文件系统中读入10个文件,每个文件10M
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
查看读取结果
hdfs dfs -text /benchmarks/TestDFSIO/io_read/part-00000
清除测试数据
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 -clean
更多基准测试参见:
https://blog.csdn.net/azhao_dn/article/details/6930909
HDFS架构
架构图:
- NameNode 记录着每一台机器存储文件的元数据信息,并提供抽象目录树给客户端访问。
- DataNode 负责文件内容的读写。
- 文件在DataNode中以文件块的方式存储,称之为Block;在hadoop2.x中默认是128M。为了解决Block丢失的问题,引入了副本机制;读取文件时NameNode尽量让用户先读取最近的副本。
- NameNode 与 DataNode 之间存在着心跳机制,DataNode 周期性往NameNode 发送心跳信号和状态报告,状态报告包含了该 DataNode 上存储的数据列表。
- NameNode总结:
- 存储元数据(内存/磁盘)
- 保存文件、block、DataNode之间的映射关系
- DataNode 总结:
- 存储文件内容(磁盘)。
- 维护blockid到 DataNode 本地文件的映射关系。
- 分布式文件系统: Hadoop提供了FileSystem接口给用户操作HDFS集群,该接口有很多实现类,如HDFS,Local,WebHDFS,HAR等。
NameNode元数据管理
- fsimage 存放的是一份最完整的元数据信息,内容比较大。其数据存储在dfs.namenode.name.dir(hdfs-site.xml)指定的文件路径中。
- 使用命令 hdfs oiv可以查看fsimage的数据,如:hdfs oiv -i fsimage_0000000000000000864 -p XML -o hello.xml
- edits 记录了最近一段时间的元数据信息操作日志,内容相对较小。其数据存储在dfs.namenode.edits.dir (hdfs-site.xml)指定的文件路径中。
- 查看命令 hdfs oev可以查看edits数据,如:hdfs oev -i edits_0000000000000000865-0000000000000000866 -p XML -o myedit.xml
- 为了控制edits日志不断膨胀太大,引入SecondaryNameNode的checkpoint机制。它的主要职责是合并fsimage与edits并清空旧的edits。
- SecondaryNameNode的checkpoint步骤:
- SNN通知NN切换edits日志。
- NN接收到通知后,所有的操作日志往一个新的edits文件写入。
- SNN获取fsimage与edits两个文件,把他们加载到内存中合并成一个新的fsimage。
- SNN将新的fsimage发送给NN并替换掉旧的。
- SNN默认触发checkpoint的条件有2种:1.edits文件达到64M 2.一个小时合并一次,也可以在hdfs-site.xml中修改fs.checkpoint.period,fs.checkpoint.size: edits的值。
HDFS文件的读写过程
HDFS写文件过程:
- client发起文件上传请求,NameNode检查元数据(目标文件是否已存在,父目录是否存在),返回是否可以上传。
- 如可以上传,client请求上传第一个block。
- NameNode根据配置文件中指定的备份数及机架感知原理进行文件分配,返回可用的DataNode的地址如:A,B,C。
- 如果副本数是三个,NN会找三台机器。
- 第一台:离客户端最近的一台机器。
- 第二台:最近一台机器的相同路由器下面的一台机器。
- 第三台:不同路由器下面找一台机器。
- client 与ABC机器建立pipeline。
- client开始以packet为单位(默认64K)往A上传第一个block,A收到packet后会传给B,B传C; A每传一个packet就会放入一个应答队列等待应答。
- 通过ack机制判断一个block是否上传成功,当一个block传输完成之后,client再次请求NameNode上传第二个block到服务器,直到所有block上传完毕。
HDFS读文件过程:
- client发起文件读取请求,NameNode检查元数据(有没读取权限,文件是否存在),返回是否可以读取。
- 如可以读取,NameNode会视情况返回文件的部分或者全部block列表(多线程)。
- 对于每个block,NameNode 都会返回含有该 block 副本的 DataNode 地址;
- 返回的DN根据网络拓扑图的远近进行排序,离client越近的DN地址越前。
- 心跳机制中超时汇报的 DN 状态为 STALE,这样的排靠后。
- client 选取排序靠前的 DataNode 来读取 block。
- 如果客户端本身就是DataNode, 那么将从本地直接获取数据(短路读取特性);
- 如果读取过程中断,客户端需要重新请求DN读取block。
- 所有的block 块传输完毕后,在客户端拼接成一个完整的文件。
HDFS java api操作
1.创建maven工程并导入jar包
由于cdh版本的所有的软件涉及版权的问题,所以并没有将所有的jar包托管到maven仓库当中去,而是托管在了CDH自己的服务器上面,所以我们默认去maven的仓库下载不到,需要自己手动的添加repository去CDH仓库进行下载,以下两个地址是官方文档说明,请仔细查阅
https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh5_maven_repo.html
https://www.cloudera.com/documentation/enterprise/release-notes/topics/cdh_vd_cdh5_maven_repo_514x.html
<repositories>
<repository>
<id>cloudera</id>
<!-- 制定我们去哪个网站下载jar包 -->
<url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.0-mr1-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-mapreduce-client-core</artifactId>
<version>2.6.0-cdh5.14.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 打包的插件 我们项目当中的其他jar包都打到一起去-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<!-- <verbal>true</verbal>-->
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<minimizeJar>true</minimizeJar>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
2.使用url的方式访问数据
@Test
public void demo1()throws Exception{
//第一步:注册hdfs 的url,让java代码能够识别hdfs的url形式
URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory());
InputStream inputStream = null;
FileOutputStream outputStream =null;
//定义文件访问的url地址
String url = "hdfs://192.168.52.100:8020/test/input/install.log";
//打开文件输入流
try {
inputStream = new URL(url).openStream();
outputStream = new FileOutputStream(new File("c:\\hello.txt"));
IOUtils.copy(inputStream, outputStream);
} catch (IOException e) {
e.printStackTrace();
}finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
3.使用文件系统方式访问数据
在 java 中操作 HDFS,主要涉及以下 Class:
Configuration:该类的对象封转了客户端或者服务器的配置; FileSystem:该类的对象是一个文件系统对象,可以用该对象的一些方法来对文件进行操作,通过 FileSystem 的静态方法 get 获得该对象。
FileSystem fs = FileSystem.get(conf)
get 方法从 conf 中的一个参数 fs.defaultFS 的配置值判断具体是什么类型的文件系统。如果我们的代码中没有指定 fs.defaultFS,并且工程 classpath下也没有给定相应的配置,conf中的默认值就来自于hadoop的jar包中的core-default.xml , 默 认 值 为 : file:/// , 则 获 取 的 将 不 是 一 个DistributedFileSystem 的实例,而是一个本地文件系统的客户端对象
4.获取FileSystem的几种方式
第一种方式获取FileSystem
@Test
public void getFileSystem() throws URISyntaxException, IOException {
Configuration configuration = new Configuration();
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), configuration);
System.out.println(fileSystem.toString());
}
第二种方式获取FileSystem
@Test
public void getFileSystem2() throws URISyntaxException, IOException {
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS","hdfs://192.168.52.100:8020");
FileSystem fileSystem = FileSystem.get(new URI("/"), configuration);
System.out.println(fileSystem.toString());
}
第三种方式获取FileSystem
@Test
public void getFileSystem3() throws URISyntaxException, IOException {
Configuration configuration = new Configuration();
FileSystem fileSystem = FileSystem.newInstance(new URI("hdfs://192.168.52.100:8020"), configuration);
System.out.println(fileSystem.toString());
}
第四种:
@Test
public void getFileSystem4() throws Exception{
Configuration configuration = new Configuration();
configuration.set("fs.defaultFS","hdfs://192.168.52.100:8020");
FileSystem fileSystem = FileSystem.newInstance(configuration);
System.out.println(fileSystem.toString());
}
5.递归遍历文件系统当中的所有文件
@Test
public void listFile() throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
FileStatus[] fileStatuses = fileSystem.listStatus(new Path("/"));
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isDirectory()){
Path path = fileStatus.getPath();
listAllFiles(fileSystem,path);
}else{
System.out.println("文件路径为"+fileStatus.getPath().toString());
}
}
}
public void listAllFiles(FileSystem fileSystem,Path path) throws Exception{
FileStatus[] fileStatuses = fileSystem.listStatus(path);
for (FileStatus fileStatus : fileStatuses) {
if(fileStatus.isDirectory()){
listAllFiles(fileSystem,fileStatus.getPath());
}else{
Path path1 = fileStatus.getPath();
System.out.println("文件路径为"+path1);
}
}
}
递归遍历官方提供的API版本
@Test
public void listMyFiles()throws Exception{
//获取fileSystem类
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
//获取RemoteIterator 得到所有的文件或者文件夹,第一个参数指定遍历的路径,第二个参数表示是否要递归遍历
RemoteIterator<LocatedFileStatus> locatedFileStatusRemoteIterator = fileSystem.listFiles(new Path("/"), true);
while (locatedFileStatusRemoteIterator.hasNext()){
LocatedFileStatus next = locatedFileStatusRemoteIterator.next();
System.out.println(next.getPath().toString());
}
fileSystem.close();
}
6.下载文件到本地
程序执行的main方法
/**
* 拷贝文件的到本地
* @throws Exception
*/
@Test
public void getFileToLocal()throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
FSDataInputStream open = fileSystem.open(new Path("/test/input/install.log"));
FileOutputStream fileOutputStream = new FileOutputStream(new File("c:\\install.log"));
IOUtils.copy(open,fileOutputStream );
IOUtils.closeQuietly(open);
IOUtils.closeQuietly(fileOutputStream);
fileSystem.close();
}
8.hdfs文件上传
@Test
public void putData() throws Exception{
FileSystem fileSystem = FileSystem.get(new URI("hdfs://192.168.52.100:8020"), new Configuration());
fileSystem.copyFromLocalFile(new Path("file:///c:\\install.log"),new Path("/hello/mydir/test"));
fileSystem.close();
}
9.HDFS的小文件合并
由于hadoop擅长存储大文件,因为大文件的元数据信息比较少,如果hadoop集群当中有大量的小文件,那么每个小文件都需要维护一份元数据信息,会大大的增加集群管理元数据的内存压力,所以在实际工作当中,如果有必要一定要将小文件合并成大文件进行一起处理
在我们的hdfs 的shell命令模式下,可以通过命令行将很多的hdfs文件合并成一个大文件下载到本地,命令如下
cd /export/servers
hdfs dfs -getmerge /config/*.xml ./hello.xml