HDFS


HDFS优点:

1.高容错性

1.1数据自动保存多个副本。它通过增加副本的形式,提高容错性。

1.2某一个副本丢失以后,它可以自动恢复。

2.适合海量数据处理

2.1数据规模:能够处理数据规模达到 GB、TB、甚至PB级别的数据。

2.2文件规模:能够处理百万规模以上的文件数量,数量相当之大。

3.流式数据访问

3.1一次写入,多次读取,不能修改,只能追加。

3.2它能保证数据的一致性。

4.可构建在廉价机器上,通过多副本机制,提高可靠性。

HDFS缺点:

1.不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。

2.无法高效的对大量小文件进行存储

2.1存储大量小文件的话,它会占用 NameNode大量的内存来存储文件、目录和块信 息。这样是不可取的,因为NameNode的内存总是有限的。

2.2小文件存储的寻道时间会超过读取时间,它违反了HDFS的设计目标。

3.并发写入、文件随机修改

3.1一个文件只能有一个写,不允许多个线程同时写。

3.2仅支持数据 append(追加),不支持文件的随机修改。

HDFS架构:

1.Client:客户端

1.1文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个一个的Block,然后进行存储。

1.2与NameNode交互,获取文件的位置信息。

1.3与DataNode交互,读取或者写入数据。

1.4Client提供一些命令来管理HDFS,比如启动或者关闭HDFS。

1.5Client可以通过一些命令来访问HDFS。

2.NameNode:master,HDFS的管理者

2.1管理HDFS的名称空间。

2.2管理数据块(Block)映射信息

2.3配置副本策略

2.4处理客户端读写请求。

3.DataNode:Slave。NameNode下达命令,DataNode执行实际的操作

3.1存储实际的数据块。

3.2执行数据块的读/写操作。

4.Secondary NameNode:并非NameNode的热备

当NameNode挂掉的时候,它并不能马上替换NameNode并提供服务。

4.1辅助NameNode,分担其工作量。

4.2定期合并Fsimage和Edits,并推送给NameNode。

4.3在紧急情况下,可辅助恢复NameNode。

HDFS块大小:

Hadoop1.x默认block大小64MB

Hadoop2.x 3.x默认block大小128MB

实际生产中一般256MB或512MB

块大小的设置依据:

经过大量测试得出的结论:磁盘寻址时间和文件读写时间为1:100时,HDFS性能最佳

block过小,文件数量较多,NN压力较大,文件查找时间增加

block过大,读取一个block的时间较长,单个MapTask处理较慢

总结:HDFS块的大小设值主要取决于磁盘传输速率。

HDFS命令操作:

基本语法:hdfs dfs 具体命令 / hadoop fs 具体命令

常用命令:

  • -help:输出这个命令参数

hdfs dfs -help rm
  • -ls: 显示目录信息
hdfs dfs -ls /sanguo
  • -mkdir:在hdfs上创建目录
hdfs dfs -mkdir /sanguo
  • -moveFromLocal从本地剪切粘贴到hdfs
1 vim shuguo.txt
2 输入shuguo
3 #将shuguo.txt文件剪切到hdfs的sanguo目录下
4 hdfs dfs -moveFromLocal ./shuguo.txt /sanguo
  • -appendToFile :追加一个文件到已经存在的文件末尾
1 vim liubei.txt
2 hdfs dfs -appendToFile liubei.txt /sanguo/shuguo.txt
  • -cat :显示文件内容
hdfs dfs -cat /sanguo/shuguo.txt
  • -tail:显示一个文件的末尾

  • -chgrp 、-chmod、-chown:linux文件系统中的用法一样,修改文件所属权限

  • -copyFromLocal:从本地文件系统中拷贝文件到hdfs路径去

1 vim weiguo.txt
2 hdfs dfs -copyFromLocal weiguo.txt /sanguo
  • -copyToLocal:从hdfs拷贝到本地
hdfs dfs -copyToLocal /sanguo/shuguo.txt ./
  • -cp :从hdfs的一个路径拷贝到hdfs的另一个路径
hdfs dfs -cp /sanguo/shuguo.txt /jinguo
  • -mv:在hdfs目录中移动文件
hdfs dfs -mv /sanguo/weiguo.txt /jinguo
  • -get:等同于copyToLocal,就是从hdfs下载文件到本地
hdfs dfs -get /sanguo/shuguo.txt ./shuguo2.txt
  • -getmerge :合并下载多个文件,比如hdfs的目录 /hj/下有多个文件:log.1, log.2,log.3,...

  • -put:等同于copyFromLocal

1 vim wuguo.txt
2 hdfs dfs -put ./wuguo.txt /sanguo
  • -rm:删除文件或文件夹

    -r:递归删除文件夹里的内容

  • -rmdir:删除空目录

  • -df :统计文件系统的可用空间信息

  • -du统计文件夹的大小信息

  • -setrep:设置hdfs中文件的副本数量

 1 -help:输出这个命令参数
 2 -ls: 显示目录信息
 3 # -mkdir:在hdfs上创建目录
 4 hdfs dfs -mkdir /dir1
 5 # -rmdir:删除空目录
 6 hdfs dfs -rmdir /dir1
 7 #-df :统计文件系统的可用空间信息
 8 # 查看当前HDFS集群的容量占用情况 h:heiuman
 9 hdfs dfs -df -h
10 #-copyFromLocal:从本地文件系统中拷贝文件到hdfs路径去
11 # -put:等同于copyFromLocal
12 # 将本地文件上传到HDFS集群
13 hdfs dfs -put /root/1.txt /
14 #-moveFromLocal从本地剪切粘贴到hdfs
15 # 上传并删除本地文件
16 # -copyToLocal:从hdfs拷贝到本地
17 # -get:等同于copyToLocal,就是从hdfs下载文件到本地
18 hdfs dfs -get /1.txt  /root
19 # -cp :从hdfs的一个路径拷贝到hdfs的另一个路径
20 # -mv:在hdfs目录中移动文件
21 # -cpmv 是集群路径之间的操作
22 
23 # -cat :显示文件内容
24 # -tail:显示一个文件的末尾
25 
26 # -appendToFile  :追加一个文件到已经存在的文件末尾
27 # 将多个本地文件合并到HDFS集群上的单个文件
28 hdfs dfs -appendToFile 1.txt 2.txt 3.txt /Harry.txt
29 # -getmerge  :合并下载多个文件,比如hdfs的目录 /hj/下有多个文件:log.1, log.2,log.3,...
30 # 将多个HDFS文件下载并合并到单个本地文件
31 hdfs dfs -getmerge /1.txt /2.txt /3.txt /root/res.txt
32 
33 linux文件系统中的用法一样,修改文件所属权限
34 -chgrp
35 -chmod
36 -chown
37 
38 # -rm:删除文件或文件夹
39 hdfs dfs -rm -r /文件或者文件夹
40 
41 # -du统计文件夹的大小信息
42 # 查看文件夹占用磁盘空间的大小
43 hdfs dfs -du -s -h /文件夹
44 
45 # -setrep:设置hdfs中文件的副本数量
46 # HDFS默认将文件保存3个副本
47 # 可以通过setrep根据文件的安全性要求单独改变文件的副本个数
48 hdfs dfs -setrep n /文件

-ls: 显示目录信息

 hdfs dfs -ls /

-mkdir:在hdfs上创建目录

 hdfs dfs -mkdir /dir1

-appendToFile :追加一个文件到已经存在的文件末尾

 hdfs dfs -appendToFile b.txt /a.txt

-copyFromLocal:从本地文件系统中拷贝文件到hdfs路径去

-put:等同于copyFromLocal

 1 # 查看当前HDFS集群的容量占用情况
 2 hdfs dfs -df -h
 3 cd ~
 4 ll
 5 tauch a.txt
 6 touch a.txt
 7 ll
 8 #向a.txt文件添加内容
 9 echo '123' >> a.txt
10 echo '456' >> a.txt
11 #查看a.txt内容
12 cat a.txt
13 hdfs dfs -put a.txt /

-copyToLocal:从hdfs拷贝到本地

1 hdfs dfs -copyToLocal /a.txt ./c.txt
2 ls
3 cat c.txt

-tail:显示一个文件的末尾

 hdfs dfs -tail /a.txt

-cp :从hdfs的一个路径拷贝到hdfs的另一个路径

 hdfs dfs -cp /a.txt /dir1

-df :统计文件系统的可用空间信息

1 # 查看当前HDFS集群的容量占用情况 h:heiuman
2 hdfs dfs -df -h

-du统计文件夹的大小信息

1 # 查看文件夹占用磁盘空间的大小
2 hdfs dfs -du -s -h /

将存储文件合并成一个文件夹

 1 #将hadoop压缩包从本地拷贝到hdfs根路径下
 2 hdfs dfs -put /opt/java/tar/hadoop-2.7.7.tar.gz /
 3 #进入hadoop中data目录下,逐层进入
 4 cd /opt/java/modul/hadoop-2.7.7/data
 5 ll
 6 cd dfs
 7 ll
 8 cd data
 9 ll
10 cd current/
11 ll
12 cd BP-1277261197-192.168.222.81-1639389816732/
13 ll
14 cd current/
15 ll
16 cd finalized/
17 ll
18 cd subdir0/
19 ll
20 cd subdir0/
21 ll
22 ll-h
23 cd subdir0/
24 ll -h
25 #将两个文件合并成一个文件放到1.tar文件中
26 hdfs dfs -appendToFile blk_1073741829 blk_1073741830 /1.tar.gz
27 cd /
28 #将1.tar文件拷贝到本地目录下
29 hdfs dfs -get /1.tar.gz ./
30 ll
31 #解压
32 tar zxvf /1.tar.gz
33 ll
34 #移除
35 rm -rf 1.tar.gz
36 rm -rf hadoop-2.7.7/
37 ll
38 ls

下载到本地

HDFS JavaAPI操作

  • maven依赖hadoop-client
 1 //最外层父类pom文件
 2 <dependencyManagement>
 3     <dependencies>
 4         <dependency>
 5             <groupId>org.apache.hadoopgroupId>
 6             <artifactId>hadoop-clientartifactId>
 7             <version>2.7.7version>
 8         dependency>
 9     dependencies>
10 dependencyManagement>
1 最内层子类pom文件
2 <dependencies>
3     <dependency>
4         <groupId>org.apache.hadoopgroupId>
5         <artifactId>hadoop-clientartifactId>
6     dependency>
7 dependencies>
  • 关键类FileSystem
 1 public class HdfsDemo1 {
 2     public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
 3         //创建资源路径
 4 URI uri = new URI("hdfs://bd0801:8020");
 5         //创建配置文件
 6 Configuration c = new Configuration();
 7         //登录用户
 8         String user ="root";
 9         //获得文件系统对象
10 FileSystem fs = FileSystem.get(uri,c,user);
11         FileStatus[] fileStatuses = fs.listStatus(new Path("/"));
12 //-rw-r--r--   3 root supergroup    12 2021-12-14 10:57 /a.txt
13 for (FileStatus ss:fileStatuses) {
14 System.out.println(ss.getPermission().toString());
15  System.out.println(ss.getPath().getName());
16         }
17     }
18 }

log4j.properties文件配置

1 ### 设置###
2 log4j.rootLogger = error,stdout
3 
4 ### 输出信息到控制抬 ###
5 log4j.appender.stdout = org.apache.log4j.ConsoleAppender
6 log4j.appender.stdout.Target = System.out
7 log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
8 log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n
  • 配置Hadoop的windows开发环境(非必要)

日志打印报错下载插件

  1. winutils.exe 先创建一个目录C:\hadoop-2.7.7 在当前目录中再创建一个C:\hadoop-2.7.7\bin目录 拷贝文件winutils.exeC:\hadoop-2.7.7\bin目录中 添加windows环境变量 HADOOP_HOME hadoop-2.7.7目录

  2. hadoop.dll hadoop.dll拷贝C:\Windows\System32

API操作hdfs:

  • 实现文件上传、下载、删除、创建、更改名称、类型判断、查看详情

 1 public class HdfsDemo {
 2     public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
 3         //获得资源路径
 4 URI uri=new URI("hdfs://bd0801:8020");
 5         //创建配置文件
 6  Configuration configuration=new Configuration();
 7         //创建用户
 8         String user="root";
 9         //获得文件系统对象
10         FileSystem fs=FileSystem.get(uri,configuration,user);
11         //获得文件系统
12        // ls(fs);
13         //上传文件
14         Path src=new Path("C:\\Users\\Administrator\\Desktop\\mysql-connector-java-8.0.26.jar");
15         Path dst=new Path("/");
16  fs.copyFromLocalFile(false,true,src,dst);
17         //下载文件
18         Path src1=new Path("C:\\Users\\Administrator\\Desktop");
19         Path dst1=new Path("/a.txt");
20 fs.copyToLocalFile(true,dst1,src1,false);
21         //创建目录
22 fs.mkdirs(new Path("/dir1/dir2/dir3"));
23         //删除文件
24         fs.delete(new Path("/mysql-connector-java-8.0.26.jar"),true);
25         //文件名更改
26 fs.rename(new Path("/ss.txt"),new Path("/bb.txt"));
27         //查看文件详情和文件系统获取方法相同
28         //类型判断
29         boolean directory = fs.isDirectory(new Path("/a.txt"));
30         System.out.println(directory);
31     }
32         //获得文件详情
33     private static void ls(FileSystem fs) throws IOException {
34         FileStatus[] fileStatuses = fs.listStatus(new Path("/"));
35 //-rw-r--r--   3 root supergroup 12 2021-12-14 10:57 /a.txt
36         //获得文件最大长度
37         long max=0;
38         for (FileStatus ss:fileStatuses) {
39             if (ss.getLen()>max){
40                 max=ss.getLen();
41             }
42         }
43         System.out.println(max+"@@@@@@");
44         // Long o = Arrays.stream(fileStatuses).map(FileStatus::getLen).max(Long::compareTo).get();
45         // System.out.println(o+"@@");
46         for (FileStatus f:fileStatuses) {
47             //文件类型
48         String type=f.isFile()?"-":"d";
49             //权限
50 String permission = f.getPermission().toString();
51             //副本数
52 short replication = f.getReplication();
53             //拥有者
54        String owner = f.getOwner();
55             //所属组
56        String group = f.getGroup();
57             //文件大小
58        long len = f.getLen();
59             //最后一次修改日期
60             long modificationTime = f.getModificationTime();
61             //路径
62             String path = f.getPath().toUri().toString();
63             //hdfs:// bd0801:8020/dir1/a.txt,截取/
64             int i = path.indexOf("/", 7);
65             path = path.substring(i);
66 StringBuffer sb=new StringBuffer();
67 sb.append(type).append(permission).append("\t")
68 .append(replication).append("\t")
69 .append(owner).append("\t")
70 .append(group).append("\t")
71 .append(TimeUtil.lenFormat((max+"").length(),len+""," ")).append("\t")
72 .append(TimeUtil.formatDate(modificationTime)).append("\t")
73 .append(path);
74  System.out.println(sb.toString());
75         }
76     }
77 }
  • 创建工具类进行日期和长度转化

 1 public class TimeUtil {
 2     public static void main(String[] args) {
 3        /* int max=8;
 4         String str="123";
 5         String step=" ";
 6         lenFormat(max, str, step);    System.out.println(sb.append(str).toString());*/
 7 
 8        /*long time=0;
 9         String date = formatDate(time);
10         System.out.println(date);*/
11     }
12 
13     //时间格式化 ctrl+shift+m
14 public static String formatDate(long time) {
15         SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm");
16         return sdf.format(time);
17     }
18 
19     //长度格式化
20     public static String lenFormat(int max, String str, String step) {
21 StringBuffer sb=new StringBuffer();
22         for (int i = 0; i ) {
23             sb.append(step);
24         }
25         return sb.append(str).toString();
26     }
27 }
  • 通过流进行文件的上传和下载

 1 public class HdfsDemo1 {
 2     public static void main(String[] args) throws URISyntaxException, IOException, InterruptedException {
 3         //创建资源路径
 4         URI uri=new URI("hdfs://bd0801:8020");
 5 Configuration c=new Configuration();
 6       //设置上传副本数
 7         c.set("dfs.replication","2");
 8         String user="root";
 9 FileSystem fs=FileSystem.get(uri,c,user);
10         upload(fs);//上传
11         download(fs);//下载
12     }
13 
14     private static void download(FileSystem fs) throws IOException {
15         //下载
16         // 本地文件《--输出流《-- hdfsJava客户端《--输入流《--到 hdfs
17         File src=new File("C:\\Users\\Administrator\\Desktop\\mysql-connector-java-8.0.26.jar");
18         Path dst=new Path("/mysql-connector-java-8.0.26.jar");
19         //获得输入流
20 FSDataInputStream fis = fs.open(dst);
21         //获得输出流
22         FileOutputStream fos=new FileOutputStream(src);
23         IOUtils.copyBytes(fis,fos,4096);
24         System.out.println("下载成功");
25         fis.close();
26         fos.close();
27     }
28 
29     private static void upload(FileSystem fs) throws IOException {
30         //上传
31         // 本地文件----》输入流--》 hdfsJava客户端--》输出到 hdfs
32         File src=new File("C:\\Users\\Administrator\\Desktop\\mysql-connector-java-8.0.26.jar");
33         Path dst=new Path("/mysql-connector-java-8.0.26.jar");
34         FileInputStream fis=new FileInputStream(src);
35         //输出流
36 FSDataOutputStream fos = fs.create(dst);
37         //流拷贝
38         IOUtils.copyBytes(fis,fos,4096);
39         System.out.println("上传成功");
40         //上传文件关闭
41         fos.close();
42         fis.close();
43     }
44 }

HDFS文件写入流程:

NN和SNN的工作机制:

namenode启动

1.第一次启动namenode格式化后,创建fsimage和edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。

2.客户端对元数据进行增删改的请求

3.namenode记录操作日志,更新滚动日志。

4.namenode在内存中对数据进行增删改

Secondary NameNode工作

1.Secondary NameNode询问namenode是否需要checkpoint。直接带回namenode是否检查结果。

2.Secondary NameNode请求执行checkpoint。

3.namenode滚动正在写的edits日志

4.将滚动前的编辑日志和镜像文件拷贝到Secondary NameNode

5.Secondary NameNode加载编辑日志和镜像文件到内存,并合并。

6.生成新的镜像文件fsimage.chkpoint

7.拷贝fsimage.chkpoint到namenode

8.namenode将fsimage.chkpoint重新命名成fsimage

镜像文件和编辑日志文件:

1.Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件idnode的序列化信息。

2.Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到edits文件中。

3.seen_txid文件保存的是一个数字,就是最后一个edits_的数字

4.每次Namenode启动的时候都会将fsimage文件读入内存,并从00001开始到seen_txid中记录的数字依次执行每个edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成Namenode启动的时候就将fsimage和edits文件进行了合并。