Introduction
HDFS开创性地设计出一套文件存储方式,即对文件分割后分别存放;HDFS将要存储的大文件进行分割,分割后存放在既定的存储块(Block )中,并通过预先设定的优化处理,模式对存储的数据进行预处理,从而解决了大文件储存与计算的需求;一个HDFS集群包括两大部分,即NameNode与DataNode。一般来说,一个集群中会有一个NameNode和多个DataNode共同工作;NameNode是集群的主服务器,主要是用于对HDFS中所有的文件及内容数据进行维护,并不断读取记录集群中DataNode主机情况与工作状态,并通过读取与写入镜像日志文件的方式进行存储;DataNode在HDFS集群中担任任务具体执行角色,是集群的工作节点。文件被分成若干个相同大小的数据块,分别存储在若干个DataNode上,DataNode会定期向集群内NameNode发送自己的运行状态与存储内容,并根据NameNode发送的指令进行工作;NameNode负责接受客户端发送过来的信息,然后将文件存储位置信息发送给提交请求的客户端,由客户端直接与DataNode进行联系,从而进行部分文件的运算与操作。Block是HDFS的基本存储单元,默认大小是64M;HDFS还可以对已经存储的Block进行多副本备份,将每个Block至少复制到3个相互独立的硬件上,这样可以快速恢复损坏的数据;用户可以使用既定的API接口对HDFS中的文件进行操作;当客户端的读取操作发生错误的时候,客户端会向NameNode报告错误,并请求NameNode排除错误的DataNode后后重新根据距离排序,从而获得一个新的DataNode的读取路径。如果所有的DataNode都报告读取失败,那么整个任务就读取失败;对于写出操作过程中出现的问题,FSDataOutputStream并不会立即关闭。客户端向NameNode报告错误信息,并直接向提供备份的DataNode中写入数据。备份DataNode被升级为首选DataNode,并在其余2个DataNode中备份复制数据。NameNode对错误的DataNode进行标记以便后续对其进行处理
Quick Start
HDFS基本命令
hadoop fs -cmd
cmd:具体的操作,基本上与UNIX的命令行相同
args:参数
HDFS资源URI格式:
scheme://authority/path
scheme:协议名,file或hdfs
authority:namenode主机名
path:路径
示例:hdfs://localhost:9000/user/chunk/test.txt
假设已经在core-site.xml里配置了fs.default.name=hdfs://localhost:9000,则仅使用/user/chunk/test.txt即可。
hdfs默认工作目录为/user/$USER,$USER是当前的登录用户名。
HDFS命令示例:
hadoop fs -mkdir /user/trunk
hadoop fs -ls /user
hadoop fs -lsr /user (递归的)
hadoop fs -put test.txt /user/trunk
hadoop fs -put test.txt . (复制到hdfs当前目录下,首先要创建当前目录)
hadoop fs -get /user/trunk/test.txt . (复制到本地当前目录下)
hadoop fs -cat /user/trunk/test.txt
hadoop fs -tail /user/trunk/test.txt (查看最后1000字节)
hadoop fs -rm /user/trunk/test.txt
hadoop fs -help ls (查看ls命令的帮助文档)
图中的2:文件备份数量,因为采用了两台机器的全分布模式,所以此处为2.对于目录,使用-。
在put的时候遇到问题:
Configuration
Read File
存储结构
行存储
HDFS块内行存储的例子:
基于Hadoop系统行存储结构的优点在于快速数据加载和动态负载的高适应能力,这是因为行存储保证了相同记录的所有域都在同一个集群节点,即同一个HDFS块。不过,行存储的缺点也是显而易见的,例如它不能支持快速查询处理,因为当查询仅仅针对多列表中的少数几列时,它不能跳过不必要的列读取;此 外,由于混合着不同数据值的列,行存储不易获得一个极高的压缩比,即空间利用率不易大幅提高。
列存储
在
HDFS上按照列组存储表格的例子。在这个例子中,列
A和列
B存储在同一列组,而列
C和列
D分别存储在单独的列组。查询时列存储能够避免读不必要的列,并且压缩一个列中的相似数据能够达到较高的压缩比。然而,由于元组重构的较高开销,它并不能提供基于
Hadoop系统的快速查询处理。列存储不能保证同一 记录的所有域都存储在同一集群节点,行存储的例子中,记录的
4个域存储在位于不同节点的
3个
HDFS块中。因此,记录的重构将导致通过集群节点网络的大 量数据传输。尽管预先分组后,多个列在一起能够减少开销,但是对于高度动态的负载模式,它并不具备很好的适应性。
数据读取
hdfs读取数据流程图:
1、首先调用
FileSystem对象的
open方法,其实获取的是一个
DistributedFileSystem的实例。2、
DistributedFileSystem通过
RPC(远程过程调用
)获得文件的第一批
block的
locations,同一
block按照重复数会返回多个
locations,这些
locations按照
hadoop拓扑结构排序,距离客户端近的排在前面。3、前两步会返回一个
FSDataInputStream对象,该对象会被封装成
DFSInputStream对象,
DFSInputStream可以方便的管理
datanode和
namenode数据流。客户端调用
read方 法,
DFSInputStream就会找出离客户端最近的
datanode并连接
datanode。4、数据从
datanode源源不断的流向客户端。5、如果第一个
block块的数据读完了,就会关闭指向第一个
block块的
datanode连接,接着读取下一个
block块。这些操作对客户端来说是透明的,从客户端的角度来看只是读一个持续不断的流。6、如果第一批
block都读完了,
DFSInputStream就会去
namenode拿下一批
blocks的
location,然后继续读,如果所有的
block块都读完,这时就会关闭掉所有的流。
数据写入
hdfs写数据流程:
1.客户端通过调用
DistributedFileSystem的
create方法,创建一个新的文件。
2.DistributedFileSystem通过
RPC(远程过程调用
)调用
NameNode,去创建一个没有
blocks关联的新文件。创建前,
NameNode会做各种校验,比如文件是否存在,客户端有无权限去创建等。如果校验通过,
NameNode就会记录下新文件,否则就会抛出
IO异常。
3.前两步结束后会返回
FSDataOutputStream的对象,和读文件的时候相似,
FSDataOutputStream被封装成
DFSOutputStream,
DFSOutputStream可以协调
NameNode和
DataNode。客户端开始写数据到
DFSOutputStream,DFSOutputStream会把数据切成一个个小
packet,然后排成队列
data queue。
4.DataStreamer会去处理接受
data queue,它先问询
NameNode这个新的
block最适合存储的在哪几个
DataNode里,比如重复数是
3,那么就找到
3个最适合的
DataNode,把它们排成一个
pipeline。
DataStreamer把
packet按队列输出到管道的第一个
DataNode中,第一个
DataNode又把
packet输出到第二个
DataNode中,以此类推。
5.DFSOutputStream还有一个队列叫
ack queue,也是由
packet组成,等待
DataNode的收到响应,当
pipeline中的所有
DataNode都表示已经收到的时候,这时
akc queue才会把对应的
packet包移除掉。
6.客户端完成写数据后,调用
close方法关闭写入流。
7.DataStreamer把剩余的包都刷到
pipeline里,然后等待
ack信息,收到最后一个
ack后,通知
DataNode把文件标示为已完成。
NameNode HA
NameNode HA架构如下:
- Active NameNode和Standby NameNode:
两台NameNode形成互备,一台处于Active状态,为主NameNode,另外一台处于Standby状态,为备NameNode,只有主NameNode才能对外提供读写服务。
-
主备切换控制器ZKFailoverController:
ZKFailoverController作为独立的进程运行,对NameNode的主备切换进行总体控制。ZKFailoverController能及时检测到NameNode的健康状况,在主NameNode故障时借助Zookeeper实现自动的主备选举和切换。
-
Zookeeper集群:
为主备切换控制器提供主备选举支持。
-
共享存储系统:
共享存储系统是实现NameNode的高可用最为关键的部分,共享存储系统保存了NameNode在运行过程中所产生的HDFS的元数据。主NameNode和备用NameNode通过共享存储系统实现元数据同步。在进行主备切换的时候,新的主NameNode在确认元数据完全同步之后才能继续对外提供服务。