赞
踩
1)HDFS产生背景
随着数据量越来越大,在一个文件系统存不下所有的数据,那么就分配到更多的文件系统管理的磁盘中,但是不方便管理和维护,需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。
2)HDFS定义
HDFS(Hadoop Distributed File System),它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。HDFS的使用场景:适合一次写入,多次读出的场景。一个文件经过创建、写入和关闭之后就不需要改变。
HDFS 最初是作为 Apache Hadoop 项目的一部分开发的。它被设计用来有效地存储和管理大规模数据集,并具有高度的容错性和可伸缩性。以下是关于HDFS的一些关键特点和定义:
(1)分布式存储:HDFS将大文件划分为多个数据块,并在集群中的多台服务器上存储这些数据块的副本。这种方式可以实现数据的分布式存储和处理,提高了数据访问的速度和吞吐量。
(2)容错性:HDFS具有高度的容错性,通过存储数据块的冗余副本来应对服务器故障。如果某个节点出现故障,HDFS能够自动在其他节点上重新复制数据块,确保数据完整性和可靠性。
(3)扩展性:HDFS可以很容易地扩展以适应不断增长的数据量。通过添加新的节点来扩展存储容量和计算能力,HDFS可以无缝地处理大规模数据集,并在需要时灵活地增加服务器数量。
(4)数据局部性:HDFS利用数据局部性原则来提高性能,即将计算和数据尽可能地放置在相同的节点上(机架感知),减少数据传输的开销,提高数据处理效率。
(5)适用于大数据:HDFS是为存储和处理大数据而设计的,可以有效地处理海量数据,并提供高可靠性和高吞吐量的数据存储解决方案。
总的来说,HDFS是一种专为大规模数据处理而设计的分布式文件系统,具有容错性、可伸缩性和高性能的特点,适用于处理需要大数据存储和分析的场景。
HDFS(Hadoop Distributed File System)是 Apache Hadoop 生态系统中的一部分,用于存储大规模数据集并提供高可靠性、高性能的分布式存储解决方案。以下是 HDFS 的优缺点:
优点:
(1)容量扩展性(Scalability):HDFS 可以轻松扩展以处理大量数据。它支持横向扩展,可以在集群中添加更多的节点来扩大存储容量和处理能力。
(2)高容错性(Fault Tolerance):HDFS 提供了高度的容错性,通过数据复制和块的多副本存储在不同节点上,以应对硬件故障和节点失效。
(3)高性能(High Performance):HDFS 允许并行读/写数据,适用于大规模数据处理任务。数据在集群中分布存储和处理,实现了高速数据访问。
在分布式系统中,"并行"和"并发"是两个相关但不同的概念。在Hadoop Distributed File System(HDFS)中,它确实支持并行读写操作,这与支持并发读写的概念有所不同。 - 并行读写:并行读写涉及同时从多个不同的位置读取或写入数据。在HDFS中,多个数据节点可以同时处理读写请求,从而提高数据传输速度和系统性能。这种并行处理方式有助于加快大规模数据的读取和写入操作。 - 并发读写:并发读写指的是同时执行多个读写操作,可能针对同一个文件或数据块。在传统的文件系统中,往往需要额外的控制机制来确保并发读写的正确性,避免数据损坏或冲突。然而,HDFS设计时更侧重于大文件、批处理等工作负载,因此通常情况下并不支持对同一数据块的并发写操作。 在HDFS中,虽然支持多个客户端同时进行读写操作(并行读写),但对于同一数据块的写操作通常是排他的,即一次只能有一个客户端进行写入操作。这是因为HDFS采用了一种写一次、多次读取的设计模式(Write Once, Read Many),强调数据的高可靠性、持久性和一致性。 因此,HDFS在特定情况下支持并行读写,但不是指对同一数据块的并发写。对于同一数据块的写入会采用一些机制确保数据的一致性,例如副本机制和写入锁等。 |
(4)简易扩展性(Ease of Scalability):HDFS 的扩展性使得添加和移除节点相对容易,无需中断服务,对系统运行和维护具有较高的灵活性。
(5)数据本地性(Data Locality):HDFS 支持数据本地性,即处理数据的节点通常也存储着相应的数据块,减少了数据传输的开销,有助于提高性能。
(6)适合处理大数据:
- 数据规模:能够处理数据规模达到GB、TB甚至PB级别的数据量;
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大
(7)低成本:可以构建在廉价服务器上,通过多副本机制,提高可靠性。
缺点:
(1)小文件问题(Small Files Problem):HDFS 不太适合存储大量小文件,因为每个文件的元数据开销较大,可能导致资源浪费和性能下降。
(2)实时性(Real-time Performance):HDFS 设计的初衷是处理大规模批处理任务,对于需要低延迟和实时性的应用不是最佳选择,如毫秒级的存储数据是做不到的。
(3)复杂性(Complexity):配置和管理 HDFS 集群可能需要一定的专业知识和经验。对于小型部署来说可能会显得过于繁琐和复杂。
(4)不适合小规模数据(Not Suitable for Small Data):对于小规模数据集来说,引入 HDFS 的开销可能会超过其带来的好处,不适合在这种情况下使用。
(5)不支持并发写入、文件随机修改
- 一个文件只能有一个写,不允许多个线程同时写;
- 仅支持数据 append(追加写),不支持文件的随机修改,如果要实现随机修改,需要客户端代码实现,先将随机插入位置的文件后面的数据拷贝一份,加入新数据,然后再将数据追加回去。
总体而言,HDFS 是一种适用于大规模数据存储和分析的分布式文件系统,具有众多优点但也需注意缺点,并根据应用需求和实际场景做出合适的选择。
(1)NameNode(NN):元数据空间管理者
- 管理HDFS名称空间(NameSpace)
- 配置副本策略
- 管理数据块(Block)映射信息
- 处理客户端读写请求
(2)DataNode(DN):NameNode下达命令,DataNode执行实际的操作
- 数据存储:DataNode 负责在本地文件系统存储数据块,将数据块写入磁盘并处理读取请求。HDFS 将大文件分成多个数据块,并在不同的 DataNode 上复制这些数据块,以实现数据的高可靠性和容错性。
- 数据复制和块报告:DataNode 负责处理数据块的复制操作。它会周期性地向 NameNode 发送块报告(Block Report),告知 NameNode 所管理的数据块的信息,包括哪些数据块存储在自己所在的节点上。
- 数据块复制的执行和处理:当某个数据块的副本数低于预定义的复制因子时(通常是3),DataNode 负责向其他节点复制数据块,以维持数据的冗余性和可靠性。
- 数据块的定期检查与复制:DataNode 负责监视和维护节点上存储的数据块的完整性。它会定期检查数据块的状态,并确保数据块复制的数量符合 HDFS 的副本因子要求。
- 接受客户端的读写请求:DataNode 会处理客户端的读取和写入请求。当客户端需要读取数据时,DataNode 会返回对应的数据块;当客户端需要写入数据时,DataNode 会接收并存储数据块。
总的来说,DataNode 是 HDFS 中负责存储数据的节点,通过协同工作,DataNode、NameNode 和客户端共同构成了 HDFS 分布式存储系统,实现了数据在集群环境下的高可靠性、容错性和可扩展性。
(3)Client:客户端
- 文件切分,文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;
- 与NameNode交互,获取文件的位置信息;
- 与DataNode交互,读取或写入数据;
- Client提供命令管理HDFS,比如NameNode的格式化等;
- Client 通过命令访问HDFS,比如对HDFS增删查改操作。
(4)SecondaryNameNode:并非NameNode的热备。当NameNode挂掉的时候,它不会替换NameNode提供服务。
- 辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode;
- 在紧急情况下,可以辅助恢复NameNode。
(5)StandbyNameNode:在HDFS-HA配置中的一部分
- Standby NameNode 是 Hadoop 高可用性(High Availability)部署的一部分,旨在防止单点故障。它与主 NameNode 处于热备份状态,即时准备接管主 NameNode 的工作。
- 当主 NameNode 发生故障时,StandbyNameNode 可以快速接管服务,确保 HDFS 的正常运行,提高系统的可靠性。
- Standby NameNode 会与主 NameNode保持同步,实时复制元数据变更,以确保在切换时数据一致性。
- StandbyNameNode 在 HA 环境中会定期从主 NameNode 处复制编辑日志和镜像文件,合并这些日志并生成新的镜像文件,以保证在主 NameNode发生故障时可以快速地恢复元数据并启动为新的主 NameNode。
- StandbyNameNode 也会周期性地检查主 NameNode 的健康状态,确保主 NameNode的正常运行。如果StandbyNameNode 检测到主 NameNode出现故障,它会触发自动故障转移并接管为新的主 NameNode,保证 HDFS 的高可用性和持久性。
HDFS 中的文件在物理上是分块存储(Block),块的大小可以通过配置参数(dfs.blocksize)来规定,默认的大小在Hadoop2.x 和Hadoop3.x 版本中是128MB,Hadoop1.x 版本是64MB。
Q:为什么是128MB的块大小?
(1)集群中的block位置信息存储在NameNode中(内存中)
(2)假设寻址时间为10ms,即查找目标block的时间为10ms;
(3)寻址时间为传输时间的 1% 时,系统则为最佳状态(专家结论),因此传输时间=10ms/0.01=1s
(4)目前普通机械硬盘(HDD)的传输速率大致在100 MB/s到200 MB/s之间;
(5)因此 block=100MB/s * 1s = 100MB 默认设置为100MB可以减少寻址开销。
Q:为什么块的大小不能设置太小,也不能设置太大,我的集群应该根据什么来设置块大小?
(1)HDFS的块设置太小,会增加寻址时间,即程序找寻块的开始位置比较耗时,因为块设置太小,会被分成多个块,元数据信息会增加;
(2)如果块设置太大,从磁盘传输数据的时间会明显大于查找这个块开始位置的时间。程序处理这块数据时,会显得比较慢。
因此:HDFS块的大小设置主要取决于磁盘传输速率。(磁盘的传输速率用fio等工具测试一下)
hadoop fs -具体命令 或者 hdfs dfs -具体命令。
- [seven@hadoop111 logs]$ hadoop fs
- Usage: hadoop fs [generic options]
- [-appendToFile [-n] <localsrc> ... <dst>]
- [-cat [-ignoreCrc] <src> ...]
- [-checksum [-v] <src> ...]
- [-chgrp [-R] GROUP PATH...]
- [-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
- [-chown [-R] [OWNER][:[GROUP]] PATH...]
- [-concat <target path> <src path> <src path> ...]
- [-copyFromLocal [-f] [-p] [-l] [-d] [-t <thread count>] [-q <thread pool queue size>] <localsrc> ... <dst>]
- [-copyToLocal [-f] [-p] [-crc] [-ignoreCrc] [-t <thread count>] [-q <thread pool queue size>] <src> ... <localdst>]
- [-count [-q] [-h] [-v] [-t [<storage type>]] [-u] [-x] [-e] [-s] <path> ...]
- [-cp [-f] [-p | -p[topax]] [-d] [-t <thread count>] [-q <thread pool queue size>] <src> ... <dst>]
- [-createSnapshot <snapshotDir> [<snapshotName>]]
- [-deleteSnapshot <snapshotDir> <snapshotName>]
- [-df [-h] [<path> ...]]
- [-du [-s] [-h] [-v] [-x] <path> ...]
- [-expunge [-immediate] [-fs <path>]]
- [-find <path> ... <expression> ...]
- [-get [-f] [-p] [-crc] [-ignoreCrc] [-t <thread count>] [-q <thread pool queue size>] <src> ... <localdst>]
- [-getfacl [-R] <path>]
- [-getfattr [-R] {-n name | -d} [-e en] <path>]
- [-getmerge [-nl] [-skip-empty-file] <src> <localdst>]
- [-head <file>]
- [-help [cmd ...]]
- [-ls [-C] [-d] [-h] [-q] [-R] [-t] [-S] [-r] [-u] [-e] [<path> ...]]
- [-mkdir [-p] <path> ...]
- [-moveFromLocal [-f] [-p] [-l] [-d] <localsrc> ... <dst>]
- [-moveToLocal <src> <localdst>]
- [-mv <src> ... <dst>]
- [-put [-f] [-p] [-l] [-d] [-t <thread count>] [-q <thread pool queue size>] <localsrc> ... <dst>]
- [-renameSnapshot <snapshotDir> <oldName> <newName>]
- [-rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ...]
- [-rmdir [--ignore-fail-on-non-empty] <dir> ...]
- [-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
- [-setfattr {-n name [-v value] | -x name} <path>]
- [-setrep [-R] [-w] <rep> <path> ...]
- [-stat [format] <path> ...]
- [-tail [-f] [-s <sleep interval>] <file>]
- [-test -[defswrz] <path>]
- [-text [-ignoreCrc] <src> ...]
- [-touch [-a] [-m] [-t TIMESTAMP (yyyyMMdd:HHmmss) ] [-c] <path> ...]
- [-touchz <path> ...]
- [-truncate [-w] <length> <path> ...]
- [-usage [cmd ...]]
-
- Generic options supported are:
- -conf <configuration file> specify an application configuration file
- -D <property=value> define a value for a given property
- -fs <file:///|hdfs://namenode:port> specify default filesystem URL to use, overrides 'fs.defaultFS' property from configurations.
- -jt <local|resourcemanager:port> specify a ResourceManager
- -files <file1,...> specify a comma-separated list of files to be copied to the map reduce cluster
- -libjars <jar1,...> specify a comma-separated list of jar files to be included in the classpath
- -archives <archive1,...> specify a comma-separated list of archives to be unarchived on the compute machines
-
- The general command line syntax is:
- command [genericOptions] [commandOptions]
(1)启动Hadoop集群
(2)-help:输出这个命令的帮助文档
- [seven@hadoop111 logs]$ hadoop fs -help rm
- -rm [-f] [-r|-R] [-skipTrash] [-safely] <src> ... :
- Delete all files that match the specified file pattern. Equivalent to the Unix
- command "rm <src>"
-
- -f If the file does not exist, do not display a diagnostic message or
- modify the exit status to reflect an error.
- -[rR] Recursively deletes directories.
- -skipTrash option bypasses trash, if enabled, and immediately deletes <src>.
- -safely option requires safety confirmation, if enabled, requires
- confirmation before deleting large directory with more than
- <hadoop.shell.delete.limit.num.files> files. Delay is expected when
- walking over large directory recursively to count the number of
- files to be deleted before the confirmation.
- [seven@hadoop111 logs]$
(1)-moveFromLocal:从本地剪切粘贴到HDFS
(2)-copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
(3)-put:等同于copyFromLocal,更习惯用put
(4)-appendToFile:追加一个文件到已经存在的文件末尾
hadoop fs -appendToFile xx_2.txt /xx_1.txt
(1)-copyToLocal:从HDFS拷贝到本地
(2)-get:等同于copyToLocal,生产环境更习惯用get
(3)-moveToLocal <src> <localdst>:从HDFS剪切到本地文件系统
2.3.4 直接操作文件
(1)-rm -r:递归删除目录及目录里面内容
(2)-du:统计文件夹的大小信息
- []$ hadoop fs -du -s -h /jinguo
- 27 81 /jinguo
-
- []$ hadoop fs -du -h /jinguo
- 14 42 /jinguo/shuguo.txt
- 7 21 /jinguo/weiguo.txt
- 6 18 /jinguo/wuguo.tx
说明:-s 查看的信息:27表示文件大小;81表示 27*3 个副本;/jinguo表示查看的目录
(3)-setrep:设置HDFS中文件的副本数量
hadoop fs -setrep 10 /jinguo/shuguo.txt
说明:这里设置的副本数只是记录在NameNode的元数据中,是否真的会有这么多副本,还得看DataNode的数量。因为目前只有3台设备,最多也就3个副本,只有节点数的增加到10台时,副本数才能达到10。
(1)解压Hadoop安装包到本地文件系统
G:\Bigdata\bigdata_client\hadoop-3.3.6
(2)下载winutils
https://github.com/cdarlint/winutils
双击:winutils.exe
如果一闪而过,表示系统正常,如果出现缺少某个 dll 文件,则需要修复系统。
(3)配置HADOOP_HOME环境变量
(4)配置Path环境变量。
注意:如果环境变量不起作用,可以重启电脑试试。
(5)在IDEA中创建一个Maven工程,并导入相应的依赖坐标+日志
- <dependencies>
- <dependency>
- <groupId>org.apache.hadoop</groupId>
- <artifactId>hadoop-client</artifactId>
- <version>3.3.6</version>
- </dependency>
-
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>4.13.1</version>
- </dependency>
-
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- <version>1.7.30</version>
- </dependency>
- </dependencies>
在项目的src/main/resources目录下,新建一个文件,命名为“log4j.properties”,在文件中填入以下内容:
- log4j.rootLogger=INFO, stdout
- log4j.appender.stdout=org.apache.log4j.ConsoleAppender
- log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
- log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n
- log4j.appender.logfile=org.apache.log4j.FileAppender
- log4j.appender.logfile.File=target/hadoop.log
- log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
- log4j.appender.logfile.layout.ConversionPattern=%d %p [%c] - %m%n
(6)创建HdfsClient类,并完成连接测试
- import org.apache.hadoop.conf.Configuration;
- import org.apache.hadoop.fs.FileSystem;
- import org.apache.hadoop.fs.Path;
- import org.junit.Test;
-
- import java.io.IOException;
- import java.net.URI;
- import java.net.URISyntaxException;
-
- /**
- * ClassName: HdfsClient
- * Package: com.huaweicloud.hadoop_client
- *
- * @Author: Seven
- * @Create: 2024/5/11 23:52
- * @Version: 1.0
- * @Description: HDFS API 测试
- */
- public class HdfsClient {
- @Test
- public void testMkdirs() throws IOException, URISyntaxException, InterruptedException {
-
- // 1 获取文件系统
- Configuration configuration = new Configuration();
-
- FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), configuration);
- //FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), configuration,"seven");
-
- // 2 创建目录
- fs.mkdirs(new Path("/xiyou/huaguoshan/"));
-
- // 3 关闭资源
- fs.close();
- }
- }
注意:客户端去操作HDFS时,是有一个用户身份的。默认情况下,HDFS客户端API会从采用Windows默认用户访问HDFS,会报权限异常错误。所以在访问HDFS时,一定要配置用户(seven)。
参数优先级排序:(1)客户端代码中设置的值 ->(2)ClassPath下的用户自定义配置文件 ->(3)然后是服务器的自定义配置(xxx-site.xml) ->(4)服务器的默认配置(xxx-default.xml)
- @Test
- public void testCopyFromLocalFile() throws URISyntaxException, IOException, InterruptedException {
- // 1 获取文件系统
- Configuration conf = new Configuration();
- URI uri = new URI("hdfs://hadoop112:8020");
- FileSystem fs = FileSystem.get(uri, conf, "seven");
-
- // 2 上传文件
- Path file_abs_path = new Path("G:/Bigdata/1.hadoop/datas/xunwukong.txt");
- Path dest_path = new Path("/xiyou/huaguoshan");
- fs.copyFromLocalFile(file_abs_path, dest_path);
-
- // 3 关闭资源。
- fs.close();
- }
- @Test
- public void testCopyToLocalFile() throws IOException, URISyntaxException, InterruptedException {
- Configuration conf = new Configuration();
- FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), conf, "seven");
-
- // 执行下载:
- // boolean delSrc 是否将原文件删除
- // Path src 要下载的文件路径,到文件名
- // Path dst 指的是将要下载到的路径
- // boolean useRawLocalFileSystem 是否开启文件校验。
- fs.copyToLocalFile(false, new Path("/xiyou/huaguoshan/xunwukong.txt"),
- new Path("G:/Bigdata/1.hadoop/datas/xunwukong2.txt"), true);
-
- fs.close();
-
- }
- @Test
- public void testRename() throws URISyntaxException, IOException, InterruptedException {
- Configuration conf = new Configuration();
- FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), conf, "seven");
-
- fs.rename(new Path("/xiyou/huaguoshan/xunwukong.txt"),
- new Path("/xiyou/huaguoshan/meihouwang.txt"));
-
- fs.close();
- }
- @Test
- public void testDelete() throws URISyntaxException, IOException, InterruptedException {
- Configuration conf = new Configuration();
- FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), conf, "seven");
-
- fs.delete(new Path("/xiyou"), true);
-
- fs.close();
- }
- /**
- * 查看文件名称、权限、长度、块信息等
- */
- @Test
- public void testListFiles() throws URISyntaxException, IOException, InterruptedException {
- Configuration conf = new Configuration();
- FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), conf, "seven");
-
- RemoteIterator<LocatedFileStatus> listFiles = fs.listFiles(new Path("/xiyou"), true);
- SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
- while (listFiles.hasNext()){
- LocatedFileStatus fileStatus = listFiles.next();
-
- System.out.println("========== " + fileStatus.getPath() + " =========");
- System.out.println("Permission: " + fileStatus.getPermission());
- System.out.println("Owner: " + fileStatus.getOwner());
- System.out.println("Group: " + fileStatus.getGroup());
- long ts = fileStatus.getModificationTime();
- String formattedDate = sdf.format(ts);
- System.out.println("Mtime: " + formattedDate);
- System.out.println("Replications: " + fileStatus.getReplication());
- System.out.println("BlockSize: " + fileStatus.getBlockSize());
- System.out.println("Locations: " + Arrays.toString(fileStatus.getBlockLocations()));
- System.out.println("Name: " + fileStatus.getPath().getName());
- }
-
- }
========== hdfs://hadoop112:8020/xiyou/huaguoshan/meihouwang.txt =========
Permission: rw-r--r--
Owner: seven
Group: supergroup
Mtime: 2024-05-12 00:44:18
Replications: 3
BlockSize: 134217728
Locations: [0,54,hadoop112,hadoop111,hadoop113]
Name: meihouwang.txt
其中:Locations: [0,54,hadoop112,hadoop111,hadoop113]
0:表示数据块在文件中的起始偏移量
54:表示数据块的长度
hadoop112,hadoop111,hadoop113:代表存储数据块的节点,即这些数据节点存储着副本。
- @Test
- public void testStatus() throws IOException, URISyntaxException, InterruptedException {
- Configuration conf = new Configuration();
- FileSystem fs = FileSystem.get(new URI("hdfs://hadoop112:8020"), conf, "seven");
- FileStatus[] fileStatuses = fs.listStatus(new Path("/xiyou"));
- for(FileStatus fileStatus: fileStatuses){
- if (fileStatus.isFile()){
- System.out.println("f: " + fileStatus.getPath().getName());
- }else {
- System.out.println("d: " + fileStatus.getPath().getName());
- }
- }
-
- fs.close();
- }
(1)客户端通过DistributedFileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否存在,父目录是否存在。
(2)NameNode返回是否可以上传。
(3)客户端请求第一个Block上传到哪几个DataNode服务器上。
(4)NameNode返回三个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步)。
在HDFS写数据过程中,NameNode会选择距离待上传数据最近距离的DataNode接收数据。那么这个最近距离怎么计算呢?
节点距离:两个节点到达最近的共同父节点的距离之和
(1)客户端通过DistributedFileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
(2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
(3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以Packet为单位来做校验)。
(4)客户端以Packet为单位接收,先在本地缓存,然后写入目标文件。
思考:NameNode中的元数据是存储在哪里的?
首先,我们做个假设,如果存储在NameNode节点的磁盘中,因为经常需要进行随机访问,还有响应客户请求,必然是效率过低。因此,元数据需要存放在内存中。但如果只存在内存中,一旦断电,元数据丢失,整个集群就无法工作了。因此产生在磁盘中备份元数据的FsImage。
这样又会带来新的问题,当在内存中的元数据更新时,如果同时更新FsImage,就会导致效率过低,但如果不更新,就会发生一致性问题,一旦NameNode节点断电,就会产生数据丢失。因此,引入Edits文件(只进行追加操作,效率很高)。每当元数据有更新或者添加元数据时,修改内存中的元数据并追加到Edits中。这样,一旦NameNode节点断电,可以通过FsImage和Edits的合并,合成元数据。
但是,如果长时间添加数据到Edits中,会导致该文件数据过大,效率降低,而且一旦断电,恢复元数据需要的时间过长。因此,需要定期进行FsImage和Edits的合并,如果这个操作由NameNode节点完成,又会效率过低。因此,引入一个新的节点SecondaryNamenode,专门用于FsImage和Edits的合并。
1)第一阶段:NameNode启动
(1)第一次启动NameNode格式化后,创建Fsimage和Edits文件。如果不是第一次启动,直接加载编辑日志和镜像文件到内存。
(2)客户端对元数据进行增删改的请求。
(3)NameNode记录操作日志,更新滚动日志。
2)第二阶段:Secondary NameNode工作
(1)Secondary NameNode询问NameNode是否需要CheckPoint。直接带回NameNode是否检查结果。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。
NameNode被格式化之后,将在NameNode指的的目录中产生Fsimage文件:
(1)fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目录和文件inode的序列化信息。
(2)edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先会被记录到edits文件中。
(3)seen_txid:该文件保存的是一个数字,就是最后一个edits 的数字。
(4)VERSION:版本信息,每次NameNode启动的时候都会将fsimage文件读入内存,加载edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将fsimage和edits文件进行了合并。
1)oiv 查看fsimage文件
(1)查看ovi 和 oev 命令:hdfs 回车,检查输出的内容
oev apply the offline edits viewer to an edits file
oiv apply the offline fsimage viewer to an fsimage
(2)基本语法:
hdfs oiv -p 文件类型 -i 镜像文件 -o 转换后文件输出路径
hdfs oiv -p XML -i fsimage_0000000000025 -o ./fsimage.xml
2)oev 查看edits 文件内容
(1)基本语法:
hdfs oev -p 文件类型 -i 编辑日志 -o 转换后文件输出路径
hdfs oev -p XML -i edits_0000000000000008577-0000000000000008578 -o ./edits.xml
1)通常情况下,SecondaryNameNode每隔一小时执行一次。
- [hdfs-default.xml]
- <property>
- <name>dfs.namenode.checkpoint.period</name>
- <value>3600s</value>
- </property>
2)一分钟检查一次操作次数,当操作次数达到1百万时,2NN执行一次
- <property>
- <name>dfs.namenode.checkpoint.txns</name>
- <value>1000000</value>
- <description>操作动作次数</description>
- </property>
-
- <property>
- <name>dfs.namenode.checkpoint.check.period</name>
- <value>60s</value>
- <description> 1分钟检查一次操作次数</description>
- </property>
(1)一个数据块在DataNode上以文件形式存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度,块数据的校验和,以及时间戳。
(2)DataNode启动后向NameNode注册,通过后,周期性(6小时)的向NameNode上报所有的块信息。
DN向NN汇报当前解读信息的时间间隔,默认6小时;
- <property>
- <name>dfs.blockreport.intervalMsec</name>
- <value>21600000</value>
- <description>Determines block reporting interval in milliseconds.</description>
- </property>
DN扫描自己节点块信息列表的时间,默认6小时
- <property>
- <name>dfs.datanode.directoryscan.interval</name>
- <value>21600s</value>
- <description>Interval in seconds for Datanode to scan data directories and reconcile the difference between blocks in memory and on the disk.
- Support multiple time unit suffix(case insensitive), as described
- in dfs.heartbeat.interval.
- </description>
- </property>
(3)心跳是每3秒一次,心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。如果超过10分钟没有收到某个DataNode的心跳,则认为该节点不可用。
(4)集群运行中可以安全加入和退出一些机器。
如下是DataNode节点保证数据完整性的方法。
(1)当DataNode读取Block的时候,它会计算CheckSum。
(2)如果计算后的CheckSum,与Block创建时值不一样,说明Block已经损坏。
(3)Client读取其他DataNode上的Block。
(4)常见的校验算法crc(32),md5(128),sha1(160)
(5)DataNode在其文件创建后周期验证CheckSum。
需要注意的是hdfs-site.xml 配置文件中的heartbeat.recheck.interval的单位为毫秒,dfs.heartbeat.interval的单位为秒。
- <property>
- <name>dfs.namenode.heartbeat.recheck-interval</name>
- <value>300000</value>
- </property>
-
- <property>
- <name>dfs.heartbeat.interval</name>
- <value>3</value>
- </property>
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。