1、产生背景
传统的本地文件系统(单机式),在数据量增长过快、数据备份、数据安全性、操作使用便捷性上存在严重不足。
2、特点介绍
-
高容错和高可用性,硬件错误是常态而不是异常
HDFS设计为运行在普通硬件上,所以硬件故障是很正常的。HDFS提供文件存储副本策略,可以实现错误自检并快速自动恢复。个别硬件的损坏不影响整体数据完整性和计算任务的正常执行。
-
流式数据访问
HDFS主要采用流式数据读取,做批量处理而不是用户交互处理,因此HDFS更关注数据访问的高吞吐量。
-
弹性存储,支持大规模数据集
HDFS支持大文件存储,典型的文件在GB甚至TB级别,可以支持数以千万计的大规模数据集。根据业务需要灵活的增加或者缩减存储节点。弹性存储的最大挑战是减小在修改存储节点时的数据震荡问题。
-
简单一致性模型
HDFS文件实行一次性写、多次读的访问模式。设计为文件一经创建、写入和关闭之后就不需要再更改了,这种设计和假定简化了数据一致性问题,使高吞吐量成为可能。
-
移动计算而非移动数据
由于HDFS支持大文件存储,对于大文件来说,移动计算比移动数据的代价要低。这样也可以减少网络的拥塞和提高系统的吞吐量。
-
协议和接口多样性
为上层应用提供了多种接口,Http RestFul接口、NFS接口、Ftp接口等等POSIX标准协议,另外通常会有自己的专用接口。
-
多样的数据管理功能
对于数据压缩、数据加密、数据缓存和存储配额等提供了多样的管理功能。
3、应用场景
-
各大电信运营商
-
中大型互联网公司,如BAT、京东、乐视、美团等
-
金融银行保险类公司
-
各大云平台底层存储平台
-
其他本地系统无法承载存储能力的应用
HDFS架构设计
1、HDFS是什么
HDFS(Hadoop Distributed File System )Hadoop分布式文件系统。
由论文为GFS(Google File System)Google 文件系统启发,是Google GFS的开源Java实现。
2、HDFS组件角色
2.1 NameNode(简称NN)
-
HDFS元数据管理者,管理NameSpace(文件系统命名空间),记录文件是如何分割成数据块以及他们分别存储在集群中的哪些数据节点上。
-
NameSpace或其本身属性的任何更改都由NameNode记录,维护整个文件系统的文件和目录。
2.2 DataNode(简称DN)
-
DataNode是文件系统的工作节点。根据客户端或者NameNode发送的管理指令,负责HDFS的数据块的读写和检索操作。
-
通过心跳机制定期向NameNode发送他们的存储块的列表。
2.3 Client
-
客户端Client代表用户与NameNode或者DataNode交互来访问整个文件系统的对象。
-
开发人员面向Client API来编程实现,对NameNode、DataNode来说透明无感。
3、HDFS架构设计
3.1基本架构
3.3写文件流程
2.2查看某目录下文件列表
2.3查看某文本文件的内容
2.4创建目录
2.5删除目录
2.6从hdfs下载文件
2.7从本地上传文件到hdfs
2.8查看压缩的文件内容
2.9查看文件大小
2.10创建文件
2.11查看命令帮助信息
2.12自由练习
-
HDFS是一个主从式(Master/Slave)的体系结构。HDFS集群中有一个NameNode和一些DataNodes,
-
NameNode管理文件的元数据,DataNode存储实际的数据。从用户的角度看,就像操作传统的文件系统一样,可以通过目录路径对文件执行创建、读取、删除操作。客户端联系NameNode来获取元数据信息,而真正的文件I/O是直接和DataNode进行交互的。
3.2读文件流程
详细步骤:
-
第一步:Client向NameNode发送数据请求后,寻找数据对应的数据块的位置信息。
-
第二步:NameNode返回文件对应的数据块元数据信息,如所属机器、数据块的block_id、数据块的先后顺序等。
-
第三步:由Client与DataNode直接通信,读取各个block数据块的信息。过程为并行读取,由客户端合并数据。
-
详细步骤:
-
第一步:
-
Client向NameNode发送写数据请求后,寻找可以写入的数据块block信息的机器位置。
-
若文件过大,写入可能会分成很多block数据块,实际上是通过一个block一个block的申请。
-
若副本为3,则每次请求后返回一个block的对应的3个副本的block的存放位置。
-
第二步:
-
Client获取到对应的block数据块所处的DataNode节点位置后,Client开始写操作。
-
Client先写入第一个DataNode,以数据包package的方式逐个发送和接收。如64K大小的package包大小来发送和接收。
-
存在多个副本时,package包的写入是依次进行的。写入到第一个DataNode后,第一个向第二个DataNode传输。第二个写完后,由第二个向第三个DataNode传输package。以此类推。
-
写完一个block数据块后,如果还有则反复进行第一步和第二步。
-
第三步:
-
待所有的数据块block均写完后,Client接收到全部写完的ack答复,告诉NameNode数据已写完,Client关闭socket流。
-
DataNode也会向NameNode报告新增block数据块的信息。
-
HDFS高可用性措施
1、冗余备份
数据存储在这些HDFS中的节点上,为了防止因为某个节点宕机而导致数据丢失,HDFS对数据进行冗余备份,至于具体冗余多少个副本,在dfs.replication中配置。
2、跨机架副本存放
仅仅对数据进行冗余备份还不够,假设所有的备份都在一个节点上,那么该节点宕机后,数据一样会丢失,因此HDFS要有一个好的副本存放策略,该策略还在开发中。目前使用的是,以dfs.replication=3为例,在同一机架的两个节点上各备份一个副本,然后在另一个机架的某个节点上再放一个副本。前者防止该机架的某个节点宕机,后者防止某个机架宕机。
3、心跳检测
DataNode节点定时向NameNode节点发送心跳包,以确保DataNode没有宕机。如果宕机,会采取相应措施,比如数据副本的备份。
4、数据完整性检测
NameNode在创建HDFS文件时,会计算每个数据的校验和并储存起来。当客户端从DataNode获取数据时,他会将获取的数据的校验和与之前储存的校验和进行对比。
5、安全模式
HDFS启动时,会进入安全模式,此时不允许写操作。这时,NameNode会收到所有DataNode节点的数据块报告,在确认安全之后,系统自动退出安全模式。
6、核心文件备份
HDFS的核心文件是映像文件(image file)和事务日志(edit log),如果这些文件损坏,将会导致HDFS不可用。系统支持对这两个文件的备份,以确保NameNode宕机后的恢复。
7、空间回收
从HDFS中删除的文件会首先被放入到/trash中,/trash文件夹中的内容是被删除文件最后的副本,该文件夹会被定时清空。该文件夹中不存在的文件就彻底不存在了。
HDFS shell应用
1、HDFS Shell命令分类
2、hdfs dfs常用命令
hdfs dfs与hadoop fs对等,只是一个先后推荐使用的区别,目前推荐使用hdfs dfs。
hdfs提供了类似shell命令的方式,可以像操作本地系统文件一样来操作hdfs文件,常用的命令如下:
2.1查看所有命令
-
查看hdfs dfs下的所有可用命令:
-
-
查看hdfs根目录下的文件列表:
-
hdfs dfs -ls /
-
-
查看hdfs的某个文本文件:
-
hdfs dfs -cat /tmp/index.html
-
-
在hdfs中创建/tmp/tianliangedu目录:
-
hdfs dfs –mkdir /tmp/tianliangedu
-
-
删除/tmp/tianliangedu目录:
-
hdfs dfs –rm -r /tmp/tianliangedu
-
-
将hdfs中的/tmp/index.html文件下载到本地文件的当前路径下:
-
hdfs dfs -copyToLocal /tmp/index.html .
-
-
从本地当前目录下,上传之前的index.html文件到hdfs的/tmp/tianliangedu目录中:
-
hdfs dfs -copyFromLocal index.html /tmp/tianliangedu
-
-
查看压缩文本文件的格式:
-
hdfs dfs -text /tmp/tianliangedu/index.html.gz | more
-
-
查看hdfs中目录/user/zel/job017 中文件占用磁盘大小:
-
hdfs dfs -du -h /user/zel/job017
-
-
在/tmp/tianliangedu目录中创建空文件HelloWorld.txt:
-
hdfs dfs -touchz /tmp/tianliangedu/HelloWorld.txt
-
-
查看hdfs dfs中某个命令的用法,比如:cp:
-
hdfs dfs -usage cp
-
-
自由练习以上命令。
-
自学练习get、put、mv、cp、lsr等命令。
-
3、hdfs dfsadmin
hdfs dfsadmin是集群管理员的常用命令,用来对HDFS集群的整体运行进行管理和运维。常用的管理命令如下:
3.1查看可用的管理命令
-
查看管理命令的帮助信息:
-
hdfs dfsadmin -help
-
3.2报告文件系统信息
-
报告文件系统的基本信息和统计信息
-
hdfs dfsadmin -report
-
3.3设置目录配额
-
设置目录配额,目录配额是一个长整型数,限定指定目录下的名字个数:
-
hdfs dfsadmin -setQuota <quota> <dirname>……<dirname>
-
比如:hdfs dfsadmin -setQuota 10 /tmp/tianliangedu
3.4安全模式管理
当集群环境启动时,NameNode会进入一个安全模式。此时不会出现数据块的写操作。NameNode会收到各个DataNode拥有的数据块列表的数据块报告,由此NameNode获得所有的数据块信息。数据块达到最小副本数时,该数据块就被认为是安全的。
-
hdfs dfsadmin -safemode get ##返回安全模式是否开启的信息,返回Safe mode is OFF/OPEN
-
hdfs dfsadmin -safemode enter ##进入安全模式
-
hdfs dfsadmin -safemode leave ##强制NameNode退出安全模式
-
hdfs dfsadmin -safemode wait ##等待,一直到安全模式结束
HDFS Java API应用
HDFS 提供了两种访问接口,除了上面介绍的Hadoop Shell接口,还另外提供了Java API 接口,对HDFS里面的文件进行操作,具体每个Block放在哪台DataNode上面,对于开发者来说是透明的,可以像操作本地系统文件一样,对HDFS进行文件上传、创建、重命名、删除等操作。
下面就以实际操作为例子来说明其API的具体使用。
-
操作需求
// hdfs dfs -cat /tmp/tianliangedu/input.txt
从hdfs文件/tmp/tianliangedu/input.txt中读取其文本内容,并打印出来。
2、步骤分解
3、操作实现
3.1资源准备
本地新建文件index.txt,写入“HelloWorld Hadoop”内容,上传至HDFS文件系统的/tmp/tianliangedu/input.txt文件中。
3.2 Maven环境搭建
Maven开发环境搭建,用Eclipse IDE工具,创建一个新的Maven项目。
3.3 Pom配置依赖
修改新建项目的pom.xml的配置文件,将Hadoop的依赖加入进去:
-
配置依赖jar包的坐标,即描述我是谁。
-
指定依赖的仓库
-
具体需要哪些相关依赖
-
配置打包插件
具体配置参考如下:
-
<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.tianliangedu.course</groupId>
-
<artifactId>TlHadoopCore</artifactId>
-
<version>0.0.1-SNAPSHOT</version>
-
<properties>
-
<!-- 设置项目编码为 UTF-8 -->
-
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-
</properties>
-
<!-- 首先配置仓库的服务器位置,首选阿里云,也可以配置镜像方式,效果雷同 -->
-
<repositories>
-
<repository>
-
<id>nexus-aliyun</id>
-
<name>Nexus aliyun</name>
-
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
-
</repository>
-
</repositories>
-
<dependencies>
-
<!-- 引入hadoop-cli-2.7.4依赖 -->
-
<dependency>
-
<groupId>org.apache.hadoop</groupId>
-
<artifactId>hadoop-client</artifactId>
-
<version>2.7.4</version>
-
<scope>provided</scope>
-
</dependency>
-
</dependencies>
-
<build>
-
<finalName>TlHadoopCore</finalName>
-
<plugins>
-
<plugin>
-
<artifactId>maven-compiler-plugin</artifactId>
-
<version>2.3.2</version>
-
<configuration>
-
<source>1.8</source>
-
<target>1.8</target>
-
<encoding>UTF-8</encoding>
-
</configuration>
-
</plugin>
-
<plugin>
-
<artifactId>maven-assembly-plugin</artifactId>
-
<configuration>
-
<descriptorRefs>
-
<descriptorRef>jar-with-dependencies</descriptorRef>
-
</descriptorRefs>
-
<archive>
-
<manifest>
-
<mainClass>com.tl.job009.hdfs.HdfsFileRead</mainClass>
-
</manifest>
-
</archive>
-
</configuration>
-
<executions>
-
<execution>
-
<id>make-assembly</id>
-
<phase>package</phase>
-
<goals>
-
<goal>assembly</goal>
-
</goals>
-
</execution>
-
</executions>
-
</plugin>
-
</plugins>
-
</build>
-
</project>
3.4 编码实现
-
实现步骤
-
定义读取的HDFS文件路径
-
用HDFS Java API将HDFS文本文件转化为字符串形式:
1)、先将HDFS文本文件以字节数组形式读取到内存中。
2)、将字节数组转换成字符串。
-
通过控制台将字符串打印出来
-
涉及Hadoop中的类说明
-
Configuration
1)、负责HDFS系统的配置文件的加载或者更改配置文件的工具类,将配置文件件加载到内存中。
2)、通过new Configuration()初始化Configuration对象,即将HDFS的默认配置文件加载到内存中,供别的对象使用。
-
FileSystem
1)、对HDFS系统的引用,操作HDFS的根类。所有HDFS文件操作均以此类为源头发起。
2)、通过FileSystem.get(new Configuration())初始化针对某集群的FileSystem对象。
-
Path
1)、HDFS文件的抽象,与Java IO流中的File对象对等。
2)、通过new Path(hdfsFilePath)得到Path对象。
-
FSDataInputStream
1)、HDFS系统的文件字节输入流,类比于Java IO中的InputStream类,实现对HDFS文件的读取。
2)、FileSystem.open(filePath)可以获取HDFS的输入流。
-
FSDataOutputStream
1)、HDFS系统的文件字节输出流,类比于Java IO中的OutputStream类,实现对HDFS文件的写入。
2)、FileSystem.create(filePath)可以获取HDFS的输出流。
-
具体代码
-
//新建源码包com.tianliangedu.utils,并在其中新建java文件HdfsFileOperatorUtil.java。
-
//代码如下:
-
package com.tianliangedu.utils;
-
import java.io.ByteArrayOutputStream;
-
import org.apache.hadoop.conf.Configuration;
-
import org.apache.hadoop.fs.FSDataInputStream;
-
import org.apache.hadoop.fs.FileSystem;
-
import org.apache.hadoop.fs.Path;
-
import org.apache.log4j.Logger;
-
/**
-
* hdfs 文件操作工具类,从任意的hdfs filepath中读取文本内容
-
*/
-
public class HdfsFileOperatorUtil {
-
//添加日志输出能力
-
Logger logger = Logger.getLogger(HdfsFileOperatorUtil.class);
-
-
// 加载配置文件到内存对象
-
static Configuration hadoopConf = new Configuration();
-
/** 从HDFS上读取文件 */
-
public static String readFromFile(String srcFile) throws Exception {
-
//文件路径的空判断
-
if (srcFile == null || srcFile.trim().length() == 0) {
-
throw new Exception("所要读取的源文件" + srcFile + ",不存在,请检查!");
-
}
-
//将文件内容转换成字节数组
-
byte[] byteArray = readFromFileToByteArray(srcFile);
-
if (byteArray == null || byteArray.length == 0) {
-
return null;
-
}
-
//将utf-8编码的字节数组通过utf-8再进行解码
-
return new String(byteArray, "utf-8");
-
}
-
/**
-
* 将指定的文件路径从hdfs读取并转换为byte array.
-
*
-
* @param srcFile
-
* @return
-
*/
-
public static byte[] readFromFileToByteArray(String srcFile)
-
throws Exception {
-
if (srcFile == null || srcFile.trim().length() == 0) {
-
throw new Exception("所要读取的源文件" + srcFile + ",不存在,请检查!");
-
}
-
//获取hadoopConf对应的hdfs集群的对象引用
-
FileSystem fs = FileSystem.get(hadoopConf);
-
//将给定的srcFile构建成一个hdfs的路径对象Path
-
Path hdfsPath=new Path(srcFile);
-
FSDataInputStream hdfsInStream = fs.open(hdfsPath);
-
-
//初始化一块字节数组缓冲区,大小为65536。缓存每次从流中读取出来的字节数组
-
byte[] byteArray = new byte[65536];
-
//初始化字节数输出流, 存放最后的所有字节数组
-
ByteArrayOutputStream bos = new ByteArrayOutputStream();
-
-
// 实际读过来多少
-
int readLen = 0;
-
//只要还有流数据能读出来,就一直读下去
-
while ((readLen = hdfsInStream.read(byteArray)) > 0) {
-
bos.write(byteArray,0,readLen);
-
//byteArray = new byte[65536];
-
}
-
//读取完成,将hdfs输入流关闭
-
hdfsInStream.close();
-
//将之前写到字节输出流中的字节,转换成一个整体的字节数组
-
byte[] resultByteArray=bos.toByteArray();
-
bos.close();
-
return resultByteArray;
-
}
-
-
public static void main(String[] args) throws Exception {
-
//定义要读入的hdfs的文件路径
-
String hdfsFilePath = "/tmp/tianliangedu/input.txt";
-
//将文件从hdfs读取下来,转化成字符串
-
String result = readFromFile(hdfsFilePath);
-
//根据题意,将字符串通过命令行输出
-
System.out.println(result);
-
}
-
}
-
-
本地运行
1)Hadoop2.x及以后已经支持本地local模式模拟运行hdfs操作
"Usage of hadoop-site.xml is deprecated. Instead use core-site.xml, "
+ "mapred-site.xml and hdfs-site.xml to override properties of " +
"core-default.xml, mapred-default.xml and hdfs-default.xml " +
"respectively");
}
addDefaultResource("core-default.xml");
addDefaultResource("core-site.xml");
}
//所在类路径---org.apache.hadoop.conf.Configuration
//配置文件部分
static{
//print deprecation warning if hadoop-site.xml is found in classpath
ClassLoader cL = Thread.currentThread().getContextClassLoader();
if (cL == null) {
cL = Configuration.class.getClassLoader();
}
if(cL.getResource("hadoop-site.xml")!=null) {
LOG.warn("DEPRECATED: hadoop-site.xml found in the classpath. " +
-
将输入路径改为windows开发环境的本地路径即可。
-
如:String hdfsFilePath = “E:/temp/input.txt”
-
-
直接右击运行主类main_class即可,效果如下
-
-
标准运行-远程生产环境正式布署运行
-
Maven打包
-
右击项目,run as -> maven install进行打包及上传至本地仓库中,生成target目录下的
TlHadoopCore-jar-with-dependencies.jar文件。
-
将运行包发布上传至Hadoop环境
-
通过rz命令,将生成的TlHadoopCore-jar-with-dependencies.jar上传到hdfs环境中。
-
线上测试运行
-
yarn jar TlHadoopCore-jar-with-dependencies.jar com.tianliangedu.utils.HdfsFileOperatorUtil
-
查看验证效果
-
经典问题分析
-
HDFS为何要将文件分成block块存储?
-
减少底层操作系统的IO读取时的寻址时间
-
方便更高效的流式读取,提高吞吐量
-
HDFS block块的默认大小时多少?
-
dfs.blocksize为Hadoop定义block块大小的设置参数,在hdfs-site.xml中
-
版本不一样,默认值不同。Hadoop2.2.x及以后版本均为128M
-
HDFS block块的大小是否可以更改?
-
可以修改
-
参数修改对以前的文件不起作用,对以后的文件起作用
-
也可针对上传文件临时修改,指定-D dfs.blocksize即可
-
一个block块文件是否可以存储多个文件数据?
-
一个block块文件不会跨文件存储
-
一个block块文件最多只会存储一个文件对应的数据
-
如果一个文件的大小,小于一个blocksize,那么它实际占用多大空间?
-
实际文件多大则占多大空间,但是占了一个block块的元数据空间大小
-
小文件越多,Hadoop NameNode的压力越大。故Hadoop的优势在于处理大文件数据,GB、TB甚至PB等。
-
HDFS block越大越好?还是越小越好?
-
越大则分块越少,则NameNode压力将减小,但并行的IO和处理能力降低
-
越小则分块越多,则NameNode处理压力越大,但因为寻址时间太久,不利于提高吞吐量
-
适中即可,一般采用官方的128M即可
-
关于Configuration工作原理说明
-
Configuration类的配置文件加载分析
-
-