HDFS面试题总结
Posted Icedzzz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDFS面试题总结相关的知识,希望对你有一定的参考价值。
参考链接:
https://zhuanlan.zhihu.com/p/265980039
尚硅谷视频
文章目录
HDFS架构
HDFS 采用 master/slave 架构。一个 HDFS 集群是由一个 Namenode(HA的情况除外) 和多个 Datanodes 组成。Namenode 是一个中心服务器,负责管理文件系统的名字空间(namespace) 以及客户端对文件的访问。集群中的 Datanode 一般是一个节点一个,负责管理它所在节点上的存储。 HDFS **暴露了文件系统的名字空间,用户能够以文件的形式在上面存储数据。**从内部看,一个文件其实被分成一个或多个数据块,这些块存储在一组 Datanode 上。Namenode 执行文件系统的名字空间操作,比如打开、关闭、重命名文件或目录。它也负责确定数据块到具体 Datanode 节点的映射。Datanode 负责处理文件系统客户端的读写请求。在 Namenode 的统一调度下进行数据块的创建、删除和复制。
HA架构
Active NameNode 和 Standby NameNode: 两台 NameNode 形成互备,一台处于 Active 状态,为主 NameNode,另外一台处于 Standby 状态,为备 NameNode,只有主 NameNode 才能对外提供读写服务,同一个时刻不应该出现两个节点都为Standby 状态或者都为Active状态,如有,那么需要进行排查问题所在(这里生产环境遇到多次问题,典型问题之一)。
主备切换控制器 ZKFailoverController: ZKFailoverController 负责定期的检测 NameNode 的健康状况,在主 NameNode(Active状态) 故障时借助 Zookeeper 实现自动的主备选举和切换。
**共享存储系统:**共享存储系统保存了 NameNode 在运行过程中所产生的 HDFS 的元数据。**主 NameNode 和 备 NameNode 通过共享存储系统实现元数据同步。**在进行主备切换的时候,新的主 NameNode 在确认元数据完全同步之后才能继续对外提供服务。(主备元数据同步问题,生产环境中也可能经常出现,典型问题之一)
NameNode 的主备切换实现
NameNode 主备切换主要由 ZKFailoverController、HealthMonitor 和 ActiveStandbyElector 这 3 个组件来协同实现:
ZKFailoverController 作为 NameNode 机器上一个独立的进程启动 (在 hdfs 启动脚本之中的进程名为 zkfc),启动的时候会创建 HealthMonitor 和 ActiveStandbyElector 这两个主要的内部组件。
HealthMonitor 主要负责检测 NameNode 的健康状态,如果检测到 NameNode 的状态发生变化,会回调 ZKFailoverController 的相应方法进行自动的主备选举。
ActiveStandbyElector 主要负责完成自动的主备选举,内部封装了 Zookeeper 的处理逻辑,一旦 Zookeeper 主备选举完成,会回调 ZKFailoverController 的相应方法来进行 NameNode 的主备状态切换。
ActiveStandbyElector 实现分析
Namenode(包括 YARN ResourceManager) 的主备选举是通过 ActiveStandbyElector 来完成的,ActiveStandbyElector 主要是利用了 Zookeeper 的写一致性和临时节点机制,
具体的主备选举实现如下:
- 在zk上面创建对应的节点
如果 HealthMonitor 检测到对应的 NameNode 的状态正常,那么表示这个 NameNode 有资格参加 Zookeeper 的主备选举。如果目前还没有进行过主备选举的话,那么相应的 ActiveStandbyElector 就会发起一次主备选举,** 尝试在 Zookeeper 上创建一个路径为/hadoop-ha/ d f s . n a m e s e r v i c e s / A c t i v e S t a n d b y E l e c t o r L o c k 的 临 时 节 点 ∗ ∗ ( {dfs.nameservices}/ActiveStandbyElectorLock 的临时节点 ** ( dfs.nameservices/ActiveStandbyElectorLock的临时节点∗∗({dfs.nameservices} 为 Hadoop 的配置参数 dfs.nameservices 的值),Zookeeper 的写一致性会保证最终只会有一个 ActiveStandbyElector 创建成功,那么创建成功的 ActiveStandbyElector 对应的 NameNode 就会成为主 NameNode,ActiveStandbyElector 会回调 ZKFailoverController 的方法进一步将对应的 NameNode 切换为 Active 状态。而创建失败的 ActiveStandbyElector 对应的 NameNode 成为备 NameNode,ActiveStandbyElector 会回调 ZKFailoverController 的方法进一步将对应的 NameNode 切换为 Standby 状态。
数据复制(副本机制)
HDFS旨在可靠地在大型群集中的机器上存储文件。它将每个文件存储为一系列的块。文件的块被复制到多个节点(复制到几个由复制因子控制,hdfs-site.xml的dfs.replication属性)以实现容错。复制因子可以针对生成环境需要进行配置,复制因子可以在文件创建时指定,并可以稍后更改。
一个文件的所有块(文件越大按照块大小切分出来的块越多)中除最后一个块之外的所有块都具有相同的大小(hdfs-site.xml的dfs.blocksize)。HDFS中的文件是一次写入的(附加和截断除外),并且在任何时候都严格限定只有一个writer。
**NameNode控制块的复制,它定期从集群中的每个DataNode接收Heartbeat和Blockreport。**收到Heartbeat意味着DataNode运行正常,Blockreport包含DataNode上所有块的列表(NameNode即可以知道是否有丢块或多块)。
副本放置策略
副本的放置对于HDFS的可靠性和性能至关重要。基于机架感知(Namenode知道副本在哪个rack)的副本放置策略的目的是提高数据可靠性,可用性和网络带宽利用率。
如果复制因子为3:
则HDFS的放置策略是在本地机器上放置第一个副本(如果写入器位于datanode上),否则随机放置在一个数据节点上;
第二个副本复制在另一个机架(非第一个副本所在的机架)的节点上;
第三个副本放在与第二个副本存放节点相同的机架上的其他节点上。
这样放置的优势所在:降低了读取数据时使用的总体网络带宽,因为块仅放置在两个独特的机架中,而不是三个。使用此策略,文件的副本不会均匀分布在机架上。三分之一的副本位于一个节点,三分之二的副本位于一个机架上,另外三分之一均匀分布在其余机架上。此策略可提高写入性能,而不会影响数据可靠性或读取性能。
注意:
如果复制因子大于3,则随机确定第4个副本和后续副本的位置,同时将每个机架的副本数保持在上限以下((replicas - 1) / racks + 2)
由于NameNode不允许一个DataNode具有同一个块的多个副本,因此创建的最大副本数不应该大于DataNode节点的总数量。
副本选择(即读取的时候优先从哪个节点读取)
为了尽量减少全局带宽消耗和读取延迟,HDFS会尝试选择最接近读请求的块。如果在读节点的同一机架上存在副本,则该副本优先满足读取请求。如果HDFS群集跨越多个数据中心,则驻留在本地数据中心的副本优先于任何远程副本。
机架感知
在海量数据处理中,主要限制原因之一是节点之间数据的传输速率,即带宽。因此,将两个节点之间的带宽作为两个节点之间的距离的衡量标准。
hadoop为此采用了一个简单的方法:**把网络看作一棵树,两个节点之间的距离是他们到最近共同祖先的距离总和。**该树中的层次是没有预先设定的, 但是相对与数据中心,机架和正在运行的节点,通常可以设定等级。具体想法是针对以下每个常见,可用带宽依次递减:
-
同一节点上的进程;
-
同一机架上的不同节点;
-
同一数据中心中不同机架上的节点;
-
不同数据中心的节点
举个例子,假设有数据中心d1,机架r1中的节点n1。该节点可以表示为“/d1/r1/n1”。利用这种标记,这里给出四种距离描述:
- 同一节点上的进程:/d1/r1/n1到/d1/r1/n1的距离
- 同一机架上的不同节点: /d1/r1/n1到/d1/r1/n2的距离
- 同一数据中心不同机架上的节点: /d1/r1/n1到/d1/r2/n2的距离
- 不同数据中心上的节点: /d1/r1/n1到/d2/r2/n2的距离
注意点:
-
当没有配置机架信息时,**全部的机器Hadoop都默认在同一个默认的机架下,以名为”/default-rack”。**这样的情况下,不论什么一台datanode机器,无论物理上是否属于同一个机架。都会被觉得是在同一个机架下。
-
一旦配置topology.script.file.name。就依照网络拓扑结构来寻找datanode。topology.script.file.name这个配置选项的value指定为一个可运行程序。通常为一个脚本。
官方的python脚本:
#!/usr/bin/python
# this script makes assumptions about the physical environment.
# 1) each rack is its own layer 3 network with a /24 subnet, which
# could be typical where each rack has its own
# switch with uplinks to a central core router.
#
# +-----------+
# |core router|
# +-----------+
# / \\
# +-----------+ +-----------+
# |rack switch| |rack switch|
# +-----------+ +-----------+
# | data node | | data node |
# +-----------+ +-----------+
# | data node | | data node |
# +-----------+ +-----------+
#
# 2) topology script gets list of IP's as input, calculates network address, and prints '/network_address/ip'.
import netaddr
import sys
sys.argv.pop(0) # discard name of topology script from argv list as we just want IP addresses
netmask = '255.255.255.0' # set netmask to what's being used in your environment. The example uses a /24
for ip in sys.argv: # loop over list of datanode IP's
address = '{0}/{1}'.format(ip, netmask) # format address string so it looks like 'ip/netmask' to make netaddr work
try:
network_address = netaddr.IPNetwork(address).network # calculate and print network address
print "/{0}".format(network_address)
except:
print "/rack-unknown" # print catch-all value if unable to calculate network address
为什么需要HDFS?
当数据集的大小超出一台独立的服务服务器的存储能力时,应该怎么存储数据?答案就是对数据进行分区(partition)并将其存储到多个服务器上面,那这必然涉及到跨网络存储和管理,也势必引入网络编程的复杂性,因此,分布式的文件管理系统比普通的架设在单个服务器上的存储系统更加的复杂。Hadoop 生态圈中有一个组件叫做HDFS(Hadoop Distributed Filesystem) 就是专为了解决这个问题的分布式文件系统。
HDFS为什么要切块?
HDFS中对文件的存储是按照块进行的,文件进来后,它会将文件依据配置的大小进行切分为很多块(每个块的大小是可配置的比如,128M或64M等),然后对块进行存储。这样做有两个好处:
- 一个明显的好处是,一个文件的大小可以大于网络中任意一个磁盘的容量,文件的所有块并不需要存储在同一个磁盘上,因此它们可以利用集群上的任意一个磁盘进行存储。
- 另一个很重要的好处是,块很适合进行数据备份,从而提高了数据的容错能力和可用性。 HDFS默认会将块复制3个(默认值,可配置)单独的物理服务器上面进行容错。这样即使一个服务器挂了,也不会导致数据丢失,因为它会从另外的服务器进行读取并恢复成3个副本,同时也对负载进行均衡。
HDFS里面为什么一般设置块大小为64MB或128MB?
随着技术的发展你认为块的大小会有什么变化?
-
为什么不能远少于64MB?
(1)减少硬盘寻道时间。 HDFS设计前提是应对大数据量操作,若数据块大小设置过小,那需要读取的数据块数量就会增多,从而间接增加底层硬盘的寻道时间,因为找到一个块,就需要寻道一次。
(2) 减少NameNode内存消耗。 由于NameNode记录着DataNode中的数据块信息,若数据块大小设置过小,则数据块数量增多,需要维护的数据块信息就会增多,从而消耗NameNode的内存。现场出现过多次,因为存储了大量的小文件,导致NameNode内存溢出。对于HDFS来说,存储小文件是不合适的。 -
为什么不能远大于64MB?
(1)问题分解问题。数据量的大小与问题解决的复杂度呈线性关系。对于同一个算法,处理的数据量越大,时间复杂度越高。
(2)约束Map输出。在Map Reduce框架里,Map之后的数据是要经过排序才执行Reduce操作的。这通常涉及到归并排序,而归并排序的算法思想便是“对小文件进行排序,然后将小文件归并成大文件”,因此“小文件”不宜过大。
(3)那么随着技术的发展,网络越来越快,磁盘也越来越快,内存,cpu性能越来越高,block的是可以增大的,我现在使用的block的大小就是128M,未来可能是256M也说不准。
HDFS的读写
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 步)。
HDFS读操作
(1) 客户端通过 DistributedFileSystem 向 NameNode 请求下载文件, NameNode 通过查询元数据,找到文件块所在的 DataNode 地址。
(2) 挑选一台 DataNode(就近原则,然后随机)服务器,请求读取数据。
(3) DataNode 开始传输数据给客户端(从磁盘里面读取数据输入流,以 Packet 为单位来做校验)。
(4) 客户端以 Packet 为单位接收,先在本地缓存,然后写入目标文件。
SecondaryNameNode
主备切换
HDFS负载均衡
Hadoop的HDFS集群非常容易出现机器与机器之间磁盘利用率不平衡的情况,例如:当集群内新增、删除节点,或者某个节点机器内硬盘存储达到饱和值。当数据不平衡时,Map任务可能会分配到没有存储数据的机器,这将导致网络带宽的消耗,也无法很好的进行本地计算。
当HDFS负载不均衡时,需要对HDFS进行数据的负载均衡调整,即对各节点机器上数据的存储分布进行调整。从而,让数据均匀的分布在各个DataNode上,均衡IO性能,防止热点的发生。进行数据的负载均衡调整,必须要满足如下原则:
- 数据平衡不能导致数据块减少,数据块备份丢失
- 管理员可以中止数据平衡进程
- 每次移动的数据量以及占用的网络资源,必须是可控的
- 数据均衡过程,不能影响namenode的正常工作
HDFS数据负载均衡原理
数据均衡过程的核心是一个数据均衡算法**,该数据均衡算法将不断迭代数据均衡逻辑,使得数据较多的节点上面的数据向数据较少的节点移动,直至集群内数据均衡为止。**该数据均衡算法每次迭代的逻辑如下:
步骤分析如下:
- **数据均衡服务(Rebalancing Server)**首先要求 NameNode 生成 DataNode 数据分布分析报告,获取每个DataNode磁盘使用情况
- Rebalancing Server汇总需要移动的数据分布情况,计算具体数据块迁移路线图。数据块迁移路线图,确保网络内最短路径
- 开始数据块迁移任务,Proxy Source Data Node复制一块需要移动数据块将复制的数据块复制到目标DataNode上
- 删除原始数据块
- 目标DataNode向Proxy Source Data Node确认该数据块迁移完成
- Proxy Source Data Node向Rebalancing Server确认本次数据块迁移完成。然后继续执行这个过程,直至集群达到数据均衡标准
HDFS快照
Hdfs的快照(snapshot)是在某一时间点对指定文件系统拷贝,快照采用只读模式,可以对重要数据进行恢复、防止用户错误性的操作。 快照分两种,Hdfs属于前者
- 建立文件系统的索引,每次更新文件不会真正的改变文件,而是新开辟一个空间用来保存更改的文件;
- 拷贝所有的文件系统
HDFS数据完整性
受网络不稳定、硬件损坏等因素,IO操作过程中难免会出现数据丢失或脏数据,难免会出现数据丢失或脏数据,数据传输的量越大,出现错误的概率就越高。
检测数据是否损坏的常见措施是,**在数据第一次引入系统时计算校验和(checksum)并存储,在数据进行传输后再次计算校验和进行对比,如果计算所得的新校验和和原来的校验和不匹配,就认为数据已损坏。**注意,校验和也是可能损坏的,因为校验和本身也是数据,但由于校验和比数据小得多,所以损坏的概率比较小。
对本地文件I/O的检查
在Hadoop中,本地文件系统的数据完整性由客户端负责。重点在读取文件时进行校验和的处理。
每当hadoop创建文件file时,hadoop就会同时在同一个文件夹下创建隐藏文件.file.crc,这个文件记录了 文件a的校验和。
对HDFS的I/O数据进行检查
一般来说,HDFS会在三种情况下检验校验和:
- DataNode接收数据后存储数据前 DataNode接收数据一般有两种情况:
- 从客户端上传数据
- DataNode从其他DataNode上接收数据。
当客户端上传数据时,正在写数据的客户端将数据及其校验和发送到由一系列datanode组成的Pipeline管线。Pipeline管线中最后一个datanode负责验证校验和。
- 客户端读取DataNode上的数据时 Hadoop会在客户端读取DataNode上的数据时,使用DFSClient中的read函数先将数据读入到用户的数据缓冲区,然后再检查校验和。
将他们与datanode中存储的校验和进行比较,每个datanode均持久保存有一个用于验证的校验和日志,所以它知道每个数据块的最后一次验证时间,客户端成功验证一个数据块后,会告诉这个datanode,datanode由此更新日志。
- DataNode后台守护进程的定期检查 DataNode会在后台运行DataBlockScanner,这个程序定期(hdfs-site.xml)验证存储在这个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面试题总结的主要内容,如果未能解决你的问题,请参考以下文章