查看原文
其他

【大数据哔哔集20210122】面试官问我HDFS丢不丢数据?我啪就把这个文章甩到他脸上

点击上方蓝色字体,选择“设为星标”

回复”资源“获取更多惊喜

数据一致性

HDFS作为分布式文件系统在分布式环境下如何保证数据一致性。HDFS中,存储的文件将会被分成若干的大小一致的block分布式地存储在不同的机器上,需要NameNode节点来对这些数据进行管理,存储这些block的结点称为DataNode,NameNode是用来管理这些元数据的。
NameNode保证元数据的一致性
客户端上传文件时,NameNode首先往edits log文件中记录元数据的操作日志。与此同时,NameNode将会在磁盘做一份持久化处理(fsimage文件):它跟内存中的数据是对应的,如何保证和内存中的数据的一致性?在edits logs满之前对内存和fsimage的数据做同步,合并edits logs和fsimage上的数据,然后edits logs上的数据即可清除。而当edits logs满之后,文件的上传不能中断,所以将会往一个新的文件edits.new上写数据,而老的edits logs的合并操作将由secondNameNode来完成,即所谓的checkpoint操作。
checkpoint的触发一般由两种限制,一个是edits logs的大小限制,即fs.checkpoint.size配置;一个是指定时间,即fs.checkpoint.period配置。根据规定,大小的限制是优先的,规定edits文件一旦超过阈值,则不管是否达到最大时间间隔,都会强制checkpoint。
SecondaytNameNode 是 HA(High Available 高可用性)的一个解决方案,但不支持热备,配置即可。SecondaryNameNode执行过程:从NameNode上下载元数据信息(fsimage、edits),然后把二者合并,生成新的fsimage,在本地保存,并将其推送到NameNode,替换旧的fsimage。(注:SecondaryNameNode 只存在于Hadoop1.0中,Hadoop2.0以上版本中没有,但在伪分布模式中是有SecondaryNameNode的,在集群模式中是没有SecondaryNameNode的)
SecondaryNameNode 工作流程步骤:
  • SecondaryNameNode 通知NameNode切换edits文件

  • SecondaryNameNode 从NameNode获得fsimage和edits(通过http)

  • SecondaryNameNode 将fsimage载入内存,然后开始合并edits。同样合并edits操作是需要满足一定条件才进行的,有两个条件:1)fs.checkpoint.period指定两次checkpoint的最大时间间隔,默认3600秒 2)fs.checkpoint.size规定edits文件的最大值,一旦超过这个值(默认大小是64M)则强制checkpoint,不管是否到达最大时间间隔。)

  • SecondaryNameNode 将新的fsimage发回给NameNode

  • NameNode用新的fsimage替换旧的fsimage

校验和
HDFS 会对写入的所有数据计算校验和(checksum),并在读取数据时验证校验和。针对指定字节的数目计算校验和。字节数默认是512 字节,可以通过io.bytes.per.checksum属性设置。通过CRC-32编码后为4字节。
DataNode 在保存数据前负责验证checksum。client 会把数据和校验和一起发送到一个由多个DataNode 组成的队列中,最后一个DataNode 负责验证checksum。如果验证失败,会抛出一个ChecksumException。客户端需要处理这种异常。客户端从DataNode 读取数据时,也会验证checksum。每个DataNode 都保存了一个验证checksum的日志。每次客户端成功验证一个数据块后,都会告知DataNode ,DataNode 会更新日志。
每个DataNode 也会在一个后台线程中运行一个DataBlockScanner,定期验证这个 DataNode 上的所有数据块。在用 hadoop fs get 命令读取文件时,可以用 -ignoreCrc 忽略验证。如果是通过FileSystem API 读取时,可以通过setVerifyChecksum(false),忽略验证。Hadoop中的LocalFileSystem会进行客户端的检验和,写文件时,会在目录下创建一个名为.filename.crc的隐藏文件,如果想禁止校验和功能,可以用RawLocalFileSystem代替LocalFileSystem 。
Configuration conf = ...
FileSystem fs = new RawLocalFileSystem();
fs.initialize(null, conf);
或者直接设置fs.file.impl属性为org.apache.hadoop.fs.RawLocalFileSystem,这样会全局禁用checksum。LocalFileSystem内部使用了ChecksumFileSystem完成checksum工作。通过ChecksumFileSystem可以添加校验和功能。

HA高可用

冗余副本
HDFS处理节点失效的一个方法就是数据冗余,即对数据做多个备份,在HDFS中可以通过配置文件设置备份的数量,如果不进行设置,这个数量默认为3。注意参数dfs.replication.min和dfs.replication的区别:在一个块被写入期间,只要至少写入了dfs.replication.min个副本数(默认是1),写操作就会成功,直到达到其目标副本数dfs.replication(默认是3) HDFS副本在机架上如何分布?
HDFS采用一种称为机架感知的策略来改进数据的可靠性、可用性和网络带宽的利用率。在大多数情况下,HDFS副本系数是默认为3(dfs.replication),HDFS的存放策略是将一个副本存放在本地机架节点上,一个副本存放在同一个机架的另一个节点上,最后一个副本放在不同机架的节点上。这种策略减少了机架间的数据传输,提高了写操作的效率。机架的错误远远比节点的错误少,所以这种策略不会影响到数据的可靠性和可用性。与此同时,因为数据块只存放在两个不同的机架上,所以此策略减少了读取数据时需要的网络传输总带宽。
机架感知
通常,大型Hadoop集群是以机架的形式来组织的,同一个机架上不同节点间的网络状况比不同机架之间的更为理想。Namenode设法将数据块副本保存在不同的机架上以提高容错性 HDFS如何得知哪个Datanode在哪个机架上?
HDFS使用了一种称为“机架感知”的策略。HDFS不能够自动判断集群中各个Datanode的网络拓扑情况,必须通过配置dfs.network.script参数来确定节点所处的机架。文件提供了IP->rackid的翻译,NameNode通过这个得到集群中各个Datanode节点的rackid。
心跳机制
检测节点失效使用“心跳机制”。每个DataNode节点周期性地向NameNode发送心跳信号。网络分区可能导致一部分DataNode跟NameNode失去联系。NameNode通过心跳信号的缺失来检测这一情况,并将这些近期不再发送心跳信号DataNode标记为宕机,不会再将新的IO请求发给它们。
任何存储在宕机DataNode上的数据将不再有效。DataNode的宕机可能会引起一些数据块的副本系数低于指定值,NameNode不断地检测这些需要复制的数据块,一旦发现就启动复制操作。
在下列情况下,可能需要重新复制:a) 某个DataNode节点失效 b) 某个副本遭到损坏 c) DataNode上的硬盘错误 d) 文件的冗余因子增大
DataNode与NameNode心跳报告内容
一个数据块在DataNode以文件存储在磁盘上,包括两个文件,一个是数据本身,一个是元数据包括数据块的长度、块数据的校验和以及时间戳。当DataNode读取block的时候,它会计算checksum,如果计算后的checksum,与block创建时值不一样,说明该block已经损坏。如果块已损坏,Client会读取其它DataNode上的block。NameNode标记该块已经损坏,然后复制block达到预期设置的文件备份数。
DataNode启动后向NameNode注册,心跳是每3秒一次,目的是告诉namenode自己的存活状况以及可用空间。心跳返回结果带有NameNode给该DataNode的命令如复制块数据到另一台机器,或删除某个数据块。确认datanode宕机 (1)停止发送心跳报告 默认连续10次心跳接受不到即连续10*3=30s不间断。这10次中间只要有1次接受到了重新记录心跳。
(2)namenode发送检查
在连续10次NameNode没有接收到DataNode的心跳报告之后,NameNode断定DataNode可能宕机了,NameNode主动向DataNode发送检查 NameNode会开启后台的守护(阻塞)进程 等待检查结果的。NameNode检查DataNode的时间:默认5min。
默认检查2次每次检查5min连续2次检查(10min)都没有反应确认DataNode宕机了(发送一次等待5分钟)。NameNode确认一个DataNode宕机需要的总时间:103s+300s2=630s。
当DataNode启动的时候,它会遍历本地文件系统,产生一份HDFS数据块和本地文件对应关系的列表,这就是报告块(BlockReport),报告块包含了DataNode上所有块的列表。每小时DataNode默认向NameNode发送block report。汇报DataNode的数据节点情况。
安全模式
  • NameNode启动后会进入一个称为安全模式的特殊状态。处于安全模式的NameNode对于客户端来说是只读的。NameNode从所有的DataNode接收心跳信号和块状态报告(blockreport)。

  • 每个数据块都有一个指定的最小副本数(dfs.replication.min),当NameNode检测确认某个数据块的副本数目达到这个最小值,那么该数据块就会被认为是副本安全(safely replicated)的。

  • 在一定百分比(这个参数配置于dfs.safemode.threshold.pct,默认值是99.9%)的数据块被NameNode检测确认是安全之后,再过若干时间后(这个参数配置于dfs.safemode.extension,默认值是30秒),NameNode将退出安全模式状态。接下来NameNode会确定还有哪些数据块的副本没有达到指定数目,并将这些数据块复制到其他DataNode上。

校验和
  • HDFS会对写入的所有数据计算校验和(checksum),并在读取数据时验证。Datanode在收到客户端的数据或者复制其他Datanode的数据时,在验证数据后会存储校验和。正在写数据的客户端将数据及其校验和发送到由一系列Datanode组成的管线,管线中的最后一个Datanode负责验证校验和。如果Datanode检测到错误,客户端便会收到一个ChecksumException

  • 客户端从Datanode读取数据时,也会验证校验和,将它们与Datanode中存储的校验和进行比较。每个Datanode均持久保存一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间。客户端成功验证一个数据块后,会通知这个Datanode更新次日志

  • 此外,每个Datanode也会在一个后台运行一个称为DataBlockScanner的进程定期验证存储在这个Datanode上的所有数据块。检测到错误后,Namenode将这个已损坏的数据块标记为已损坏,之后从其他Datanode复制此数据的副本,最后使得数据的副本达到指定数目

回收站
当用户或应用程序删除某个文件时,这个文件并没有立刻从HDFS中删除。实际上,HDFS会将这个文件重命名转移到/trash目录。只要文件还在/trash目录中,该文件就可以被迅速地恢复。
文件在/trash中保存的时间是可配置的(配置参数fs.trash.interval),当超过这个时间时,Namenode就会将该文件从命名空间中删除。删除文件会使得该文件相关的数据块被释放。注意,从用户删除文件到HDFS空闲空间的增加之间会有一定时间的延迟。
元数据保护
FsImage和Editlog是HDFS的核心数据。如果这些文件损坏了,整个HDFS都将失效。因而,Namenode可以配置成支持维护多个FsImage和Editlog的副本。任何对FsImage或者Editlog的修改,都将同步到它们的副本上。这种多副本的同步操作可能会降低Namenode每秒处理的名字空间事务数量。然而这个代价是可以接受的,因为即使HDFS的应用是数据密集的,它们也非元数据密集的。当Namenode重启的时候,它会选取最近的完整的FsImage和Editlog来使用
快照机制
快照支持某一特定时刻的数据的复制备份。利用快照,可以让HDFS在数据损坏时恢复到过去一个已知正确的时间点。HDFS目前还不支持快照功能,但计划会在将来的版本支持。

容错机制

故障的类型主要有以下三种,针对这三种故障类型,HDFS提供了不同的故障检测机制:
针对DataNode失效问题,HDFS使用了心跳机制,DataNode定期向NameNode发送心跳信息,NameNode根据心跳信息判断DataNode是否存活 针对网络故障而导致无法收发数据的问题,HDFS提供了ACK的机制,在发送端发送数据后,如果没有收到ACK并且经过多次重试后仍然如此,则认为网络故障 针对数据损坏问题,所有DataNode会定期向NameNode发送自身存储的块清单,在传输数据的同时会发送总和校验码,NameNode依次来判断数据是否丢失或损坏
读容错
读失败时:
  • DFSInputStream 会去尝试连接列表里的下一个 DataNode ,同时记录下这个异常节点

  • DFSInputStream 也会对获取到的数据核查 checknums 。如果损坏的 block 被发现了,DFSInputStream 就试图从另一个拥有备份的 DataNode 中去读取备份块中的数据

  • 最后异常数据的同步工作也是由 NameNode 来安排完成

写容错
当写流程中一台 DataNode 宕机了:
首先 pipeline 被关闭,在确认队列中的剩下的 package 会被添加进数据队列的起始位置上不再发送,以防止在失败的节点下游的节点再丢失数据 然后,存储在正常的 DataNode 上的 block 会被指定一个新的标识,并将该标识传递给 NameNode,以便故障DataNode在恢复后可以删除自己存储的那部分已经失效的数据块 失败节点会从 pipeline 中移除,然后剩下两个好的 DataNode 会组成一个的新的 pipeline ,剩下的这些 block 的包会继续写进 pipeline 中正常的 DataNode 中 最后,NameNode 会发现节点宕机导致部分 block 的块备份数小于规定的备份数,此时NameNode 会安排使节点的备份数满足 dfs.replication 配置要求
DataNode失效
在NameNode中会持有数据块表和DataNode两张表。数据块表存储着某个数据块(包括副本)所在的DataNode,DataNode表存储着每个DataNode中保存的数据块列表。由于DataNode会周期性地给NameNode发送自己所持有的数据块信息,因此NameNode会持续更新数据块表和DataNode表。如果发现某个DataNode上的数据块错误,NameNode会从数据块表删除该数据块;如果发现某个DataNode失效,NameNode会对两张表进行更新。NameNode还会周期性地扫描数据块表,如果发现数据块表中某个数据库的备份数量低于所设置的备份数,则会协调从其它DataNode复制数据到另一个DataNode上完成备份。
HDFS副本放置策略
如果写请求出现在某个DataNode上,第一个副本就会存放在当前DataNode所在的机器上,如果该机器负载过重,可以将该备份放在同一个机架上的随机机器上。接着第二个副本就会存放在不同于第一个副本所在机架的机架上,第三个副本会随机存放在第二个副本所在机架的任一DataNode上。
在三个副本的情况下,第一个副本与原数据在相同机器上,另外两个副本放在其它机架的随机机器上。这样的设置可以使得性能与容灾兼备,优先从同机器上获取备份数据,减少数据传输开销;在该机器宕机的情况下,从另一个机架获取备份数据,避免同一个机架的机器集体宕机的情况出现。
HDFS的HA架构
以上的所有容错都是基于DataNode的故障问题进行考虑的,但是NameNode本身就存在单点故障,如果NameNode出现故障,则整个集群会直接宕机。因此HDFS提供了HA的架构,对于一个典型的HA集群而言,NameNode会被配置在两台独立的机器上,在任何时间上,一个NameNode处于Active状态,而另一个NameNode处于Standby状态,Active状态的NameNode会响应集群中所有的客户端的请求,Standby状态的NameNode只是作为一个副本,保证在必要的时候提供一个快速的转移,使得上层对NameNode的切换无感知,Standby NameNode与Active NameNode应时刻保持同步,在Active NameNode和Standby NameNode之间要有个共享的存储日志的地方,Active NameNode把EditLog写到共享的存储日志中,Standby NameNode读取日志并执行,使得Active NameNode和Standby NameNode内存中的HDFS元数据保持同步。


【大数据哔哔集20210121】SparkSQL优化策略小盘点

【大数据哔哔集20210120】Kafka 的高可靠性是怎么实现的

【大数据哔哔集20210118】Spark数据倾斜调优七大方案

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存