Java大数据开发入门系列(二)————HDFS

一、概述

HDFS (Hadoop Distributed File System)是 Hadoop 下的分布式文件系统,具有高容错、高吞吐量等特性,可以部署在低成本的硬件上。

二、起因

要发展大数据,首要问题就是考虑数据存储的问题,大数据存储存在以下核心问题:

  • 数据存储容量的问题,既然大数据要解决的是数以PB计的数据计算问题,而一般的服务器磁盘容量通常1-2TB,那么如何存储这么大规模的数据。
  • 数据读写速度的问题,一般磁盘的连续读写速度为几十MB,以这样的速度,几十PB的数据恐怕要读写到天荒地老。
  • 数据可靠性的问题,磁盘大约是计算机设备中最易损坏的硬件了,在网站一块磁盘使用寿命大概是一年,如果磁盘损坏了,数据怎么办?

在大数据技术出现之前,人们就需要面对这些关于存储的问题,对应的解决方案就是RAID技术。RAID(独立磁盘冗余阵列)技术主要是为了改善磁盘的存储容量,读写速度,增强磁盘的可用性和容错能力。目前服务器级别的计算机都支持插入多块磁盘(8块或者更多),通过使用RAID技术,实现数据在多块磁盘上的并发读写和数据备份。

常用RAID技术原理

1.RAID 0

数据在从内存缓冲区写入磁盘时,根据磁盘数量将数据分成N份,这些数据同时并发写入N块磁盘,使得数据整体写入速度是一块磁盘的N倍。读取的时候也一样,因此RAID0具有极快的数据读写速度,但是RAID0不做数据备份,N块磁盘中只要有一块损坏,数据完整性就被破坏,所有磁盘的数据都会损坏。

2.RAID 1

数据在写入磁盘时,将一份数据同时写入两块磁盘,这样任何一块磁盘损坏都不会导致数据丢失,插入一块新磁盘就可以通过复制数据的方式自动修复,具有极高的可靠性。

3.RAID 10

结合RAID0和RAID1两种方案,将所有磁盘平均分成两份,数据同时在两份磁盘写入,相当于RAID1,但是在每一份磁盘里面的N/2块磁盘上,利用RAID0技术并发读写,既提高可靠性又改善性能,不过RAID10的磁盘利用率较低,有一半的磁盘用来写备份数据。

4.RAID5

相比RAID3,更多被使用的方案是RAID5。RAID5和RAID3很相似,但是校验数据不是写入第N块磁盘,而是螺旋式地写入所有磁盘中。这样校验数据的修改也被平均到所有磁盘上,避免RAID3频繁写坏一块磁盘的情况。

5.RAID6

如果数据需要很高的可靠性,在出现同时损坏两块磁盘的情况下(或者运维管理水平比较落后,坏了一块磁盘但是迟迟没有更换,导致又坏了一块磁盘),仍然需要修复数据,这时候可以使用RAID6。

RAID6和RAID5类似,但是数据只写入N-2块磁盘,并螺旋式地在两块磁盘中写入校验信息(使用不同算法生成)。

RAID 等级 RAID0 RAID1 RAID3 RAID5 RAID6 RAID10
别名 条带 镜像 专用奇偶校验条带 分布奇偶校验条带 双重奇偶校验条带 镜像加条带
容错性
冗余类型
热备份选择
读性能
随机写性能 一般 一般
连续写性能 一般
需要磁盘数 n≥1 2n (n≥1) n≥3 n≥3 n≥4 2n(n≥2)≥4
可用容量 全部 50% (n-1)/n (n-1)/n (n-2)/n 50%

RAID技术只是在单台服务器的多块磁盘上组成阵列,大数据需要更大规模的存储空间和访问速度。因此,Hadoop结合前面的RAID技术和Google提出的GFS论文,创造了HDFS。HDFS(Hadoop分布式文件系统)是根据GFS(Google文件系统)的原理开发的,是GFS的简化版。

三、HDFS架构原理

HDFS的架构

HDFS的架构:主从架构,三大角色

  1. Namenode负责整个分布式文件系统的元数据(MetaData)管理,也就是文件路径名,数据block的ID以及存储位置等信息,承担着操作系统中文件分配表(FAT)的角色。HDFS为了保证数据的高可用,会将一个block复制为多份(缺省情况为3份),并将三份相同的block存储在不同的服务器上。这样当有磁盘损坏或者某个DataNode服务器宕机导致其存储的block不能访问的时候,Client会查找其备份的block进行访问。
  2. Datanode负责文件数据的存储和读写操作,HDFS将文件数据分割成若干块(block),每个DataNode存储一部分block,这样文件就分布存储在整个HDFS服务器集群中。
  3. SecondaryNamenode严格意义上来说并不属于namenode的备份节点,它主要起到的作用其实是替namenode分担压力,降低负载(元数据的编辑日志合并,也就是edits log)之用

1.心跳机制

为了保证集群的高可用性和高可靠性(HA),DataNode会通过心跳和NameNode保持通信,如果DataNode超时未发送心跳,NameNode就会认为这个DataNode已经失效,立即查找这个DataNode上存储的block有哪些,以及这些block还存储在哪些服务器上,随后通知这些服务器再复制一份block到其他服务器上,保证HDFS存储的block备份数符合用户设置的数目,即使再有服务器宕机,也不会丢失数据。

心跳机制

1.NameNode启动之后,会开一个ipc server。NameNode 全权管理数据块的复制,它周期性从集群中的每个 DataNode 接收心跳信号和 block 状态报告,接收到心跳信号意味着该 DataNode 节点工作正常,块状态报告包含了该 DataNode 上所有数据块的列表

2.DataNode启动,连接NameNode注册,每隔3s向NameNode发送一个心跳,并携带状态信息,周期性地向 NameNode 上报 block 报告。NameNode 返回对该 DataNode 的指令,如将数据块复制到另一台机器,或删除某个数据块等,而当某一个 DataNode 超过10min还没向 NameNode 发送心跳,此时 NameNode 就会判定该 DataNode 不可用,此时客户端的读写操作就不会再传达到该 DataNode 上。

2.安全模式

Hadoop 集群刚开始启动时会进入安全模式,就用到了心跳机制。在集群刚启动的时候,每一个 DataNode 都会向 NameNode 发送 block 报告,NameNode 会统计它们上报的总block数,除以一开始知道的总个数total,当 block/total < 99.99% 时,会触发安全模式,安全模式下客户端就没法向HDFS写数据,只能进行读数据。

Namenode感知Datanode掉线死亡时间的计算公式为:

1
timeout = 2 * heartbeat.recheck.interval + 10 * dfs.heartbeat.interval

HDFS默认超时时间为630秒,heartbeat.recheck.interval(重新检查的时间间隔) 的默认值为5分钟,而 dfs.heartbeat.interval(发送一次心跳的间隔) 默认值为3秒。

安全模式不仅仅是集群刚启动时等所有的Datanode汇报这一种情况会进入安全模式的,还有就是HDFS数据块丢失达到一个比例的时候,也会自动进入,这个比例默认是0.1%,1000个块丢1个已经很严重的事件了。

四、HDFS的使用

关于java api的说明:

FileSystem 是所有 HDFS 操作的主入口。

1
2
3
4
5
Configuration configuration = new Configuration();
// 这里我启动的是单节点的 Hadoop,所以副本系数设置为 1,默认值为 3
configuration.set("dfs.replication", "1");
// HDFS_PATH hdfs连接地址,HDFS_USER hdfs连接用户
FileSystem fileSystem = FileSystem.get(new URI(HDFS_PATH), configuration, HDFS_USER);

1. 显示当前目录结构

shell命令:

1
2
3
4
5
6
# 显示当前目录结构
hadoop fs -ls <path>
# 递归显示当前目录结构
hadoop fs -ls -R <path>
# 显示根目录下内容
hadoop fs -ls /

java API:

1
FileStatus[] statuses = fileSystem.listStatus(new Path("/"));

2. 创建目录

shell命令:

1
2
3
4
# 创建目录
hadoop fs -mkdir <path>
# 递归创建目录
hadoop fs -mkdir -p <path>

java API:

1
fileSystem.mkdirs(new Path("/test0/"));

3. 删除操作

shell命令:

1
2
3
4
# 删除文件
hadoop fs -rm <path>
# 递归删除目录和文件
hadoop fs -rm -R <path>

java API:

1
2
//返会 Boolean类型
fileSystem.delete(new Path("/hdfs-api/test/b.txt"), true);

4. 从本地加载文件到 HDFS

shell命令:

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

java API:

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

5. 从 HDFS 导出文件到本地

shell命令:

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

java API:

1
2
3
4
5
6
7
8
9
10
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);

6. 查看文件内容

shell命令:

1
2
3
# 二选一执行即可
hadoop fs -text <path>
hadoop fs -cat <path>

java API:

1
2
3
FSDataInputStream inputStream = fileSystem.open(new Path("/hdfs-api/test/a.txt"));
String context = inputStreamToString(inputStream, "utf-8");
System.out.println(context);

7. 显示文件的最后一千字节

1
2
3
hadoop fs -tail  <path> 
# 和Linux下一样,会持续监听文件内容变化 并显示文件的最后一千字节
hadoop fs -tail -f <path>

8. 拷贝文件

shell命令:

1
hadoop fs -cp [src] [dst]

9. 移动文件

shell命令:

1
hadoop fs -mv [src] [dst]

10. 统计当前目录下各文件大小

shell命令:

  • 默认单位字节
  • -s : 显示所有文件大小总和,
  • -h : 将以更友好的方式显示文件大小(例如 64.0m 而不是 67108864)
1
hadoop fs -du  <path>

11. 合并下载多个文件

shell命令:

  • -nl 在每个文件的末尾添加换行符(LF)
  • -skip-empty-file 跳过空文件
1
2
3
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

12. 统计文件系统的可用空间信息

shell命令:

1
hadoop fs -df -h /

13. 更改文件复制因子

shell命令:

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

14. 权限控制

shell命令:

1
2
3
4
5
6
7
# 权限控制和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 ]

15. 文件检测

shell命令:

1
hadoop fs -test - [defsz]  URI

可选选项:

  • -d:如果路径是目录,返回 0。
  • -e:如果路径存在,则返回 0。
  • -f:如果路径是文件,则返回 0。
  • -s:如果路径不为空,则返回 0。
  • -r:如果路径存在且授予读权限,则返回 0。
  • -w:如果路径存在且授予写入权限,则返回 0。
  • -z:如果文件长度为零,则返回 0。
1
2
# 示例
hadoop fs -test -e filename

java API:

1
2
// 返会Boolean类型
fileSystem.exists(new Path("/hdfs-api/test/a.txt"));