# HDFS

# 一、HDFS优缺点

# 1.1 HDFS优点

  1. 高容错性
    • 数据自动保存多个副本。它通过增加副本的形式,提高容错性。
    • 某一个副本丢失以后,它可以自动恢复。
  2. 适合处理大数据
    • 数据规模:能够处理数据规模达到GB、TB、甚至PB级别的数据;
    • 文件规模:能够处理百万规模以上的文件数量,数量相当之大。
  3. 可构建在廉价机器上,通过多副本机制,提高可靠性。

# 1.2 HDFS缺点

  1. 不适合低延时数据访问,比如毫秒级的存储数据,是做不到的。
  2. 无法高效的对大量小文件进行存储。
    • 存储大量小文件的话,它会占用NameNode大量的内存来存储文件目录和块信息。这样是不可取的,因为NameNode的内存总是有限的;
    • 小文件存储的寻址时间会超过读取时间,它违反了HDFS的设计目标。
  3. 不支持并发写入、文件随机修改。
    • 一个文件只能有一个写,不允许多个线程同时写;
    • 仅支持数据append(追加),不支持文件的随机修改

# 二、HDFS 架构

# 2.1 NameNode

  1. 管理HDFS的名称空间
  2. 配置副本策略
  3. 管理数据块(Block)映射信息

# 2.2 Secondary NameNode

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

  1. 辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode
  2. 在紧急情况下,可辅助恢复NameNode

# 2.3 Client

  1. 文件切分。文件上传HDFS的时候,Client将文件切分成一个一个的Block,然后进行上传;
  2. 与NameNode交互,获取文件的位置信息;
  3. 与DataNode交互,读取或者写入数据;
  4. Client提供一些命令来管理HDFS,比如NameNode格式化;
  5. Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作;

# 2.4 DataNode

就是Slave。NameNode 下达命令,DataNode执行实际的操作。

  1. 存储实际的数据块;
  2. 执行数据块的读/写操作。

# 2.5 HDFS 写数据流程

  1. Client:向NameNode请求上传文件xx
  2. NameNode:响应是否可以上传文件(会检查权限,目录结构等)
  3. Client:请求上传第一个Block,让NameNode返回DataNode
  4. NameNode:返回节点(优先本地节点,比如在DN1上传数据,就优先放DN1,NameNode还得考虑负载均衡等)
  5. Client:请求与DataNode建立Block传输通道
  6. DataNode:应答
  7. Client:传输数据
  8. Client:告诉Namenode数据传输完成

# 2.6 HDFS 读数据流程

  1. Client:请求下载文件xx
  2. NameNode:返回目标文件的元数据
  3. Client:请求读取DataNode数据(多个DataNode
  4. DataNode:传输数据

# 2.7 NameNode工作机制

NameNode

  1. NameNode:通电后先加载edits_inprogress_xx 和 fsimage_xx到内存
  2. Client:发送元数据的增删改请求 xxx
  3. NameNode:在edits_inprogress中记录要干什么,然后再更改内存
  4. NameNode:内存数据增删改

Secondary NameNode

  1. Secondary NameNode:问NameNode是否要CheckPoint (触发条件,定时间到(默认一小时)或Edits中数据满了)
  2. Secondary NameNode:可以CheckPoint以后请求执行
  3. NameNode:生成新的edits_inprogress,如果CheckPoint过程中有新的访问,记录会往新的edits_inprogress文件写。原来的edits_inprogress变为edits_xx
  4. Secondary NameNode:把NameNode的fsimage和edits_xx都拷贝过来
  5. Secondary NameNode:将拷贝的文件加载到内存,然后执行合并操作。
  6. Secondary NameNode:生成一个新文件fsimage.chkpoint
  7. Secondary NameNode:把新文件fsimage.chkpoint拷贝到NameNode
  8. NameNode:把fsimage.chkpoint修改名称覆盖fsimage

# 2.8 Fsimage和Edits概念

  • Fsimage文件:HDFS文件系统元数据的一个永久性的检查点,其中包含HDFS文件系统的所有目 录和文件inode的序列化信息。
  • Edits文件:存放HDFS文件系统的所有更新操作的路径,文件系统客户端执行的所有写操作首先 会被记录到Edits文件中。
  • seen_txid文件保存的是一个数字,就是最后一个edits_的数字
  • 每次NameNode启动的时候都会将Fsimage文件读入内存,加载Edits里面的更新操作,保证内存中的元数据信息是最新的、同步的,可以看成NameNode启动的时候就将Fsimage和Edits文件进行了合并。
  1. oiv 查看 fsimage 文件
[root@master01 current]# pwd
/opt/hadoop-3.3.1/data/dfs/name/current
[root@master01 current]#  hdfs oiv -p XML -i fsimage_0000000000000000304 -o ~/fsimage.xml
[root@master01 current]# cat ~/fsimage.xml
1
2
3
4

2)oev 查看 Edits 文件

[root@master01 current]# pwd
/opt/hadoop-3.3.1/data/dfs/name/current
[root@master01 current]# hdfs oev -p XML -i edits_0000000000000000303-0000000000000000304 -o ~/edits.xml
[root@master01 current]# cat ~/edits.xml
1
2
3
4

# 2.9 CheckPoint 时间设置

  1. 通常情况下,SecondaryNameNode 每隔一小时执行一次。

hdfs-default.xml

<property>
   <name>dfs.namenode.checkpoint.period</name>
   <value>3600s</value>
</property>
1
2
3
4
  1. 一分钟检查一次操作次数,当操作次数达到 1 百万时,SecondaryNameNode 执行一次。
   <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
2
3
4
5
6
7
8
9

# 2.10 DataNode工作机制

  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>
    
    1
    2
    3
    4
    5
    DN 扫描自己节点块信息列表的时间,默认 6 小时
     <property>
         <name>dfs.datanode.directoryscan.interval</name>
         <value>21600s</value>
     </property>
    
    1
    2
    3
    4
  3. 心跳是每 3 秒一次,心跳返回结果带有 NameNode 给该 DataNode 的命令如复制块 数据到另一台机器,或删除某个数据块。如果超过 10 分钟没有收到某个 DataNode 的心跳, 则认为该节点不可用。
  4. 集群运行中可以安全加入和退出一些机器。

# 2.11 DataNode 数据完整性

  1. 当 DataNode 读取 Block 的时候,它会计算 CheckSum。
  2. 如果计算后的 CheckSum,与 Block 创建时值不一样,说明 Block 已经损坏。
  3. Client 读取其他 DataNode 上的 Block。
  4. 常见的校验算法 crc(32),md5(128),sha1(160)
  5. DataNode 在其文件创建后周期验证 CheckSum。

# 2.12 DataNode 掉线时间参数

  1. DataNode进程死亡或 者网络故障造成DataNode 无法与NameNode通信
  2. NameNode不会立即把该节点判定 为死亡,要经过一段时间,这段时间 暂称作超时时长。
  3. HDFS默认的超时时长为10分钟+30秒。
  4. 如果定义超时时间为TimeOut,则超时时长的计算公式为: TimeOut = 2 * dfs.namenode.heartbeat.recheck-interval + 10 * dfs.heartbeat.interval。 而默认的dfs.namenode.heartbeat.recheck-interval 大小为5分钟,dfs.heartbeat.interval默认为3秒。

需要注意的是 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>
1
2
3
4
5
6
7
8

# 三、HDFS 常用 shell 命令

# 3.1 从本地加载文件到 HDFS

# 二选一执行即可
hadoop fs -put  [localsrc] [dst] 
hadoop fs -copyFromLocal [localsrc] [dst] 
1
2
3

# 3.2 从 HDFS 导出文件到本地

# 二选一执行即可
hadoop fs -get  [dst] [localsrc] 
hadoop fs -copyToLocal [dst] [localsrc] 
1
2
3

# 3.3 统计当前目录下各文件大小

  • 默认单位字节
  • -s : 显示所有文件大小总和,
  • -h : 将以更友好的方式显示文件大小(例如 64.0m 而不是 67108864)
hadoop fs -du  <path>
# 显示某个目录的总大小
hadoop fs -du -h -s  /user/hive/warehouse/name
1
2
3

# 3.4 合并下载多个文件

  • -nl 在每个文件的末尾添加换行符(LF)
  • -skip-empty-file 跳过空文件
hadoop fs -getmerge
# 示例 将HDFS上的hbase-policy.xml和hbase-site.xml文件合并后下载到本地的/usr/test.xml
hadoop fs -getmerge -nl  /test/hbase-policy.xml /test/hbase-site.xml /usr/test.xml
1
2
3

# 3.5 更改文件复制因子

hadoop fs -setrep [-R] [-w] <numReplicas> <path>
1
  • 更改文件的复制因子。如果 path 是目录,则更改其下所有文件的复制因子
  • -w : 请求命令是否等待复制完成
# 示例
hadoop fs -setrep -w 3 /user/hadoop/dir1
1
2

# 3.6 权限控制

# 权限控制和Linux上使用方式一致
# 变更文件或目录的所属群组。 用户必须是文件的所有者或超级用户。
hadoop fs -chgrp [-R] GROUP URI [URI ...]
# 修改文件或目录的访问权限  用户必须是文件的所有者或超级用户。
hadoop fs -chmod [-R] <MODE[,MODE]... | OCTALMODE> URI [URI ...]
# 修改文件的拥有者  用户必须是超级用户。
hadoop fs -chown [-R] [OWNER][:[GROUP]] URI [URI ]
1
2
3
4
5
6
7

创建hive目录和权限

hadoop fs –mkdir /tmp
hadoop fs –chmod a+w /tmp
hadoop fs –mkdir /user/hive/warehouse
hadoop fs –chmod a+w /user/hive/warehouse
1
2
3
4

# 3.7 追加一个文件到已经存在的文件末尾

hadoop fs -appendToFile aa.txt bb.txt
1

# 四、HDFS Java API

# 4.1 环境准备

  1. 官方二进制包
tar -xzf Documents/Soft/BigData/Hadoop/hadoop-3.3.1.tar.gz
1
  1. 设置环境变量(windows)
export HADOOP_HOME=/Users/haseochen/hadoop-3.3.1
export PATH=$PATH:$HADOOP_HOME/bin
1
2
  1. Maven依赖 开源版
    <dependencies>
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>3.3.1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.30</version>
        </dependency>
    </dependencies>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  1. Maven依赖 CHD版

想要使用 HDFS API,需要导入依赖 hadoop-client。如果是 CDH 版本的 Hadoop,还需要额外指明其仓库地址:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.heibaiying</groupId>
    <artifactId>hdfs-java-api</artifactId>
    <version>1.0</version>


    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <hadoop.version>2.6.0-cdh5.15.2</hadoop.version>
    </properties>


    <!---配置 CDH 仓库地址-->
    <repositories>
        <repository>
            <id>cloudera</id>
            <url>https://repository.cloudera.com/artifactory/cloudera-repos/</url>
        </repository>
    </repositories>


    <dependencies>
        <!--Hadoop-client-->
        <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-client</artifactId>
            <version>${hadoop.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

# 4.2 FileSystem

FileSystem 是所有 HDFS 操作的主入口。由于之后的每个单元测试都需要用到它,这里使用 @Before 注解进行标注。

// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java

private static final String HDFS_PATH = "hdfs://192.168.0.106:8020";
private static final String HDFS_USER = "root";
private static FileSystem fileSystem;

@Before
public void prepare() {
    try {
        Configuration configuration = new Configuration();
        // 这里我启动的是单节点的 Hadoop,所以副本系数设置为 1,默认值为 3
        configuration.set("dfs.replication", "1");
        fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, HDFS_USER);
    } catch (IOException e) {
        e.printStackTrace();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (URISyntaxException e) {
        e.printStackTrace();
    }
}


@After
public void destroy() {
    fileSystem = null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# 4.3 创建目录

支持递归创建目录:

// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java

@Test
public void mkDir() throws Exception {
    fileSystem.mkdirs(new Path("/hdfs-api/test0/"));
}
1
2
3
4
5
6

# 4.4 创建指定权限的目录

FsPermission(FsAction u, FsAction g, FsAction o) 的三个参数分别对应:创建者权限,同组其他用户权限,其他用户权限,权限值定义在 FsAction 枚举类中。

@Test
public void mkDirWithPermission() throws Exception {
    fileSystem.mkdirs(new Path("/hdfs-api/test1/"),
            new FsPermission(FsAction.READ_WRITE, FsAction.READ, FsAction.READ));
}
1
2
3
4
5

# 4.5 创建文件,并写入内容

@Test
public void create() throws Exception {
    // 如果文件存在,默认会覆盖, 可以通过第二个参数进行控制。第三个参数可以控制使用缓冲区的大小
    FSDataOutputStream out = fileSystem.create(new Path("/hdfs-api/test/a.txt"),
                                               true, 4096);
    out.write("hello hadoop!".getBytes());
    out.write("hello spark!".getBytes());
    out.write("hello flink!".getBytes());
    // 强制将缓冲区中内容刷出
    out.flush();
    out.close();
}
1
2
3
4
5
6
7
8
9
10
11
12

# 4.6 判断文件是否存在

@Test
public void exist() throws Exception {
    boolean exists = fileSystem.exists(new Path("/hdfs-api/test/a.txt"));
    System.out.println(exists);
}
1
2
3
4
5

# 4.7 查看文件内容

查看小文本文件的内容,直接转换成字符串后输出:

@Test
public void readToString() throws Exception {
    FSDataInputStream inputStream = fileSystem.open(new Path("/hdfs-api/test/a.txt"));
    String context = inputStreamToString(inputStream, "utf-8");
    System.out.println(context);
}
1
2
3
4
5
6

inputStreamToString 是一个自定义方法,代码如下:

/**
 * 把输入流转换为指定编码的字符
 *
 * @param inputStream 输入流
 * @param encode      指定编码类型
 */
private static String inputStreamToString(InputStream inputStream, String encode) {
    try {
        if (encode == null || ("".equals(encode))) {
            encode = "utf-8";
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, encode));
        StringBuilder builder = new StringBuilder();
        String str = "";
        while ((str = reader.readLine()) != null) {
            builder.append(str).append("\n");
        }
        return builder.toString();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 4.8 文件重命名

@Test
public void rename() throws Exception {
    Path oldPath = new Path("/hdfs-api/test/a.txt");
    Path newPath = new Path("/hdfs-api/test/b.txt");
    boolean result = fileSystem.rename(oldPath, newPath);
    System.out.println(result);
}
1
2
3
4
5
6
7

# 4.9 删除目录或文件

public void delete() throws Exception {
    /*
     *  第二个参数代表是否递归删除
     *    +  如果 path 是一个目录且递归删除为 true, 则删除该目录及其中所有文件;
     *    +  如果 path 是一个目录但递归删除为 false,则会则抛出异常。
     */
    boolean result = fileSystem.delete(new Path("/hdfs-api/test/b.txt"), true);
    System.out.println(result);
}
1
2
3
4
5
6
7
8
9

# 4.10 上传文件到HDFS

@Test
public void copyFromLocalFile() throws Exception {
    // 如果指定的是目录,则会把目录及其中的文件都复制到指定目录下
    Path src = new Path("D:\\BigData-Notes\\notes\\installation");
    Path dst = new Path("/hdfs-api/test/");
    fileSystem.copyFromLocalFile(src, dst);
}
1
2
3
4
5
6
7

# 4.11 上传大文件并显示上传进度

// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java

@Test
    public void copyFromLocalBigFile() throws Exception {

        File file = new File("D:\\kafka.tgz");
        final float fileSize = file.length();
        InputStream in = new BufferedInputStream(new FileInputStream(file));

        FSDataOutputStream out = fileSystem.create(new Path("/hdfs-api/test/kafka5.tgz"),
                new Progressable() {
                  long fileCount = 0;

                  public void progress() {
                     fileCount++;
                     // progress 方法每上传大约 64KB 的数据后就会被调用一次
                     System.out.println("上传进度:" + (fileCount * 64 * 1024 / fileSize) * 100 + " %");
                   }
                });

        IOUtils.copyBytes(in, out, 4096);

    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 4.12 从HDFS上下载文件

@Test
public void copyToLocalFile() throws Exception {
    Path src = new Path("/hdfs-api/test/kafka.tgz");
    Path dst = new Path("D:\\app\\");
    /*
     * 第一个参数控制下载完成后是否删除源文件,默认是 true,即删除;
     * 最后一个参数表示是否将 RawLocalFileSystem 用作本地文件系统;
     * RawLocalFileSystem 默认为 false,通常情况下可以不设置,
     * 但如果你在执行时候抛出 NullPointerException 异常,则代表你的文件系统与程序可能存在不兼容的情况 (window 下常见),
     * 此时可以将 RawLocalFileSystem 设置为 true
     */
    fileSystem.copyToLocalFile(false, src, dst, true);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 4.13 查看指定目录下所有文件的信息

// code/java/HDFS/src/main/java/com/bihell/hdfs/HdfsClient.java

public void listFiles() throws Exception {
    FileStatus[] statuses = fileSystem.listStatus(new Path("/hdfs-api"));
    for (FileStatus fileStatus : statuses) {
        //fileStatus 的 toString 方法被重写过,直接打印可以看到所有信息
        System.out.println(fileStatus.toString());
    }
}
1
2
3
4
5
6
7
8
9

FileStatus 中包含了文件的基本信息,比如文件路径,是否是文件夹,修改时间,访问时间,所有者,所属组,文件权限,是否是符号链接等,输出内容示例如下:

FileStatus{
path=hdfs://192.168.0.106:8020/hdfs-api/test; 
isDirectory=true; 
modification_time=1556680796191; 
access_time=0; 
owner=root; 
group=supergroup; 
permission=rwxr-xr-x; 
isSymlink=false
}
1
2
3
4
5
6
7
8
9
10

# 4.14 递归查看指定目录下所有文件的信息

@Test
public void listFilesRecursive() throws Exception {
    RemoteIterator<LocatedFileStatus> files = fileSystem.listFiles(new Path("/hbase"), true);
    while (files.hasNext()) {
        System.out.println(files.next());
    }
}
1
2
3
4
5
6
7

和上面输出类似,只是多了文本大小,副本系数,块大小信息。

LocatedFileStatus{
path=hdfs://192.168.0.106:8020/hbase/hbase.version; 
isDirectory=false; 
length=7; 
replication=1; 
blocksize=134217728; 
modification_time=1554129052916; 
access_time=1554902661455; 
owner=root; group=supergroup;
permission=rw-r--r--; 
isSymlink=false}
1
2
3
4
5
6
7
8
9
10
11

# 4.15 查看文件的块信息

@Test
public void getFileBlockLocations() throws Exception {

    FileStatus fileStatus = fileSystem.getFileStatus(new Path("/hdfs-api/test/kafka.tgz"));
    BlockLocation[] blocks = fileSystem.getFileBlockLocations(fileStatus, 0, fileStatus.getLen());
    for (BlockLocation block : blocks) {
        System.out.println(block);
    }
}
1
2
3
4
5
6
7
8
9

# 五、HDFS 核心参数

# 5.1 NameNode 内存生产配置

  1. NameNode 内存计算

每个文件块大概占用 150byte,一台服务器 128G 内存为例,能存储多少文件块呢?

128 * 1024 * 1024 * 1024 / 150Byte ≈ 9.1亿
G     MB     KB     Byte
1
2
  1. Hadoop2.x 系列,配置 NameNode 内存

NameNode 内存默认 2000m,如果服务器内存 4G,NameNode 内存可以配置 3g。在hadoop-env.sh 文件中配置如下。

HADOOP_NAMENODE_OPTS=-Xmx3072m
1
  1. Hadoop3.x 系列,配置 NameNode 内存

hadoop-env.sh 中描述 Hadoop 的内存是动态分配的

具体修改:hadoop-env.sh

export HDFS_NAMENODE_OPTS="-Dhadoop.security.logger=INFO,RFAS - Xmx1024m"
export HDFS_DATANODE_OPTS="-Dhadoop.security.logger=ERROR,RFAS -Xmx1024m"
1
2
  1. 查看 NameNode 占用内存
[tpxcer@master01 root]$ jps
75153 NameNode
75377 DataNode
75741 NodeManager
76015 JobHistoryServer
45775 Jps
[tpxcer@master01 root]$  jmap -heap 75153
Heap Configuration:
   MaxHeapSize              = 2046820352 (1952.0MB)
[tpxcer@master01 root]$  jmap -heap 75377
Heap Configuration:
   MaxHeapSize              = 2046820352 (1952.0MB)
1
2
3
4
5
6
7
8
9
10
11
12

上面 NameNode 和 DataNode 占用内存都是自动分配的,且相等。 不是很合理。 经验参考: https://docs.cloudera.com/documentation/enterprise/6/release-notes/topics/rg_hardware_requirements.html

# 5.2 NameNode 心跳并发配置

hdfs-site.xml

The number of Namenode RPC server threads that listen to requests from clients. If dfs.namenode.servicerpc-address is not configured then Namenode RPC server threads listen to requests from all nodes.
NameNode 有一个工作线程池,用来处理不同 DataNode 的并发心跳以及客户端并发 的元数据操作。
对于大集群或者有大量客户端的集群来说,通常需要增大该参数。默认值是 10<property>
<name>dfs.namenode.handler.count</name>
   <value>21</value>
</property>
1
2
3
4
5
6

企业经验:dfs.namenode.handler.count=20 × 𝑙𝑜𝑔𝐶𝑙𝑢𝑠𝑡𝑒𝑟 𝑆𝑖𝑧𝑒,比如集群规模(DataNode 台 𝑒 数)为 3 台时,此参数设置为 21。可通过简单的 python 代码计算该值,代码如下。

``Bash

import math print int(20*math.log(3)) 21

quit() ``

# 5.3 开启回收站配置

开启回收站功能,可以将删除的文件在不超时的情况下,恢复原数据,起到防止误删除、 备份等作用。

  1. 开启回收站功能参数说明
  • 默认值 fs.trash.interval = 0,0 表示禁用回收站;其他值表示设置文件的存活时间。
  • 默认值 fs.trash.checkpoint.interval = 0,检查回收站的间隔时间。如果该值为 0,则该值设置和 fs.trash.interval 的参数值相等。
  • 要求 fs.trash.checkpoint.interval <= fs.trash.interval。
  1. 启用回收站

修改 core-site.xml,配置垃圾回收时间为 1 分钟。

<property> 
   <name>fs.trash.interval</name> 
   <value>1</value>
</property>
1
2
3
4
  1. 查看回收站
/user/xxx/.Trash/...
1
  1. 通过网页上直接删除的文件也不会走回收站。
  2. 通过程序删除的文件不会经过回收站,需要调用 moveToTrash()才进入回收站
Trash trash = New Trash(conf);
trash.moveToTrash(path);
1
2
  1. 只有在命令行利用 hadoop fs -rm 命令删除的文件才会走回收站。

# 5.4 NameNode 多目录配置

在 hdfs-site.xml 文件中添加如下内容

<property> 
    <name>dfs.namenode.name.dir</name>
    <value>file://${hadoop.tmp.dir}/dfs/name1,file://${hadoop.tmp.dir}/dfs/name2</value>
</property>
1
2
3
4

检查 name1 和 name2 里面的内容,发现一模一样。

# 5.4 DataNode 多目录配置

<property> 
    <name>dfs.datanode.data.dir</name>
    <value>file://${hadoop.tmp.dir}/dfs/data1,file://${hadoop.tmp. dir}/dfs/data2</value>
</property>
1
2
3
4

# 5.5 集群数据均衡之磁盘间数据均衡

生产环境,由于硬盘空间不足,往往需要增加一块硬盘。刚加载的硬盘没有数据时,可以执行磁盘数据均衡命令。(Hadoop3.x 新特性)

  1. 生成均衡计划(我们只有一块磁盘,不会生成计划)
hdfs diskbalancer -plan hadoop103
1
  1. 执行均衡计划
hdfs diskbalancer -execute hadoop103.plan.json
1
  1. 查看当前均衡任务的执行情况
hdfs diskbalancer -query hadoop103
1
  1. 取消均衡任务
hdfs diskbalancer -cancel hadoop103.plan.json
1

# 六、HDFS群集压测

# 6.1 测试 HDFS 写性能

  1. 测试内容:向 HDFS 集群写 10 个 128M 的文件
[tpxcer@master01 root]$ hadoop jar /opt/hadoop-3.3.1/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.1-tests.jar TestDFSIO -write -nrFiles 15 -fileSize 128MB
2021-09-03 17:52:48,167 INFO fs.TestDFSIO: ----- TestDFSIO ----- : write
2021-09-03 17:52:48,167 INFO fs.TestDFSIO:             Date & time: Fri Sep 03 17:52:48 CST 2021
2021-09-03 17:52:48,167 INFO fs.TestDFSIO:         Number of files: 15
2021-09-03 17:52:48,167 INFO fs.TestDFSIO:  Total MBytes processed: 1920
2021-09-03 17:52:48,167 INFO fs.TestDFSIO:       Throughput mb/sec: 1.2
2021-09-03 17:52:48,168 INFO fs.TestDFSIO:  Average IO rate mb/sec: 2.7
2021-09-03 17:52:48,168 INFO fs.TestDFSIO:   IO rate std deviation: 5.7
2021-09-03 17:52:48,168 INFO fs.TestDFSIO:      Test exec time sec: 161.51
1
2
3
4
5
6
7
8
9

nrFiles n为生成mapTask的数量,生产环境一般可通过hadoop103:8088查看CPU 核数,设置为(CPU 核数 - 1)

  • Numberoffiles:生成mapTask数量,一般是集群中(CPU核数-1)
  • TotalMBytesprocessed:单个map处理的文件大小
  • Throughputmb/sec:单个mapTak的吞吐量
    计算方式:处理的总文件大小/每一个 mapTask 写数据的时间累加 集群整体吞吐量:生成 mapTask 数量*单个 mapTak 的吞吐量
  • Average IO rate mb/sec::平均 mapTak 的吞吐量 计算方式:每个 mapTask 处理文件大小/每一个 mapTask 写数据的时间全部相加除以 task 数量
  • IO rate std deviation:方差、反映各个 mapTask 处理的差值,越小越均衡
  1. 测试结果分析

一共参与测试的文件:15 个文件 * 3 个副本 = 45 个 压测后的速度:1.2 实测速度:1.2M/s * 45 个文件 ≈ 54M/s

# 6.2 测试 HDFS 读性能

测试内容:读取 HDFS 集群 10 个 128M 的文件

[tpxcer@master01 root]$ hadoop jar /opt/hadoop-3.3.1/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.1-tests.jar TestDFSIO -read -nrFiles 8 -fileSize 128MB
2021-09-03 18:22:56,694 INFO fs.TestDFSIO: ----- TestDFSIO ----- : read
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:             Date & time: Fri Sep 03 18:22:56 CST 2021
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:         Number of files: 8
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:  Total MBytes processed: 1024
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:       Throughput mb/sec: 130.05
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:  Average IO rate mb/sec: 131.33
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:   IO rate std deviation: 12.87
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:      Test exec time sec: 43.76
2021-09-03 18:22:56,695 INFO fs.TestDFSIO:
1
2
3
4
5
6
7
8
9
10

# 6.3 删除测试生成数据

[tpxcer@master01 root]$ hadoop jar /opt/hadoop-3.3.1/share/hadoop/mapreduce/hadoop-mapreduce-client-jobclient-3.3.1-tests.jar TestDFSIO -clean
1

# 七、HDFS—集群扩容及缩容

# 7.1 添加白名单

在白名单中的主机ip都可以随意访问群集数据

  1. /hadoop-3.3.1/etc/hadoop 目录下分别创建 whitelistblacklist 文件
  2. 在白名单中添加主机名
master01
slave01
slave02
1
2
3
  1. hdfs-site.xml配置文件中增加dfs.hosts配置参数
<!-- 白名单 --> 
<property>
    <name>dfs.hosts</name>
    <value>/hadoop-3.3.1/etc/hadoop/whitelist</value> 
</property>
<!-- 黑名单 --> 
<property>
    <name>dfs.hosts.exclude</name>
    <value>/hadoop-3.3.1/etc/hadoop/blacklist</value>
</property>
1
2
3
4
5
6
7
8
9
10
  1. 分发配置文件
  2. 第一次添加白名单必须重启集群,不是第一次,只需要刷新 NameNode 节点即可
# 刷新节点
hdfs dfsadmin -refreshNodes
1
2

# 7.2 服役新服务器

  1. 环境准备
  • 克隆一台Hadoop主机
  • 修改IP地址和主机名称
  • 拷贝配置
  • 删除 克隆 Hadoop 的历史数据,data 和 log 数据
  • 配置ssh 无密登录
  1. 直接启动 DataNode,即可关联到集群
  2. 在白名单中增加新服役的服务器

# 7.3 服务器间数据均衡

  1. 开启数据均衡命令
sbin/start-balancer.sh - threshold 10
1

对于参数 10,代表的是集群中各个节点的磁盘空间利用率相差不超过 10%,可根据实际情况进行调整。

  1. 停止数据均衡命令
sbin/stop-balancer.sh
1

由于 HDFS 需要启动单独的 Rebalance Server 来执行 Rebalance 操作,所以尽量 不要在 NameNode 上执行 start-balancer.sh,而是找一台比较空闲的机器。

# 7.4 黑名单退役服务器

  1. 第一次添加黑名单必须重启集群,不是第一次,只需要刷新 NameNode 节点即可
hdfs dfsadmin -refreshNodes Refresh nodes successful
1
  1. 检查 Web 浏览器,退役节点的状态为 decommission in progress(退役中),说明数据 节点正在复制块到其他节点
  2. 等待退役节点状态为 decommissioned(所有块已经复制完成),停止该节点及节点资源管理器。
hdfs --daemon stop datanode
yarn --daemon stop nodemanager
1
2

注意:如果副本数是 3,服役的节点小于等于 3,是不能退役成功的,需要修改 副本数后才能退役

  1. 如果数据不均衡,可以用命令实现集群的再平衡
sbin/start-balancer.sh - threshold 10
1

# 八、HDFS—存储优化

# 8.1 纠删码

HDFS 默认情况下,一个文件有 3 个副本,这样提高了数据的可靠性,但也带来了 2 倍 的冗余开销。Hadoop3.x 引入了纠删码,采用计算的方式,可以节省约 50%左右的存储空间。

  1. 纠删码操作相关的命令
[tpxcer@master01 ~]$ hdfs ec
1
  1. 查看当前支持的纠删码策略
[tpxcer@master01 ~]$ hdfs ec -listPolicies
Erasure Coding Policies:
ErasureCodingPolicy=[Name=RS-10-4-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=10, numParityUnits=4]], CellSize=1048576, Id=5], State=DISABLED
ErasureCodingPolicy=[Name=RS-3-2-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=3, numParityUnits=2]], CellSize=1048576, Id=2], State=DISABLED
ErasureCodingPolicy=[Name=RS-6-3-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=1], State=ENABLED
ErasureCodingPolicy=[Name=RS-LEGACY-6-3-1024k, Schema=[ECSchema=[Codec=rs-legacy, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=3], State=DISABLED
ErasureCodingPolicy=[Name=XOR-2-1-1024k, Schema=[ECSchema=[Codec=xor, numDataUnits=2, numParityUnits=1]], CellSize=1048576, Id=4], State=DISABLED
1
2
3
4
5
6
7
  1. 纠删码策略解释

RS-3-2-1024k:使用 RS 编码,每 3 个数据单元,生成 2 个校验单元,共 5 个单元,也就是说:这 5 个单元中,只要有任意的 3 个单元存在(不管是数据单元还是校验单元,只要总数=3),就可以得到原始数据。每个单元的大小是 1024k=1024*1024=1048576。

RS-10-4-1024k:使用 RS 编码,每 10 个数据单元(cell),生成 4 个校验单元,共 14 个单元,也就是说:这 14 个单元中,只要有任意的 10 个单元存在(不管是数据单元还是校 验单元,只要总数=10),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576。

RS-6-3-1024k:使用 RS 编码,每 6 个数据单元,生成 3 个校验单元,共 9 个单元,也 就是说:这 9 个单元中,只要有任意的 6 个单元存在(不管是数据单元还是校验单元,只要 总数=6),就可以得到原始数据。每个单元的大小是 1024k=1024*1024=1048576。

RS-LEGACY-6-3-1024k:策略和上面的 RS-6-3-1024k 一样,只是编码的算法用的是 rs-legacy。

XOR-2-1-1024k:使用 XOR 编码(速度比 RS 编码快),每 2 个数据单元,生成 1 个校 验单元,共 3 个单元,也就是说:这 3 个单元中,只要有任意的 2 个单元存在(不管是数据 单元还是校验单元,只要总数= 2),就可以得到原始数据。每个单元的大小是1024k=1024*1024=1048576。

# 8.2 纠删码案例实操

纠删码策略是给具体一个路径设置。所有往此路径下存储的文件,都会执行此策略。 默认只开启对 RS-6-3-1024k 策略的支持,如要使用别的策略需要提前启用。

需求:将/input 目录设置为 RS-3-2-1024k 策略

  1. 开启对 RS-3-2-1024k 策略的支持
hdfs ec -enablePolicy -policy RS-3-2-1024k 
1
  1. 在 HDFS 创建目录,并设置 RS-3-2-1024k 策略
hdfs dfs -mkdir /input
hdfs ec -setPolicy -path /input -policy RS-3-2-1024k
1
2

# 九、异构存储(冷热数据分离)

异构存储主要解决,不同的数据,存储在不同类型的硬盘中,达到最佳性能的问题。

# 9.1 存储类型和存储策略

  1. 关于存储类型
  • RAM_DISK:(内存镜像文件系统)
  • SSD:(SSD固态硬盘)
  • DISK:(普通磁盘,在HDFS中,如果没有主动声明数据目录存储类型默认都是DISK)
  • ARCHIVE:(没有特指哪种存储介质,主要的指的是计算能力比较弱而存储密度比较高的存储介质,用来解决数据量的 容量扩增的问题,一般用于归档)
  1. 关于存储策略 说明:从Lazy_Persist到Cold,分别代表了设备的访问速度从快到慢
策略ID 策略名称 副本分布 说明
15 Lazy_Persist RAM_DISK:1,DISK:n-1 一个副本保存在内存RAM_DISK中,其余副本保存在磁盘中。
12 All_SSD SSD:n 所有副本都保存在SSD中。
10 One_SSD SSD:1,DISK:n-1 一个副本保存在SSD中,其余副本保存在磁盘中。
7 Hot(default) DISK:n Hot:所有副本保存在磁盘中,这也是默认的存储策略。
5 Warm DSIK:1,ARCHIVE:n-1 一个副本保存在磁盘上,其余副本保存在归档存储上。
2 Cold ARCHIVE:n 所有副本都保存在归档存储上。

# 9.2 异构存储 Shell 操作

  1. 查看当前有哪些存储策略可以用
[tpxcer@master01 ~]$ hdfs storagepolicies -listPolicies
1
  1. 为指定路径(数据存储目录)设置指定的存储策略
hdfs storagepolicies -setStoragePolicy -path xxx -policy xxx
1
  1. 获取指定路径(数据存储目录或文件)的存储策略
hdfs storagepolicies -getStoragePolicy -path xxx
1
  1. 取消存储策略;执行改命令之后该目录或者文件,以其上级的目录为准,如果是根目录,那么就是 HOT
hdfs storagepolicies -unsetStoragePolicy -path xxx
1
  1. 查看文件块的分布
bin/hdfs fsck xxx -files -blocks -locations
1
  1. 查看集群节点
hadoop dfsadmin -report
1

# 9.3 测试环境准备& 实践

待完善

# 十、HDFS—故障排除

# 11.1 NameNode 故障处理

NameNode 进程挂了并且存储的数据也丢失了,如何恢复 NameNode

  1. kill -9 NameNode 进程
kill -9 19886
1
  1. 删除 NameNode 存储的数据(/opt/hadoop-x.x.x/data/tmp/dfs/name)
rm -rf /opt/module/hadoop-x.x.x/data/dfs/name/*
1
  1. 拷贝 SecondaryNameNode 中数据到原 NameNode 存储数据目录
scp -r xxx@xxx:/opt/module/hadoop-x.x.x/data/dfs/namesecondary/* ./name/
1
  1. 重新启动 NameNode
hdfs --daemon start namenode
1

# 11.2 集群安全模式

  1. 安全模式:文件系统只接受读数据请求,而不接受删除、修改等变更请求
  2. 进入安全模式场景
    • NameNode在加载镜像文件和编辑日志期间处于安全模式;
    • NameNode在接收DataNode注册时,处于安全模式
  3. 退出安全模式条件
dfs.namenode.safemode.min.datanodes:最小可用 datanode 数量,默认 0
dfs.namenode.safemode.threshold-pct:副本数达到最小要求的 block 占系统总 block 数的 百分比,默认 0.999f。(只允许丢一个块)
dfs.namenode.safemode.extension:稳定时间,默认值 30000 毫秒,即 30
1
2
3
  1. 基本语法 集群处于安全模式,不能执行重要操作(写操作)。集群启动完成后,自动退出安全模式。
# 查看安全模式状态
[tpxcer@master01 hadoop-3.3.1]$ hdfs dfsadmin -safemode get

# 进入安全模式状态
bin/hdfs dfsadmin -safemode enter

# 离开安全模式状态
bin/hdfs dfsadmin -safemode leave

# 等待安全模式状态
bin/hdfs dfsadmin -safemode wait
1
2
3
4
5
6
7
8
9
10
11
  1. 案例 磁盘修复

(1)模拟损坏,分别进入各datanode的/hadoop-x.x.x/data/dfs/data/current/中删除某 2 个块信息
(2)重新启动集群 查看http://master01:9870/dfshealth.html#tab-overview ,会提示安全模式已经打开,块的数量没达到要求
(3)离开安全模式

hdfs dfsadmin -safemode get
hdfs dfsadmin -safemode leave
1
2
  1. 再查看查看http://master01:9870/dfshealth.html#tab-overview 根据提示处理
更新时间: 9/6/2021, 4:44:52 PM