HDFS 作为一种文件系统,当然也需要有‘block’的概念。不过HDFS的block一般比较大,默认为128MB。与普通的管理单个磁盘的文件系统一样,HDFS也将文件分割成block,每个block都作为一个独立的单元分别保存。不同点在于,在HDFS中,小于block的文件不会占用一个block的空间。(比如,文件大小为1MB,那么它会占用一个HDFS的block,但是只使用底层磁盘1MB的空间,而不是128MB。)
block为什么设置成128M:
如果块设置过大
从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时,变得非常慢;
mapreduce中的map任务通常一次只处理一个块中的数据,如果块过大运行速度也会很慢。
如果块设置过小
存放大量小文件会占用NameNode中大量内存来存储元数据,而NameNode的内存是有限的,不可取;
文件块过小,寻址时间增大,导致程序一直在找block的开始位置。
因而,块适当设置大一些,减少寻址时间,传输一个由多个块组成的文件的时间主要取决于磁盘的传输速率。
为什么默认是128M
HDFS中平均寻址时间大概为10ms;
经过前人的大量测试发现,寻址时间为传输时间的1%时,为最佳状态;
所以最佳传输时间为10ms/0.01=1000ms=1s
目前磁盘的传输速率普遍为100MB/s;
计算出最佳block大小:100MB/s x 1s = 100MB
所以我们设定block大小为128MB。
实际在工业生产中, 磁盘传输速率为200MB/s时,一般设定block大小为256MB 磁盘传输速率为400MB/s时,一般设定block大小为512MB
HDFS 采用Master/Slave的架构来存储数据,这种架构主要由四个部分组成,分别为HDFS Client、NameNode、DataNode和Secondary NameNode。一个HDFS集群是由一个NameNode和一定数目的DataNode组成的。NameNode是一个中心服务器,负责管理文件系统的名字空间 (Namespace )及客户端对文件的访问。集群中的DataNode一般是一个节点运行一个DataNode进程,负责管理它所在节点上的存储。
副本选址策略
namenode如何选择在那个datanode存储副本,这里需要对可靠性、写入带宽和读取带宽进行权衡。
副本的选址对HDFS的可靠性和性能是起到关键作用的。机架感知副本配置策略的目的是提高可靠性、可用性和网络带宽的利用率。运行在集群计算机的大型HDFS实例一般是分布在许多机架上。两个不同机架上的节点的通讯必须经过交换机。在大多数情况下,同一个机架上的不同机器之间的网络带宽要优于不同机架上的机器的。
通常情况下,当复制因子为3时,HDFS的副本放置策略是将一个副本放在本机架的一个节点上,将另一个副本放在本机架的另一个节点,最后一个副本放在不同机架的不同节点上。该策略减少机架内部的传输以提高写的性能。这个策略提高了写性能而不影响数据可靠性和读性能。为了最大限度地减少全局带宽消耗和读取延迟,HDFS试图让读取者的读取需求离副本最近。
HDFS不擅长的事情
低时间延迟的数据访问:要求低时间延迟数据访问的应用,例如几十毫秒范围,不适合在HDFS上运行。目前,对于低延迟的访问需求,HBase是更好的选择。
安全模式
在启动时,NameNode进入一个特殊的状态称之为安全模式。当NameNode进入安全模式之后数据块的复制将不会发生。NameNode接收来自DataNode的心跳和数据块报告。数据块报告包含正在运行的DataNode上的数据块信息集合。每个块都指定了最小副本数。一个数据块如果被NameNode检查确保它满足最小副本数,那么它被认为是安全的。
NameNode存储着HDFS的命名空间。NmaeNode使用一个称之为EditLog的事务日志持续地记录发生在文件系统元数据的每一个改变。NameNode在它本地的系统中用一个文件来存储EditLog。整个文件系统命名空间,包括blocks的映射关系和文件系统属性,将储存在一个叫FsImage的文件。FsImage也是储存在NameNode所在的本地文件系统中。
NameNode在内存中保存着整个文件系统命名空间的图像和文件映射关系。当Namenode启动时,它从硬盘中读取Edits和FsImage,将所有Edits中的事务作用在内存中的FsImage上,并将这个新版本的FsImage从内存中保存到本地磁盘上,然后删除旧的Edits,因为这个旧的Edits的事务都已经作用在FsImage上了。这个过程称为一个检查点(checkpoint)。只有当NameNode启动时会执行一次。
心跳机制
namenode怎么 知道datanode的存活状态?datanode每隔一定时间向namenode发送一次心跳报告,目的就是告诉namenode自己的存活状态。
这个时间间隔默认是3秒,在hdfs-default.xml中设置,由参数dfs.heartbeat.interval:
namenode什么时候判定datanode死亡?
datanode每隔3秒向namenode发送心跳报告,如果namenode连续10次没有收到datanode的心跳报告,则认为datanode可能死了,但是并没有断定其死亡(namenode还需去验证)。这个时候,namenode向datanode发送一次检查,发送一次检查的时间是5min,如果一次检查没有返回信息,namenode还会再进行一次检查,如果再没有收到信息,namenode就认为该datanode死亡。
所以,namenode最终判断datanode死亡(宕机)需要103+560*2 = 630秒。
容错
读的过程中,有可能失败的原因:
这两种情况,都会从 block 的其他备份所在的 DataNode 继续读取数据,不过如果是 block 出问题还会上报给 NameNode, NameNode 会标记该 block 已经损坏,然后复制 block 达到预期设置的文件备份数 。
简单总结如下:
容错
在写数据的过程中,如果其中一个 DataNode 节点写失败了会怎样?
1) 管道关闭。
2)正常的datanode上的当前block会有一个新ID,并将该ID传送给namenode,以便失败的datanode在恢复后可以删除那个不完整的block。
3) 失败的datanode会被移出管道,余下的数据块继续写入管道的其他两个正常的datanode。
4) namenode会标记这个block的副本个数少于指定值。block的副本会稍后在另一个datanode创建。
5)有些时候多个datanode会失败,但非常少见。只要dfs.replication.min(缺省是1)个datanode成功了,整个写入过程就算成功。缺少的副本会在集群中异步的复制,直到达到正常的副本数。
6. 当client完成了所有block的写入后,调用FSDataOutputStream的close()方法关闭文件。
7. FSDataOutputStream通知namenode写文件结束。
六.HDFS数据完整性
用户肯定都希望系统在存储和处理数据时,数据不会有任何丢失或损坏。但是,受网络不稳定、硬件损坏等因素,IO操作过程中难免会出现数据丢失或脏数据,难免会出现数据丢失或脏数据,数据传输的量越大,出现错误的概率就越高。
检测数据是否损坏的常见措施是,在数据第一次引入系统时计算校验和(checksum)并存储,在数据进行传输后再次计算校验和进行对比,如果计算所得的新校验和和原来的校验和不匹配,就认为数据已损坏。但该技术并不能修复数据——它只能检测出数据错误。(这正是不使用低端硬件的原因。具体说来,一定要使用ECC内存。)注意,校验和也是可能损坏的,不只是数据,但由于校验和比数据小得多,所以损坏的可能性非常小。
(1)对本地文件I/O的检查
在Hadoop中,本地文件系统的数据完整性由客户端负责。重点在于存车读取文件时进行校验和的处理。
具体做法是:每当hadoop创建文件a时,hadoop就会同时在同一个文件夹下创建隐藏文件.a.crc,这个文件记录了 文件a的校验和。针对数据文件的大小,每512个字节会生成一个32位的校验和(4字节),可以在src/core/core-default.xml中通过修改io.bytes.per.checksum的大小来修改每个校验和所针对的文件的大小。
在hadoop中,校验和系统单独为一类,org.apache.hadoop.fs.ChecksumFileSystem,当需要校验和机制时,可以很方便的调用它来服务。
(2)对HDFS的I/O数据进行检查
一般来说,HDFS会在三种情况下检验校验和:
DataNode接收数据后存储数据前
DataNode接收数据一般有两种情况:1.从客户端上传数据 2.DataNode从其他DataNode上接收数据。
当客户端上传数据时,正在写数据的客户端将数据及其校验和发送到由一系列datanode组成的Pipeline管线。Pipeline管线中最后一个datanode负责验证校验和。
DataNode数据存储步骤:(包括从DataNode和客户端两种传输方式)
1.在传输数据的最开始阶段,Hadoop会简单地检查数据块的完整性信息;
2.依次向各个DataNode传输数据,包括数据头信息、块信息、备份个数、校验和等;
3.Hadoop不会在数据每流动到一个DataNode都检查校验和,只会在数据流达到最后一个节点时才检查校验和
如果在验证过程中发现有不一致的块,就会抛出CheckSumException异常信息
客户端读取DataNode上的数据时
Hadoop会在客户端读取DataNode上的数据时,使用DFSClient中的read函数先将数据读入到用户的数据缓冲区,然后再检查校验和。将他们与datanode中存储的校验和进行比较
每个datanode均持久保存有一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间
客户端成功验证一个数据块后,会告诉这个datanode,datanode由此更新日志
DataNode后台守护进程的定期检查
DataNode会在后台运行DataBlockScanner,这个程序定期验证存储在这个datanode上的所有数据块(3周)
该项措施是解决物理存储媒体上位衰减,位损坏的有力措施。
Hadoop处理损坏数据的机制:
DataNode在读取block块的时候会先进行checksum(数据块校验和)
如果client发现本次计算的校验和跟创建时的校验和不一致,则认为该block块已损坏
客户端在抛出ChecksumException之前上报该block信息给namenode进行标记(“已损坏”)
这样namenode就不会把客户端指向这个block,也不会复制这个block到其他的datanode。
client重新读取另外的datanode上的block
在心跳返回时NameNode将块的复制任务交给DataNode,从完好的block副本进行复制以达到默认的备份数3
NameNode删除掉坏的block。
DataNode在一个block块被创建之日起三周后开始进行校验
如果出于一些原因在操作的时候不想让hdfs检查校验码
在FileSystem的open()之前通过设置FileSystem的setVerifyCheckSum(false)方法禁用校验和
或者命令行使用get时候添加选项-ignoreCrc或者直接使用-copyToLocal
fs.setVerifyChecksum(false) fs.open(new Path(“”)) // 就不进行校验检查了
Hadoop fs –get –ignoreCrc hdfs://master:9000/a.txt
Hadoop fs –copyToLocal hdfs://master:9000/a.txt
CheckSum校验原理
Hadoop数据的完整性检测,都是通过校验和的比较来完成,在创建新文件时(也就是在上传数据到hdfs上时)将校验和的值和数据一起保存起来。NameNode会收到来自client、DataNode的检验和信息,根据这两个信息来维护文件的块存储及向客户端提供块读取服务。
HDFS会对写入的所有数据计算校验和,并在读取数据时验证校验和。
常用的错误检测码是CRC-32(循环冗余校验),任何大小的数据输入均计算得到一个32位的整数校验和。
在写入文件时,hdfs为每个数据块都生成一个crc文件。客户端读取数据时生成一个crc与数据节点存储的crc做比对,如果不匹配则说明数据已经损坏了。数据节点在后台运行一个程序定期(默认为21天)检测数据,防止物理存储介质中位衰减而造成的数据损坏。
DataNode在写入时计算出校验和,然后每次读的时候再计算校验和进行检验
hdfs会为每一个固定长度的数据(一个个数据包)执行一次校验和
这个值由io.bytes.per.checksum指定,默认是512字节。
因为CRC32是32位即4个字节,这样校验和占用的空间就会少于原数据的1%。
datanode在存储收到的数据前会校验数据的校验和,比如收到客户端的数据或者其他副本传过来的数据。
如hdfs数据流中客户端写入数据到hdfs时的数据流,在管道的最后一个datanode会去检查这个校验和
如果发现错误,就会抛出ChecksumException到客户端
从datanode读数据的时候一样要检查校验和,而且每个datanode还保存了检查校验和的日志,每次校验都会记录到日志中
除了读写操作会检查校验和以外,datanode还跑着一个后台进程(DataBlockScanner)
定期校验存在在它上面的block,因为除了读写过程中会产生数据错误以外,硬件本身也会产生数据错误,比如位衰减(bit rot)