Hadoop分布式文件系统

分布式文件系统

分布式文件系统(Distributed File System)是一种通过网络实现文件在多台主机上进行分布式存储的文件系统,一般采用Client/Server(客户端/服务器)模式。

计算机集群的基本架构
计算机集群的基本架构

集群中的计算机节点存放在机架(Pack)上,每个机架可存放8~64个节点,同一机架上的不同节点之间通过网络互连(常用吉比特以太网),多个不同机架采用另一级网络或交换机互联。

 

大规模文件系统的整体结构

采用多副本存储,保证数据的完整性。

 

分布式文件系统的设计目标主要包括透明性、并发控制、文件复制、硬件和操作系统的异构型、可伸缩性、容错、安全等。其中透明性包括访问透明性(用户不用区分本地文件和远程文件)、位置透明性(路径名相同,用户不用管文件副本数量和实际存储位置)、性能和伸缩透明性(用户不用知道系统中节点的增加或减少以及性能的变化)。

HDFS简介以及相关概念

HDFS的目标:兼容廉价的硬件设备、流数据读写、大数据集、简单的文件模型、强大的跨平台兼容性。

HDFS的局限性:不适合低延迟数据访问、无法高效存储大量小文件、不支持多用户写入以及任意修改文件。

HDFS中,默认一个块的大小是64MB,每个块作为独立的单元进行存储。以块为单位读写数据,可以把寻址开销分摊到大量数据中。HDFS寻址开销包括磁盘寻道开销、数据块的定位开销。若客户端需要访问一个文件:从名称节点获得组成这个文件的数据块的位置列表—>根据位置列表获取实际存储各个数据块的数据节点的位置—>数据节点根据数据块信息在本地Linux文件系统中找到对应的文件—>把数据返回给客户端。

抽象的块概念的优点:支持大规模文件存储、简化系统设计、适合数据备份。

名称节点

NameNode(名称节点)负责管理分布式文件系统的命名空间(Namespace),保存了两个核心的数据结构:FsImage和EditLog,FsImage用于维护文件系统树以及文件树中所有的文件和文件夹的元数据,操作日志文件EditLog中记录了所有针对文件的创建、删除、重命名等操作。

名称节点记录了每个文件中各个块所在的数据节点的位置信息,但它并不持久化存储这些信息,而是在系统每次启动时扫描所有数据节点重构得到这些信息。

名称节点的数据结构

数据节点

DataNode(数据节点)负责数据的存储和读取,会根据客户端或名称节点的调度来进行数据的存储和检索,并向名称节点定期发送自己所存储的块的列表。

HDFS体系结构

HDFS体系结构

HDFS采用了主从(Master/Slave)结构模型,一个HDFS集群包括一个名称节点(NameNode)和若干个数据节点(DataNode)。名称节点作为中心服务器,负责管理文件系统的命名空间及客户端对文件的访问。集群中的数据节点一般是一个节点运行一个数据节点进程,负责处理文件系统客户端的读/写请求,在名称节点的统一调度下进行数据块的创建、删除和复制等操作。

HDFS命名空间

HDFS的命名空间包含目录、文件和块。

HDFS使用的是传统的分级文件体系,用户可以像使用普通文件系统一样,创建、删除目录和文件,在目录间转移文件,重命名文件等。

通信协议

HDFS是一个部署在集群上的分布式文件系统,因此,很多数据需要通过网络进行传输

所有的HDFS通信协议都是构建在TCP/IP协议基础之上的;

客户端通过一个可配置的端口向名称节点主动发起TCP连接,并使用客户端协议与名称节点进行交互;

名称节点和数据节点之间则使用数据节点协议进行交互;

客户端与数据节点的交互是通过RPC(Remote Procedure Call)来实现的。在设计上,名称节点不会主动发起RPC,而是响应来自客户端和数据节点的RPC请求。

客户端

客户端是用户操作HDFS最常用的方式,HDFS在部署时都提供了客户端;

HDFS客户端是一个库,暴露了HDFS文件系统接口,这些接口隐藏了HDFS实现中的大部分复杂性;

客户端可以支持打开、读取、写入等常见的操作,并且提供了类似Shell的命令行方式来访问HDFS中的数据;

HDFS也提供了Java API,作为应用程序访问文件系统的客户端编程接口。

HDFS体系结构的局限性

HDFS只设置唯一一个名称节点,虽然简化了系统设计,但也带来了一些明显的局限性:

(1)命名空间的限制:名称节点是保存在内存中的,因此,名称节点能够容纳的对象(文件、块)的个数会受到内存空间大小的限制。

(2)性能的瓶颈:整个分布式文件系统的吞吐量,受限于单个名称节点的吞吐量。

(3)隔离问题:由于集群中只有一个名称节点,只有一个命名空间,因此无法对不同应用程序进行隔离。

(4)集群的可用性:一旦这个唯一的名称节点发生故障,会导致整个集群变得不可用。

HDFS存储原理

冗余数据的保存

HDFS数据块多副本存储

多副本方式对数据进行冗余存储,优点:加快数据传输速度、容易检查数据错误、保证数据的可靠性。

数据存储策略

数据存放

HDFS采用以机架(Rack)为基础的数据存放策略。一个HDFS集群通常包含多个机架,不同机架之间的数据通讯需要经过交换机或路由器,同一个机架中不同机器之间的通讯则不需要(带宽更大)。

HDFS默认每个数据节点都在不同的机架上:

缺点:写入数据时不能充分利用同一机架内部机器之间的带宽;

优点:数据可靠性、多个机架并行读取数据,提高读取速度、更容易实现系统内部负载均衡和错误处理。

Block的副本放置策略

HDFS默认的冗余复制因子3

每一个文件块会被同时保存到3个地方,其中两份放在同一个机架的不同机器上,第三份放在不同机架的机器上面。

数据读取

HDFS提供了一个API可以确定一个数据节点所属的机架ID,客户端也可以调用API获取自己所属的机架ID。

当客户端读取数据时,从名称节点获得数据块不同副本的存放位置列表,列表中包含了副本所在的数据节点,可以调用API来确定客户端和这些数据节点所属的机架ID,当发现某个数据块副本对应的机架ID和客户端对应的机架ID相同时,就优先选择该副本读取数据,如果没有发现,就随机选择一个副本读取数据。

数据复制

流水线复制策略。

比如:客户端要往HDFS中写入一个文件时,首先将文件写入本地,并切分成若干块(每个块的大小由HDFS的设定值决定)。每个块都向HDFS集群中的名称节点发起写请求,名称节点根据系统中各数据节点的使用情况,选择一个数据节点列表返回给客户端,然后,客户端把数据首先写入列表中的第一个数据节点,同时把列表传给第1个数据节点,当第1个数据节点接收到4KB数据的时候,写入本地,并且向列表中的第2个数据节点发起连接请求,把自己已经接收到的4KB数据和列表传给第2个数据节点,第2个数据节点接收到4KB数据的时候,写入本地,并且向列表中的第3个数据节点发起连接请求......这样,列表中的多个数据节点形成一条数据复制的流水线。当文件写完。数据复制也同时完成。

数据错误与恢复

1.名称节点出错

名称节点保存了所有的元数据信息,其中最核心的两大数据结构是FsImage和Editlog,HDFS设置了备份机制,把这些核心文件同步复制到备份服务器SecondaryNameNode上。当名称节点出错时,就可以根据备份服务器SecondaryNameNode中的FsImage和Editlog数据进行恢复。

2.数据节点出错

每个数据节点会定期向名称节点发送“心跳”信息,向名称节点报告自己的状态。当数据节点发生故障或者网络断网时,这些数据节点就会被标记为“宕机”,节点上面的所有数据都会被标记为“不可读”,名称节点不会再给它们发送任何I/O请求。这时,由于一些数据节点的不可用,会导致一些数据块的副本数量小于冗余因子,名称节点会定期检查这种情况,一旦发现某个数据块的副本数量小于冗余因子,就会启动数据冗余复制,为它生成新的副本。

HDFS和其它分布式文件系统的最大区别就是可以调整冗余数据的位置。

3.数据出错

网络传输和磁盘错误等因素,都会造成数据错误。客户端在读取到数据后,会采用md5和sha1对数据块进行校验,以确定读取到正确的数据;

在文件被创建时,客户端就会对每一个文件块进行信息摘录,并把这些信息写入到同一个路径的隐藏文件里面。当客户端读取文件的时候,会先读取该信息文件,然后,利用该信息文件对每个读取的数据块进行校验,如果校验出错,客户端就会请求到另外一个数据节点读取该文件块,并且向名称节点报告这个文件块有错误,名称节点会定期检查并且重新复制这个块。

HDFS数据读写过程

读取文件

FileSystem是一个通用文件系统的抽象基类,可以被分布式文件系统继承,所有可能使用Hadoop文件系统的代码,都要使用这个类;

Hadoop为FileSystem这个抽象类提供了多种具体实现;

DistributedFileSystem就是FileSystem在HDFS文件系统中的具体实现;

FileSystem的open()方法返回的是一个输入流FSDataInputStream对象,在HDFS文件系统中,具体的输入流就是DFSInputStream;FileSystem中的create()方法返回的是一个输出流FSDataOutputStream对象,在HDFS文件系统中,具体的输出流就是DFSOutputStream。

步骤解读:

1、import org.apache.hadoop.fs.FileSystem;

Configuration conf = new Configuration();

FileSystem fs = FileSystem.get(conf);

FSDataInputStream in = fs.open(new Path(uri));

2、通过ClientProtocal.getBlockLocations()远程调用名称节点,获得文件开始部分数据块的位置;对于该数据块,名称节点返回保存该数据块的所有数据节点的地址,并根据距离客户端远近进行排序。

3、客户端获得输入流FSDataInputStream以后,调用read()函数开始读取数据,输入流根据前面的排序结果选择距离客户端最近的数据节点建立连接并读取数据。

4、数据从数据节点读到客户端,当该数据块读取完毕时, FSDataInputStream关闭和该数据节点的连接。

5、通过ClientProtocal.getBlockLocations()查找下一个数据块。

注:FSDataInputStream封装了DFSInputStream

 

读取文件:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FSDataInputStream;

public class MyChapter{
	public static void main(String[] args){
		try{
			Configuration conf = new Configuration();
			conf.set("fs.defaultFS","hdfs://localhost:9000");
			conf.set("fs.hdfs.imp1","org.apache.hadoop.hdfs.DistributedFileSystem");
			FileSystem fs = FileSystem.get(conf);
			Path file = new Path("test");
			FSDataInputStream getIt = fs.open(file);
			BufferedReader d = new BufferedReader(new InputStreamReader(getIt));
			String content = d.readLine();//读取文件一行
			System.out.printIn(content);
			d.close();//关闭文件
			fs.close();//关闭HDFS
		}catch (Exception e){e.printStackTrace();}
	}
}

 

写入文件

步骤解读:

1、import org.apache.hadoop.fs.FileSystem;

Configuration conf = new Configuration();

FileSystem fs = FileSystem.get(conf);

FSDataOutputStream out = fs.create(new Path(uri));

2、RPC远程调用名称节点,在文件系统的命名空间中新建一个文件,名称节点会执行一些检查(文件是否存在,客户端权限)。

4、数据被分成一个个分包,分包被放入DFSOutputStream对象的内部队列,DFSOutputStream向名称节点申请保存数据块的若干数据节点。这些数据节点形成一个数据流管道,队列中的分包最后被打包成数据包发往数据流管道中的第一个数据节点,第一个数据节点将数据包发送到第二个节点......依此类推,形成“流水线复制”。

5、为了保证节点数据准确,接收到数据的数据节点要向发送者发送“确认包”,确认包沿着数据流管道逆流而上,经过各个节点最终到达客户端。客户端收到应答时,它将对应的分包从内部队列移除。

7、DFSOutputStream调用ClientProtocal.complete()方法通知名称节点关闭文件

注:FSDataOutputStream封装了DFSOutputStream

 

写入文件:

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FSDataOutputStream;

public class MyChapter{
	public static void main(String[] args){
		try{
			Configuration conf = new Configuration();
			conf.set("fs.defaultFS","hdfs://localhost:9000");
			conf.set("fs.hdfs.imp1","org.apache.hadoop.hdfs.DistributedFileSystem");
			FileSystem fs = FileSystem.get(conf);
			byte[] buff = "Hello World".getBytes();//要写入的内容
			String filename = "test";//要写入的文件名
			FSDataOutputStream os = fs.create(new Path(filename));
			os.write(buff,0,buff.length);
			System.out.printIn("Create:"+filename);
			os.close();
			fs.close();
		}catch (Exception e){e.printStackTrace();}
	}
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值