HDFS知识总结
一、HDFS概述
1.1 HDFS产生背景
随着数据量越来越大,在一个操作系统管辖的范围内存不下了,那么就分配到更多的操作系统管理的磁盘中,但是不方便管理和维护,迫切需要一种系统来管理多台机器上的文件,这就是分布式文件管理系统。HDFS只是分布式文件管理系统中的一种。
1.2 HDFS概念
1)HDFS,它是一个文件系统,用于存储文件,通过目录树来定位文件;其次,它是分布式的,由很多服务器联合起来实现其功能,集群中的服务器有各自的角色。
2)HDFS的设计适合一次写入,多次读出的场景,且不支持文件的修改。适合用来做数据分析,并不适合用来做网盘应用。
二、HDFS优缺点
2.1 优点
1、高容错性
- 数据自动保存多个副本。它通过增加副本的形式,提高容错性
- 某一个副本丢失以后,它可以自动恢复
- Block元数据信息+心跳
- 多副本,提供容错机制,副本丢失或宕机自动恢复,默认存三份
2、适合批处理
- 移动计算而非数据
- 数据位置暴露给计算框架(Block偏移量)
3、适合大数据处理
- 数据规模:能够处理数据规模达到 GB、TB、甚至PB级别的数据
- 文件规模:能够处理百万规模以上的文件数量,数量相当之大
4、可构建在廉价机器上,通过多副本机制提高可靠性,提供容错和恢复机制
2.2 缺点
1、不适合低延时数据访问,比如毫秒级的存储数据,是做不到的
2、无法高效的对大量小文件进行存储
- 存储大量小文件的话,它会占用NameNode大量的内存来存储文件、目录和块信息。这样是不可取的,因为NameNode的内存总是有限的
- 小文件存储的寻道时间会超过读取时间,它违反了HDFS的设计目标
3、不支持并发写入、文件随机修改
- 一个文件只能有一个写,不允许多个线程同时写
- 仅支持数据 append(追加),不支持文件的随机修改
问:如何使用append实现数据的增删改查 答:追加+标记+删除更改
三、HDFS存储机制:
3.1 HDFS存储单元:
HDFS中的block、packet、chunk,很多博文介绍HDFS读写流程上来就直接从文件分块开始,其实,要把读写过程细节搞明白前,你必须知道block、packet与chunk。下面分别讲述。
1、block
在Hadoop1.0的版本中,默认的大小是64M,在Hadoop2.0以及后面的版本中,默认的大小是128M。这个值可以在HDFS配置文件hdfs-site.xml中通过dfs.block.size属性来设置。因为块太小:寻址时间占比过高。块太大:Map任务数太少,作业执行速度变慢。它是最大的一个单位。
2、packet
packet是第二大的单位,它是client端向DataNode,或DataNode的PipLine之间传数据的基本单位,默认64KB。
3、chunk
chunk是最小的单位,它是client向DataNode,或DataNode的PipLine之间进行数据校验的基本单位,默认512Byte,因为用作校验,故每个chunk需要带有4Byte的校验位。所以实际每个chunk写入packet的大小为516Byte。由此可见真实数据与校验值数据的比值约为128 : 1。(即64*1024 / 512)
例如,在client端向DataNode传数据的时候,HDFSOutputStream会有一个chunk buff,写满一个chunk后,会计算校验和并写入当前的chunk。之后再把带有校验和的chunk写入packet,当一个packet写满后,packet会进入dataQueue队列,其他的DataNode就是从这个dataQueue获取client端上传的数据并存储的。同时一个DataNode成功存储一个packet后之后会返回一个ack packet,放入ack Queue中。
3.2、HDFS的block为什么是128M?增大或减小有什么影响?
1、首先先来了解几个概念
- 寻址时间:HDFS中找到目标文件block块所花费的时间。
- 原理:文件块越大,寻址时间越短,但磁盘传输时间越长;文件块越小,寻址时间越长,但磁盘传输时间越短。
2、为什么block不能设置过大,也不能设置过小
- 如果块设置过大,一方面从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时,变得非常慢;另一方面,MapReduce中的map任务通常一次只处理一个块中的数据,如果块过大运行速度也会很慢。
- 如果设置过小,一方面存放大量小文件会占用NameNode中大量内存来存储元数据,而NameNode的内存是有限的,不可取;另一方面块过小,寻址时间增长,导致程序一直在找block的开始位置。因此,块适当设置大一些,减少寻址时间,传输一个有多个块组成的文件的时间主要取决于磁盘的传输速度。
3、块大小多少合适
- 如果寻址时间约为10ms,而传输速率为100MB/s,为了使寻址时间仅占传输时间的1%,我们要将块大小设置约为100MB。默认的块大小128MB。
块的大小:10ms x 100 x 100M/s = 100M,如图
- 如果增加文件块大小,那就需要增加磁盘的传输速率。
比如,磁盘传输速率为200MB/s时,一般设定block大小为256MB;磁盘传输速率为400MB/s时,一般设定 block大小为512MB。
3.3、HDFS为什么文件要分块进行存储管理
HDFS中文件都是以 block 块的方式存放在 HDFS 文件系统当中。好处有很多,引用Hadoop权威指南中的概述:
1. 一个文件有可能大于集群中任意一个磁盘,引入块机制,可以很好的解决这个问题
2. 使用块作为文件存储的逻辑单位可以简化存储子系统
3. 块非常适合用于数据备份进而提供数据容错能力和数据的可用性。
四、HDFS架构
4.1 Hadoop 1.x HDFS架构:未做高可用时
1、Master/Slave架构
HDFS-1.0架构
2、存在的问题
1)HDFS存在的问题
- NameNode单点故障,难以应用于在线场景
- NameNode压力过大,且内存受限,影响系统扩展性
2)MapReduce存在的问题
- JobTracker访问压力过大,影响系统扩展性
- 难以支持除MapReduce之外的计算框架,比如Spark等
4.2 Hadoop 2.x HDFS架构:高可用
架构解析:
1、主备NameNode
1)解决单点故障
- 主NameNode对外提供服务,备NameNode同步主NameNode元数据,以待切换
- 所有DataNode同时向两个NameNode汇报数据块信息
2)两种切换选择
- 手动切换
- 通过命令实现主备之间的切换,可以用HDFS升级等场合
- 自动切换:基于Zookeeper实现
- Zookeeper Failover Controller:监控NameNode健康状态
- 向Zookeepe注册NameNode
- NameNode挂掉后,ZKFC为NameNode竞争锁,获得ZKFC锁的NameNode变为Active
4.3 架构组件
1、Client:客户端
- 文件切分。文件上传 HDFS 的时候,Client 将文件切分成一个一个的Block,然后进行存储
- 与NameNode交互,获取文件的位置信息
- 与DataNode交互,读取或者写入数据
- Client提供一些命令来管理HDFS,比如NameNode格式化
- Client可以通过一些命令来访问HDFS,比如对HDFS增删查改操作
- NameNode
维护着文件的元数据,包括文件名、副本数、文件的 BlockId,以及 block 所在的服务器,会接受来自 Client 端的读写请求,和DataNode 的 Block 信息上报。
1)Master节点:主管,管理者
- 管理HDFS的名称空间
- 配置副本策略
- 管理数据块(Block)映射信息
- 处理客户端的读写请求
2)NameNode有一个虚拟的文件系统,类似Linux的根目录
3、DataNode
HDFS 的工作节点,他们根据客户端或者是 NameNode 的调度存储和检索数据,并且定期向 NameNode 发送他们所存储的块(block)的列表。
1)Slave节点
- 存储实际的数据块
- 执行数据块的读/写操作
2)NameNode下达命令,DataNode执行实际的操作
4、SecondaryNameNode
用来监控HDFS状态的辅助后台程序。Secondary NameNode不同于NameNode,它不接受或者记录任何实时的数据变化,但是,它会与NameNode进行通信,以便定期地保存HDFS元数据快照。由于NameNode是单点的,通过Secondary NameNode的快照功能,可以将NameNode的宕机时间和数据损失降低到最小。同时,如果NameNode中的Fsimage到达了指定容量,则会和Secondary NameNode进行通信,进行checkoint操作。
1)SecondaryNameNode是NameNode的冷备份
- 合并FsImage和Edits并发回给NameNode
- FsImage:元数据镜像文件(文件系统的目录树)
- Edits:元数据操作日志(针对文件系统做的修改操作记录)
2)SecondaryNameNode的合并流程解析
合并图解
合并流程:
- NameNode将元数据镜像文件FsImage落成磁盘文件,新的操作日志会写入到Edits文件中, 当达到Checkpoint出发的条件时,SecondaryNameNode开始工作
- 当触发一个Checkpoint操作时,NameNode会生成一个新的Edits,即图中的edits.new文件, 同时SecondaryNameNode会将edits(日志滚动前的edits)和fsimage复制到本地
- SecondaryNameNode加载编辑日志和镜像文件到内存,并合并成Fsimage.ckpt
- 拷贝fsimage.ckpt到NameNode,NameNode将fsimage.ckpt重新命名成fsimage
- 等待下一次Checkpoint触发SecondaryNameNode进行工作,一直这样循环操作
小结:
生成新的edits文件->复制fsimage和edits文件到SecondaryNameNode ->将编辑日志和镜像文件合并成Fsimage.ckpt->拷贝fsimage.ckpt到NameNode,重新命名为fsimage ->达到触发条件SecondaryNameNode再次开始运行
Checkpoint触发条件
- fs.checkpoint.period:指定连续两次检查点的最大时间间隔,默认1h
- fs.checkpoint.size:默认128M,edit日志文件大于这个值则强制触发
- 配置文件:core-site.xml
3)辅助NameNode,分担其工作量,比如定期合并Fsimage和Edits,并推送给NameNode
4)在紧急情况下,可辅助恢复NameNode
5)SecondaryNameNode是Hadoop 1.x中HDFS HA的一个解决方案
6)冷备份和热备份的区别
- 热备份能随时顶替挂掉的节点工作
- 冷备份在节点挂掉后,只是节点挂掉之前的部分元数据
5、JournalNode:负责两个 NameNode 高可用时的数据同步保证数据一致,存放 NameNode 的 editlog 文件(元数据),部署在任意节点,奇数个。
6、JobTracker:JobTracker后台程序用来连接应用程序与Hadoop。用户代码提交到集群以后,由JobTracker决定哪个文件将被处理,并且为 不同的task分配节点。同时,它还监控所有的task,一旦某个task失败了,JobTracker就会自动重新开启这个task,在大多数情况下这 个task会被放在不用的节点上。每个Hadoop集群只有一个JobTracker,一般运行在集群的Master节点上。
7、TaskTracker:TaskTracker与负责存储数据的DataNode相结合,其处理结构上也遵循主/从架构。JobTracker位于主节点,统领 MapReduce工作;而TaskTrackers位于从节点,独立管理各自的task。每个TaskTracker负责独立执行具体的task,而 JobTracker负责分配task。
4.4 HDSF 1.x与HDFS 2.x的区别
五、HDFS工作原理
5.1 简易版本
1、HDFS写数据流程
1)客户端通过Distributed FileSystem模块向NameNode请求上传文件,NameNode检查目标文件是否已存在,父目录是否存在。
2)NameNode返回是否可以上传。
3)客户端请求第一个 block上传到哪几个datanode服务器上。
4)NameNode返回3个datanode节点,分别为dn1、dn2、dn3。
5)客户端通过FSDataOutputStream模块请求dn1上传数据,dn1收到请求会继续调用dn2,然后dn2调用dn3,将这个通信管道建立完成。
6)dn1、dn2、dn3逐级应答客户端。
7)客户端开始往dn1上传第一个block(先从磁盘读取数据放到一个本地内存缓存),以packet为单位,dn1收到一个packet就会传给dn2,dn2传给dn3;dn1每传一个packet会放入一个应答队列等待应答。
8)当一个block传输完成之后,客户端再次请求NameNode上传第二个block的服务器。(重复执行3-7步)。
2、HDFS读数据流程
1)客户端通过Distributed FileSystem向NameNode请求下载文件,NameNode通过查询元数据,找到文件块所在的DataNode地址。
2)挑选一台DataNode(就近原则,然后随机)服务器,请求读取数据。
3)DataNode开始传输数据给客户端(从磁盘里面读取数据输入流,以packet为单位来做校验)。
4)客户端以packet为单位接收,先在本地缓存,然后写入目标文件。
5.2 详细版本
1、HDFS写数据流程
1)Client将FileA按128M分块。分成两块,block1和Block2;
2)Client向nameNode发送写数据请求,如图蓝色虚线①------>。
3)NameNode节点,记录block信息。并返回可用的DataNode,如粉色虚线②------->。
Block1: host2,host1,host6 Block2: host7,host3,host4
4)client向DataNode发送block1;发送过程是以流式写入。
流式写入过程
(1)将64M的block1按64k的package划分;
(2)然后将第一个package发送给host2;
(3)host2接收完后,将第一个package发送给host1,同时client向host2发送第二个package;
(4)host1接收完第一个package后,发送给host6,同时接收host2发来的第二个package。
(5)以此类推,如图红线实线所示,直到将block1发送完毕。
(6)host2,host1,host6向NameNode,host2向Client发送通知,说“消息发送完了”。如图粉红颜色实线所示。
(7)client收到host2发来的消息后,向namenode发送消息,说我写完了。这样就完成了。如图黄色粗实线。
(8)发送完block1后,再向host7,host3,host4发送block2,如图蓝色实线所示。
(9)发送完block2后,host7,host3,host4向NameNode,host7向Client发送通知,如图浅绿色实线所示。
(10)client向NameNode发送消息,说我写完了,如图黄色粗实线。。。这样就完毕了。
2、HDFS读数据流程
1)client向namenode发送读请求。
2)namenode查看Metadata信息,返回fileA的block的位置。
block1:host2,host1,host6 block2:host7,host3,host4
3)block的位置是有先后顺序的,先读block1,再读block2。而且block1去host2上读取;然后block2,去host7上读取。
5.3 Java力度去分析写文件流程
1.调用客户端的对象DistributedFileSystem的create方法;
2.DistributedFileSystem会发起对namenode的一个RPC连接,请求创建一个文件,不包含关于block块的请求。namenode会执行各种各样的检查,确保要创建的文件不存在,并且客户端有创建文件的权限。如果检查通过,namenode会创建一个文件(在edits中),否则创建失败,客户端抛异常IOException。
3.DistributedFileSystem返回一个FSDataOutputStream对象给客户端用于写数据。FSDataOutputStream封装了一个DFSOutputStream对象负责客户端跟datanode以及namenode的通信。
4.FSDataOutputStream对象将数据切分为小的数据包(64kb),并写入到一个内部队列(“数据队列”)。DataStreamer会读取其中内容,并请求namenode返回一个datanode列表来存储当前block副本。列表中的datanode会形成管线,DataStreamer将数据包发送给管线中的第一个datanode,第一个datanode将接收到的数据发送给第二个datanode,第二个发送给第三个。。。
5.DFSOoutputStream维护着一个数据包的队列,这的数据包是需要写入到datanode中的,该队列称为确认队列。当一个数据包在管线中所有datanode中写入完成,就从ack队列中移除该数据包。队列是在客户端维护的。
6.如果在数据写入期间datanode发生故障,则执行以下操作
a、关闭管线,把确认队列中的所有包都添加回数据队列的最前端,以保证故障节点下游的datanode不会漏掉任何一个数据包。
b、为存储在另一正常datanode的当前数据块指定一个新的标志,并将该标志传送给namenode,以便故障datanode在恢复后可以删除存储的部分数据块。
c、如果在数据写入期间datanode发生故障,待确认消息队列迟迟得不到确认消息,这时会有一个超时时间,超过这个时间,从管线中删除故障数据节点并且把余下的数据块写入管线中另外两个正常的datanode(也就是这两个节点组成新的管线并且blockID的值要发生变化,另外注意正常的节点中包括之前上传的部分小的64K文件,所以需要对其就行一个统计,确认我现在数到第几个包了,避免重复提交)。namenode在检测到副本数量不足时,会在另一个节点上创建新的副本。
d、后续的数据块继续正常接受处理。
7.在一个块被写入期间可能会有多个datanode同时发生故障,但非常少见。只要设置了dfs.replication.min的副本数(默认为1),写操作就会成功,并且这个块可以在集群中异步复制,直到达到其目标副本数(dfs.replication默认值为3)。
8.如果有多个block,则会反复从步骤4开始执行。
9.当客户端完成了数据的传输,调用数据流的close方法。该方法将数据队列中的剩余数据包写到datanode的管线并等待管线的确认
10.客户端收到管线中所有正常datanode的确认消息后,通知namenode文件写完了。
11.客户端完成数据的写入后,对数据流调用close方法。该操作将剩余的所有数据包写入datanode管线,并在联系到namenode且发送文件写入完成信号之前,等待确认。
namenode已经知道文件由哪些块组成,所以它在返回成功前只需要等待数据块进行最小量的复制。
5.4 HDFS副本存放机制
1、在HDFS中,一个文件会被拆分为一个或多个数据块。默认情况下,每个数据块都会有3个副本。每个副本都会被存放在不同的机器上,而且每一个副本都有自己唯一的编号。
DataNodes
NameNode节点选择一个DataNode节点去存储block副本的过程就叫做副本存放,这个过程的策略其实就是在可靠性和读写带宽间的权衡。
2、配备了机架感知
Hadoop的副本放置策略在可靠性(副本在不同机架)和带宽(只需跨越一个机架)中做了一个很好的平衡
Hadoop3.x副本结点选择:
- 由上图可知,第一个副本在Client所处的节点上。如果客户端在集群外,随机选一个。
- 第二个副本在另一个机架的随机一个节点。
- 第三个副本在第二个副本所在机架的随机节点。
- 更多副本:随机节点
3、未配备机架感知
- 三个DataNode机器的选择完全是随机的
5.5 HDFS的机架感知技术
1、确定节点所在的机架?
原理如下:
- 当DataNode注册时和heartbeat时,会把DataNode的IP作为参数传入,返回信息为此DataNode的机架信息。
- 如果没有参数配置,DataNode统一为默认的机架/default-rack
2、默认关闭
3、hadoop-site.xml配置:topology.script.file.name
配置选项的value指定为一个可执行脚本程序
- 脚本的编写需要充分了解真实的网络拓扑和机架信息
通过该脚本能够将机器的IP地址正确的映射到相应的机架上去
5.6 HDFS安全模式
namenode启动的时候,首先将映像文件(fsimage)或入内存,并执行编相日志(edits)中的各项操作。
一旦在内存中成功建立文件系统元数据的映时,则创建一个新的fsimage文件(这个操作不需要SecondaryNameNode)和一个空的编辑日志。
此刻namenode运行在安全模式,即namenode的文件系统对于客服端来说是只读的。(显示目录,显示文件内容等。写、制除、重命名都会失败)
在此阶段Nameode收集各个datanode的报告,当数据块达到最小副本数以上时,会被认为是“安全”的,在一定比例(可设置)的故据块被确定为“安全”后,再过若干时间,安全模式结束。
当检测到副本故不足的故据块时,该块会被复制直到达到最小副本故,系统中数据块的位置并不是由nanenode维护的,而是以块列表形式存铑在datanode中。
六、HDFS文件存储格式
hdfs 文件存储格式分为两大类:行存储和列存储
行存储:
行存储的写入是一次完成,在写入上有很大优势。将一整行存储在一起,是一种连续的存储方式,可以保证写入过程的成功或者失败,保证数据完整性。查询时如果只需要某列信息,也必须把整行都读入内存当中,在内存中对冗余数据进行过滤。没有索引的查询使用大量I/O,通过建立索引加快查询效率。因为在一行记录中可能存在多种类型的数据,数据解析需要在多种类型之间频繁转换,这个操作消耗CPU,增加了解析的时间。
列存储:
列存储由于需要把一行记录拆分成单列保存,写入次数明显比行存储多,实际时间消耗会更大。列存储会把文件切割成若干列,读取时只需要返回对应列的数据。由于每一列中的数据类型相同所以可以根据数据类型选择适合的编码和压缩格式
对照表格
操作类型 | 行存储 | 列存储 |
hdfs格式 | TextFile,Sequence,MapFile,Avro | Parquet , RCFile,ORCFile |
存储 | 连续存储 | 按列存储 |
写入操作 | 一次写入整行,效率高 | 一行数据分列多次写入,效率较低 |
查询操作 | 整行读取,内存过滤冗余行 | 按列读取 |
压缩 | 每行数据类型不同,压缩性能较差 | 每列数据类型相同,压缩性能好 |
使用场景 | OLTP | OLAP |
1) textfile
textfile为默认格式,加载速度最快,可以采用Gzip进行压缩,压缩后的文件无法split。在检索时磁盘开销大,数据解析开销大。
2) SequenceFile
SequenceFile是Hadoop提供的一种二进制文件,以[Key,Value]的形式序列化到文件中。可以把SequenceFile当做是一个容器,把所有的文件打包到SequenceFile类中可以高效的对小文件进行存储和处理。
SequenceFile主要由一个Header后跟多条Record组成。Header主要包含了Keyname和valuename,还包含了一些同步标识,用于快速定位到记录的边界。每条Record以键值对的方式进行存储,内容包括:记录长度、Key长度、Key值和value值,Value的结构取决于该记录是否被压缩。
SequenceFile支持三种记录存储方式:
- 无压缩, io效率较差. 相比压缩, 不压缩的情况下没有什么优势.
- 记录级压缩, 对每条记录都压缩. 这种压缩效率比较一般.
- 块级压缩, 这里的块不同于hdfs中的块的概念. 这种方式会将达到指定块大小的二进制数据压缩为一个块. 相对记录级压缩, 块级压缩拥有更 高的压缩效率. 一般来说使用SequenceFile都会使用块级压缩.
但是SequenceFile只支持Java, SequenceFile一般用来作为小文件的容器使用, 防止小文件占用过多的NameNode内存空间来存储其在DataNode位置的元数据。
3) RCFile
在一般的列存储中,会将不同的列分开存储,有时候存在一个表的某些列不在同一个HDFS块上,所以在查询的时候,Hive重组列的过程会浪费很多IO开销。
RCFile是Hive推出的一种专门面向列的数据格式。存储方式为数据按行分块,每块按照列存储的行列混合模式,具有压缩高,列存取快的特点。需要说明的是,RCFile在map阶段从远端拷贝仍然是拷贝整个数据块,并且拷贝到本地目录后RCFile并不是真正直接跳过不需要的列,而是通过扫描每一个行组的头部信息实现,但是在整个block级别的头部并没有定义每个列从哪个行组起始到哪个行组结束,所以读取全量数据的操作其性能比sequencefile低。
RCFile先将数据按行划分成行组,大小默认是4MB,行组内包括16字节的HDFS同步块信息,主要是为了区分同一个HDFS块上的相邻行组;元数据的头部信息主要包括该行组内的存储的行数、列的字段信息等等;在Row Group内部,再将数据按列划分存储。其结构如下:
4) ORCfile
是RCfile的升级版,支持文件切分,将数据划分为默认大小为250MB的stripe(条带),每个stripe包含索引,数据和footer。可以支持复杂的数据结构(比如Map等)
5) Parquet
parquet基于Google的dremel,擅长处理深度嵌套的数据(有点类似于嵌套多层的json格式),parquet会将嵌套结构整合为平面列存储。
6) Avro
Avro 是 Hadoop 中的一个子项目,也是 Apache 中一个独立的项目,Avro 是一个基于二进制数据传输高性能的中间件。在 Hadoop 的其他项目中,例如 HBase 和 Hive 的 Client 端与服务端的数据传输也采用了这个工具。Avro是一个语言无关的数据序列化的系统,它的出现主要是为了解决Writables缺少跨语言移植的缺陷。Avro将模式存储在文件头中,所以每个文件都是自描述的,而且Avro还支持模式演进(schema evolution),也就是说,读取文件的模式不需要与写入文件的模式严格匹配,当有新需求时,可以在模式中加入新的字段。Avro支持分片, 即使是进行Gzip压缩之后
七、HDFS文件压缩算法
在进行文件压缩算法的选择,首先要先考虑一下几个问题
- 文件和压缩算法的组合是否支持可分片, MapReduce在读取数据的时候需要并行, 这就要求压缩后的文件可以分片读取。
- CPU资源 一般来说压缩效率越高的算法对IO和存储利用率的提升越有促进作用, 但也会更高的消耗CPU资源。所以需要寻求一个平衡点。
- 共通性, 文件格式是否支持多种语言, 服务的读取。比如Hadoop主要的序列化格式为Writables, 但是Writables只支持Java, 所以后面衍生出了Avro, Thrift等格式。还如OrcFile是对Hive设计的一种列式存储格式, 但是他不支持Impala, 数据的共用性受到了制约。
- 错误处理能力, 有的文件的某一部分坏掉之后会影响整个表, 有的只会影响其后的数据, 有的只会影响坏掉数据块本身(Avro)。
- 读取和载入效率, RCFile的载入速度慢, 但是查询相应速度快, 相对更适合数据仓库一次插入多次读取的特性。
1) Gzip压缩
- 优点:压缩率比较高,压缩/解压速度也比较快,hadoop本身支持。
- 缺点:不支持分片。
- 应用场景:当每个文件压缩之后在1个block块大小内,可以考虑用gzip压缩格式。
2) lzo压缩
- 优点:压缩/解压速度也比较快,合理的压缩率,支持分片,是Hadoop中最流行的压缩格式,支持Hadoop native库。
- 缺点:压缩率比gzip要低一些,Hadoop本身不支持,需要安装,如果支持分片需要建立索引,还需要指定inputformat改为lzo格式。
- 应用场景:一个很大的文本文件,压缩之后还大于200M以上的可以考虑,而且单个文件越大,lzo优点越明显。
3) snappy压缩
- 优点:支持Hadoop native库,高速压缩速度和合理的压缩率。
- 缺点:不支持分片,压缩率比gzip要低,Hadoop本身不支持,需要安装。
- 应用场景:当MapReduce作业的map输出的数据比较大的时候,作为map到reduce的中间数据的压缩格式。
4) bzip2压缩
- 优点:支持分片,具有很高的压缩率,比gzip压缩率都高,Hadoop本身支持,但不支持native。
- 缺点:压缩/解压速度慢,不支持Hadoop native库。
- 应用场景:适合对速度要求不高,但需要较高的压缩率的时候,可以作为mapreduce作业的输出格式,输出之后的数据比较大,处理之后的数据需要压缩存档减少磁盘空间并且以后数据用得比较少的情况。
八、HDFS写文件时节点挂机处理
在文件写入过程中不会重新分配datanode。如果写入时一个datanode挂掉,会将已经写入的数据放置到数据队列的顶部,并将挂掉的datanode移出通信管道,将数据写入到剩余的datanode。在写入结束后, namenode会收集datanode的信息,发现此文件的副本没有达到配置的要求(default=3),然后寻找一个datanode保存副本。
九、HDFS租约机制
在HDFS中,当每次客户端用户往某个文件中写入数据的时候,为了保持数据的一致性,此时其它客户端程序是不允许向此文件同时写入数据的。那么HDFS是如何做到这一点的呢?答案是租约(Lease)。租约是HDFS给予客户端的一个写文件操作的临时许可证,无此证件者将不被允许操作此文件。客户端在每次读写HDFS文件的时候获取租约对文件进行读写,文件读取完毕了,然后再释放此租约。
- 每个客户端用户持有一个租约。
- 每个租约内部包含有一个租约持有者信息,还有此租约对应的文件Id列表,表示当前租约持有者正在写这些文件Id对应的文件。
- 每个租约内包含有一个最新近更新时间,最近更新时间将会决定此租约是否已过期。过期的租约会导致租约持有者无法继续执行写数据到文件中,除非进行租约的更新。
租约管理:
HDFS租约管理的操作集中在一个类上:LeaseManager。运行在NameNode的服务中。租约类的定义就是在LeaseManager中的。在LeaseManager租约管理器中,它所做的事情主要归纳为两类。
- 第一个,维护HDFS内部当前所有的租约,并以多种映射关系进行保存。保存的映射关系分为如下3种:1)租约持有者对租约的映射关系。2)文件Id对租约的映射关系。3)按照时间排序进行租约集合的保存,此关系并不是一种映射关系。
- 第二个是定期释放过期的租约对象。这个操作可以避免文件租约长期不释放导致其他客户端文件无法写文件的问题。因为在某些异常情况下,客户端程序可能在写完文件后,没有正常关闭文件,导致文件始终处于正在写的状态中,此文件在对应的租约中没有被真正的移除掉。
讲述完租约的概念以及管理之后,我们来分析租约的添加到释放的过程。以我们对于租约的一个传统概念应该是这样一个过程:首先在进行文件写操作时,进行租约的添加,然后操作结束之后,进行租约的释放。
十、HDFS安全模式
在NameNode主节点启动时,HDFS会首先进入安全模式,检查包括文件副本的数量、可用的datanode数量、集群可用block比例等参数。以上参数达到阈值(可配置)后,H即可视为系统达到安全标准,HDFS自动离开安全模式。在安全模式下,文件系统只接受读数据请求,而不接受删除、修改等变更请求。且文件block不能进行任何的副本复制操作,因此达到最小的副本数量要求是基于datanode启动时的状态来判定的,启动时不会再做任何复制(从而达到最小副本数量要求)
安全模式相关配置
系统什么时候才离开安全模式,需要满足哪些条件?可以根据以下配置内容进行确定 如果有必要,也可以通过命令强制离开安全模式。与安全模式相关的主要配置在hdfs-site.xml文件中,主要有下面几个属性
- dfs.namenode.replication.min: 最小的block副本数量,默认为1。
- dfs.namenode.safemode.threshold-pct: 副本数达到最小要求的block占系统总block数的百分比,当实际比例超过该配置后,才能离开安全模式(但是还需要其他条件也满足)。默认为0.999f,也就是说符合最小副本数要求的block占比超过99.9%时,并且其他条件也满足才能离开安全模式。如果小于等于0,则不会等待任何block副本达到要求即可离开。如果大于1,则永远处于安全模式。
- dfs.namenode.safemode.min.datanodes: 离开安全模式的最小可用datanode数量要求,默认为0。即所有datanode都不可用,仍然可以离开安全模式。
- dfs.namenode.safemode.extension: 集群可用block比例、可用datanode都达到要求之后,如果在extension配置的时间段之后依然能满足要求,此时集群才离开安全模式。单位为毫秒,默认为1。也就是当满足条件并且能够维持1毫秒之后,离开安全模式。这个配置主要是对集群的稳定程度做进一步的确认。避免达到要求后马上又不符合安全标准。
- 总结一下,要离开安全模式,需要满足以下条件:1)达到副本数量要求的block比例满足要求;2)可用的datanode节点数满足配置的数量要求;3) 1、2 两个条件满足后维持的时间达到配置的要求。
十一、HDFS负载均衡(Rebalance)
Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,例如:当集群内新增、删除节点,或者某个节点机器内硬盘存储达到饱和值。当数据不平衡时,Map任务可能会分配到没有存储数据的机器,这将导致网络带宽的消耗,也无法很好的进行本地计算。
当HDFS负载不均衡时,需要对HDFS进行数据的负载均衡调整,即对各节点机器上数据的存储分布进行调整。让数据均匀的分布在各个DataNode上,均衡IO性能,防止热点发生。进行数据的负载均衡调整,必须要满足如下原则:
- 数据平衡不能导致数据块减少,数据块备份丢失
- 管理员可以中止数据平衡进程
- 每次移动的数据量以及占用的网络资源,必须是可控的且不能影响namenode的正常工作
Rebalance
rebalance作用是为了使数据在集群中各节点的分布尽量均衡,rebalance是一个非自动的管理功能,在任意一台能够连接到HDFS的机器上命令行下输入 hadoop balancer [-threshold] 既会启动。如果集群处于不平衡状态,这个过程就会在不平衡的节点之间迁移数据,如果rebalance过程没有被打断的话,完成此次rebalance目标后过程会自动停止。
影响rebalance的参数
threshold 默认设置:10,参数取值范围:0-100 参数含义:判断集群是否平衡的阈值。如果没有达到则进行平衡任务,平衡过程中标准达到此阈值后退出。理论上,该参数设置的越小,整个集群就越平衡。
dfs.balance.bandwidthPerSec 默认设置:1048576(1M/S) 参数含义:Balancer运行时允许占用的带宽,默认为1M/S,如果宽带占用过低则影响均衡效率,宽带占用过高则影响HDFS正常任务的读写IO。
负载均衡过程
数据均衡过程的核心是一个数据均衡算法,该数据均衡算法将不断迭代数据均衡逻辑,直至集群内数据均衡为止。该数据均衡算法每次迭代的逻辑如下:
- Rebalance Server从Name Node中获取每一个Data Node磁盘使用情况。
- Rebalance Server计算哪些机器需要移动数据,哪些机器需要接受数据。
- Rebalance Server汇总需要移动的数据分布情况,计算具体数据块迁移路线图,确保网络内最短路径。
- 移动源DN的block到目标DN中,同时删除自己的block数据。
- 源DataNode向Rebalancing Server确认本次数据块迁移完成,并继续移动其他数据,一直到没有数据可以移动或者HDFS集群达到了平衡的标准为止,然后向namenode提交更新后的所有datanode信息。
十二、HDFS存储策略与异构存储
Hadoop从2.6.0版本开始支持异构存储,异构存储的意义在于HDFS中频繁访问的数据,可以将其保存在更高访问性能的存储介质(内存或SSD)上,提升其读写性能;对于几乎不会访问的数据,保存在机械硬盘或其他廉价存储上,降低成本。HDFS异构存储的配置需要用户对目录指定存储策略,即用户需要预先知道每个目录下的文件的访问热度:事先划分好冷热数据存储目录,设置好对应的存储策略,然后后续相应的程序在对应分类目录下写数据,自动继承父目录的存储策略
存储介质:
hdfs的存储策略依赖于底层的存储介质。hdfs支持的存储介质:
- ARCHIVE:高存储密度但耗电较少的存储介质,例如磁带,通常用来存储冷数据
- DISK:磁盘介质,这是HDFS最早支持的存储介质,目前是默认存储
- SSD:固态硬盘,是一种新型存储介质,目前被不少互联网公司使用
- RAM_DISK :数据被写入内存中,同时会往该存储介质中再(异步)写一份
存储介质配置:将对应的存储类型添加到dfs.datanode.data.dir的配置项中即可,配置的时候需要申明存储类型和对应目录,存储类型需要用中括号括起来,存储类型有[SSD]/[DISK]/[ARCHIVE]/[RAM_DISK],如果不指定存储类型,则默认就是DISK。
上面例子,前面12个盘,我没有设置存储类型,因为都是DISK,最后一个盘使用了SSD类型。
存储策略
存储策略可配置,可以设置全局的,也可以设置到某个文件夹。
- Lazypersist:一个副本保存在内存RAMDISK中,其余副本保存在磁盘中
- ALL_SSD:所有副本都保存在SSD中
- One_SSD:一个副本保存在SSD中,其余副本保存在磁盘中
- Hot:所有副本保存在磁盘中,这也是默认的存储策略
- Warm:一个副本保存在磁盘上,其余副本保存在归档存储上
- Cold:全部数据以ARCHIVE的方式保存
存储策略配置:
HDFS提供了专门的命令来设置对应的策略,命令使用方法如下:
- 查看策略帮助信息:hdfs storagepolicies -help
- 列出当前版本支持的存储策略:hdfs storagepolicies -listPolicies
- 设置对应路径的策略:hdfs storagepolicies -setStoragePolicy -path -policy
具体流程
- 在hdfs的配置文件hdfs-site.xml中配置对应的异构存储
- DataNode启动的时候从配置文件中读取对应的存储类型,以及容量情况,并通过心跳的形式不断的上报给NameNode。
- NameNode收到DataNode发送的关于存储类型、容量等内容的心跳包后,会进行处理,更新存储的相关内容。
- 写请求发到NameNode后,NameNode根据写请求具体的目录对应的存储策略选择对应的存储类型的DataNode进行写入操作。
十三、HDFS纠删码
为了数据的可靠性,HDFS通过多副本机制来保证。三副本冗余下,1TB的原始数据需要占用3TB的磁盘空间,存储利用率只有1/3。而且系统中大部分是使用频率非常低的冷数据,,给存储空间和网络带宽带来了很大的压力。因此,在保证可靠性的前提下如何提高存储利用率已成为当前HDFS面对的主要问题之一。
Hadoop 3.0 引入了纠删码技术(Erasure Coding),它可以提高50%以上的存储利用率,并且保证数据的可靠性。
纠删码技术简称EC,是一种编码容错技术。它通过对数据进行分块,然后计算出校验数据,使得各个部分的数据产生关联性。当一部分数据块丢失时,可以通过剩余的数据块和校验块计算出丢失的数据块。
- 无论文件大小都适用
缺点:
- 会影响一些位置敏感任务的性能,因为原先在一个节点上的块被分散到了多个不同的节点上
- 和多副本存储策略转换比较麻烦
优缺点
优点:
- 相比副本存储方式大大降低了存储资源和IO资源的使用;
- 通过XOR和RS算法保证数据安全,有效解决允许范围内数据块破碎和丢失导致的异常;
缺点:
- 恢复数据时需要去读其它数据块和奇偶校验码块数据,需要消耗IO和网络资源;
- EC算法编码和解密计算需要消耗CPU资源;
- 存储大数据,并且数据块较集中的节点运行作业负载会较高;
- EC文件不支持hflush, hsync, concat, setReplication, truncate, append 等操作。
总结
综合副本存储和EC存储优缺点,EC存储更适合用于存储备份数据和使用频率较少的非热点数据,副本存储更适用于存储需要追加写入和经常分析的热点数据。
十四、HDFS中小文件过多导致的问题与如何优化
14.1 小文件过多导致的问题
小文件是指文件size小于HDFS上block大小的文件。这样的文件会给hadoop的扩展性和性能带来严重问题。
- 首先,在HDFS中,任何block,文件或者目录在内存中均以对象的形式存储,每个对象约占150byte,如果有1000 0000个小文件,每个文件占用一个block,则namenode大约需要2G空间。如果存储1亿个文件,则namenode需要20G空间。这样namenode内存容量严重制约了集群的扩展。
- 其次,访问大量小文件速度远远小于访问几个大文件。HDFS最初是为流式访问大文件开发的,如果访问大量小文件,需要不断的从一个datanode跳到另一个datanode,严重影响性能。
- 最后,处理大量小文件速度远远小于处理同等大小的大文件的速度。每一个小文件要占用一个slot,而task启动将耗费大量时间甚至大部分时间都耗费在启动task和释放task上。
14.2 优化方案
1) 使用HAR(Hadoop Archives)
为了缓解大量小文件带给namenode内存的压力,Hadoop 0.18.0引入了Hadoop Archives(HAR files),其本质就是在HDFS之上构建一个分层文件系统。通过执行hadoop archive 命令就可以创建一个HAR文件。在命令行下,用户可使用一个以har://开头的URL就可以访问HAR文件中的小文件。使用HAR files可以减少HDFS中的文件数量。下图为HAR文件的文件结构,可以看出来访问一个指定的小文件需要访问两层索引文件才能获取小文件在HAR文件中的存储位置,因此,访问一个HAR文件的效率可能会比直接访问HDFS文件要低。对于一个mapreduce任务来说,如果使用HAR文件作为其输入,仍旧是其中每个小文件对应一个map task,效率低下。所以,HAR files最好是用于文件归档。
2) 使用sequencefile
SequenceFile核心是以文件名为key,文件内容为value组织小文件。10000个100KB的小文件,可以编写程序将这些文件放到一个SequenceFile文件,然后就以数据流的方式处理这些文件,也可以使用MapReduce进行处理。一个SequenceFile是可分割的,所以MapReduce可将文件切分成块,每一块独立操作。不像HAR,SequenceFile支持压缩。在大多数情况下,以block为单位进行压缩是最好的选择,因为一个block包含多条记录,压缩作用在block之上,比reduce压缩方式(一条一条记录进行压缩)的压缩比高。把已有的数据转存为SequenceFile比较慢。比起先写小文件,再将小文件写入SequenceFile,一个更好的选择是直接将数据写入一个SequenceFile文件,省去小文件作为中间媒介。
3) MapReduce过程中使用CombineFileInputFormat
CombineFileInputFormat是一种新的inputformat,用于将多个文件合并成一个单独的split,另外,它会考虑数据的存储位置。
十五、HDFS调优技巧
操作系统级别
- 优化文件系统:推荐使用EXT4和XFS文件系统,相比较而言,更推荐后者,因为XFS已经帮我们做了大量的优化。为服务器存储目录挂载时添加noatime属性
- 调大预读缓冲:预读技术可以有效的减少磁盘寻道次数和应用的I/O等待时间,增加Linux文件系统预读缓冲区的大小(默认为256 sectors,128KB),可以明显提高顺序文件的读性能,建议调整到1024或2048 sectors。预读缓冲区的设置可以通过blockdev命令来完成。
- 避免使用swap分区:将Hadoop守护进程的数据交换到磁盘的行为可能会导致操作超时。
集群优化
- 启用HDFS的机架感应功能。启用机架感应功能可以使HDFS优化数据块备份的分布,增强HDFS的性能和可靠性。
- 归档:Hdfs归档相当于把所有文件归档在一个文件夹里面了,该文件夹以.har命名的。当有很多的小文件时,可以通过归档来解决
- 开启压缩,可以降低HDFS的存储空间,提升文件IO
- 开启Short-CircuitLocal Read:Short Circuit策略允许客户端绕过DataNode直接读取本地数据
配置优化
- HDFS提供了十分丰富的配置选项,几乎每个HDFS配置项都具有默认值,一些涉及性能的配置项的默认值一般都偏于保守。根据业务需求和服务器配置合理设置这些选项可以有效提高HDFS的性能。
- dfs.namenode.handler.count NameNode中用于处理RPC调用的线程数,默认为10。对于较大的集群和配置较好的服务器,可适当增加这个数值来提升NameNode RPC服务的并发度。
- dfs.datanode.handler.count DataNode中用于处理RPC调用的线程数,默认为3。可适当增加这个数值来提升DataNode RPC服务的并发度
- dfs.replication 副本数,默认值为3,对于一些重要数据,可适当增加备份数。如果数据不是很重要的话,你可以设置成1份。
- dfs.datanode.data.dir 推荐设置多个磁盘目录,以增加磁盘IO的性能,多个目录用逗号进行分隔。
- hadoop.tmp.dir Hadoop临时目录,默认为系统目录/tmp。在每个磁盘上都建立一个临时目录,可提高HDFS和MapReduce的I/O效率。
- io.file.buffer.size HDFS文件缓冲区大小,默认为4096(即4K)。推荐值:131072(128K)。
- dfs.datanode.max.xcievers datanode 可同時处理的最大文件数量,推荐将这个值调大,默认是256,最大值可以配置为65535。
- 避免脏读写操作,开启以下配置 dfs.namenode.avoid.read.stale.datanode。dfs.namenode.avoid.write.stale.datanode
- fs.trash.interval HDFS清理回收站的时间周期,单位为分钟。默认为0,表示不使用回收站特性。推荐开启它,时间自定义