Hadoop经典十个问

网友投稿 271 2022-11-17

Hadoop经典十个问

@[toc]

Hadoop

hadoop 中常问的有三块,第一:存储,问到存储,就把 HDFS 相关的知识点拿出来;第二:计算框架 (MapReduce);第三:资源调度框架 (yarn)

1. 请说下 HDFS 读写流程

这个问题虽然见过无数次,面试官问过无数次,但是就是有人不能完整的说下来,所以请务必记住。并且很多问题都是从 HDFS 读写流程中引申出来的

HDFS 写流程

1)client 客户端发送上传请求,通过 RPC 与 namenode 建立通信,namenode 检查该用户是否有上传权限,以及上传的文件是否在 hdfs 对应的目录下重名,如果这两者有任意一个不满足,则直接报错,如果两者都满足,则返回给客户端一个可以上传的信息

2)client 根据文件的大小进行切分,默认 128M 一块,切分完成之后给 namenode 发送请求第一个 block 块上传到哪些服务器上

3)namenode 收到请求之后,根据网络拓扑和机架感知以及副本机制进行文件分配,返回可用的 DataNode 的地址

注:Hadoop 在设计时考虑到数据的安全与高效, 数据文件默认在 HDFS 上存放三份, 存储策略为本地一份,同机架内其它某一节点上一份, 不同机架的某一节点上一份

4)客户端收到地址之后与服务器地址列表中的一个节点如 A 进行通信,本质上就是 RPC 调用,建立 pipeline,A 收到请求后会继续调用 B,B 在调用 C,将整个 pipeline 建立完成,逐级返回 client

5)client 开始向 A 上发送第一个 block(先从磁盘读取数据然后放到本地内存缓存),以 packet(数据包,64kb)为单位,A 收到一个 packet 就会发送给 B,然后 B 发送给 C,A 每传完一个 packet 就会放入一个应答队列等待应答

6)数据被分割成一个个的 packet 数据包在 pipeline 上依次传输,在 pipeline 反向传输中,逐个发送 ack(命令正确应答),最终由 pipeline 中第一个 DataNode 节点 A 将 pipelineack 发送给 Client

7)当一个 block 传输完成之后, Client 再次请求 NameNode 上传第二个 block ,namenode 重新选择三台 DataNode 给 client

HDFS 读流程

1)client 向 namenode 发送 RPC 请求。请求文件 block 的位置

2)namenode 收到请求之后会检查用户权限以及是否有这个文件,如果都符合,则会视情况返回部分或全部的 block 列表,对于每个 block,NameNode 都会返回含有该 block 副本的 DataNode 地址; 这些返回的 DN 地址,会按照集群拓扑结构得出 DataNode 与客户端的距离,然后进行排序,排序两个规则:网络拓扑结构中距离 Client 近的排靠前;心跳机制中超时汇报的 DN 状态为 STALE,这样的排靠后

3)Client 选取排序靠前的 DataNode 来读取 block,如果客户端本身就是 DataNode, 那么将从本地直接获取数据 (短路读取特性)

4)底层上本质是建立 Socket Stream(FSDataInputStream),重复的调用父类 DataInputStream 的 read 方法,直到这个块上的数据读取完毕

5)当读完列表的 block 后,若文件读取还没有结束,客户端会继续向 NameNode 获取下一批的 block 列表

6)读取完一个 block 都会进行 checksum 验证,如果读取 DataNode 时出现错误,客户端会通知 NameNode,然后再从下一个拥有该 block 副本的 DataNode 继续读

7)read 方法是并行的读取 block 信息,不是一块一块的读取;NameNode 只是返回 Client 请求包含块的 DataNode 地址,并不是返回请求块的数据

8) 最终读取来所有的 block 会合并成一个完整的最终文件

2. HDFS 在读取文件的时候, 如果其中一个块突然损坏了怎么办

客户端读取完 DataNode 上的块之后会进行 checksum 验证,也就是把客户端读取到本地的块与 HDFS 上的原始块进行校验,如果发现校验结果不一致,客户端会通知 NameNode,然后再从下一个拥有该 block 副本的 DataNode 继续读

3. HDFS 在上传文件的时候, 如果其中一个 DataNode 突然挂掉了怎么办

客户端上传文件时与 DataNode 建立 pipeline 管道,管道正向是客户端向 DataNode 发送的数据包,管道反向是 DataNode 向客户端发送 ack 确认,也就是正确接收到数据包之后发送一个已确认接收到的应答,当 DataNode 突然挂掉了,客户端接收不到这个 DataNode 发送的 ack 确认,客户端会通知 NameNode,NameNode 检查该块的副本与规定的不符,NameNode 会通知 DataNode 去复制副本,并将挂掉的 DataNode 作下线处理,不再让它参与文件上传与下载。

4. NameNode 在启动的时候会做哪些操作

NameNode 数据存储在内存和本地磁盘,本地磁盘数据存储在 fsimage 镜像文件和 edits 编辑日志文件

首次启动 NameNode1、格式化文件系统,为了生成 fsimage 镜像文件2、启动 NameNode(1)读取 fsimage 文件,将文件内容加载进内存(2)等待 DataNade 注册与发送 block report3、启动 DataNode(1)向 NameNode 注册(2)发送 block report(3)检查 fsimage 中记录的块的数量和 block report 中的块的总数是否相同4、对文件系统进行操作(创建目录,上传文件,删除文件等)(1)此时内存中已经有文件系统改变的信息,但是磁盘中没有文件系统改变的信息,此时会将这些改变信息写入 edits 文件中,edits 文件中存储的是文件系统元数据改变的信息。 第二次启动 NameNode1、读取 fsimage 和 edits 文件2、将 fsimage 和 edits 文件合并成新的 fsimage 文件3、创建新的 edits 文件,内容为空4、启动 DataNode

5. Secondary NameNode 了解吗,它的工作机制是怎样的

Secondary NameNode 是合并 NameNode 的 edit logs 到 fsimage 文件中;

它的具体工作机制:(1)Secondary NameNode 询问 NameNode 是否需要 checkpoint。直接带回 NameNode 是否检查结果(2)Secondary NameNode 请求执行 checkpoint(3)NameNode 滚动正在写的 edits 日志(4)将滚动前的编辑日志和镜像文件拷贝到 Secondary NameNode(5)Secondary NameNode 加载编辑日志和镜像文件到内存,并合并(6)生成新的镜像文件 fsimage.chkpoint(7)拷贝 fsimage.chkpoint 到 NameNode(8)NameNode 将 fsimage.chkpoint 重新命名成 fsimage所以如果 NameNode 中的元数据丢失,是可以从 Secondary NameNode 恢复一部分元数据信息的,但不是全部,因为 NameNode 正在写的 edits 日志还没有拷贝到 Secondary NameNode,这部分恢复不了

6. Secondary NameNode 不能恢复 NameNode 的全部数据,那如何保证 NameNode 数据存储安全

这个问题就要说 NameNode 的高可用了,即 NameNode HA一个 NameNode 有单点故障的问题,那就配置双 NameNode,配置有两个关键点,一是必须要保证这两个 NN 的元数据信息必须要同步的,二是一个 NN 挂掉之后另一个要立马补上。

元数据信息同步在 HA 方案中采用的是 “共享存储”。每次写文件时,需要将日志同步写入共享存储,这个步骤成功才能认定写文件成功。然后备份节点定期从共享存储同步日志,以便进行主备切换。 监控 NN 状态采用 zookeeper,两个 NN 节点的状态存放在 ZK 中,另外两个 NN 节点分别有一个进程监控程序,实施读取 ZK 中有 NN 的状态,来判断当前的 NN 是不是已经 down 机。如果 standby 的 NN 节点的 ZKFC 发现主节点已经挂掉,那么就会强制给原本的 active NN 节点发送强制关闭请求,之后将备用的 NN 设置为 active。

如果面试官再问 HA 中的 共享存储 是怎么实现的知道吗?可以进行解释下:NameNode 共享存储方案有很多,比如 Linux HA, VMware FT, QJM 等,目前社区已经把由 Clouderea 公司实现的基于 QJM(Quorum Journal Manager)的方案合并到 HDFS 的 trunk 之中并且作为默认的共享存储实现基于 QJM 的共享存储系统主要用于保存 EditLog,并不保存 FSImage 文件。FSImage 文件还是在 NameNode 的本地磁盘上。QJM 共享存储的基本思想来自于 Paxos 算法,采用多个称为 JournalNode 的节点组成的 JournalNode 集群来存储 EditLog。每个 JournalNode 保存同样的 EditLog 副本。每次 NameNode 写 EditLog 的时候,除了向本地磁盘写入 EditLog 之外,也会并行地向 JournalNode 集群之中的每一个 JournalNode 发送写请求,只要大多数 (majority) 的 JournalNode 节点返回成功就认为向 JournalNode 集群写入 EditLog 成功。如果有 2N+1 台 JournalNode,那么根据大多数的原则,最多可以容忍有 N 台 JournalNode 节点挂掉

7. 在 NameNode HA 中,会出现脑裂问题吗?怎么解决脑裂

假设 NameNode1 当前为 Active 状态,NameNode2 当前为 Standby 状态。如果某一时刻 NameNode1 对应的 ZKFailoverController 进程发生了 “假死” 现象,那么 Zookeeper 服务端会认为 NameNode1 挂掉了,根据前面的主备切换逻辑,NameNode2 会替代 NameNode1 进入 Active 状态。但是此时 NameNode1 可能仍然处于 Active 状态正常运行,这样 NameNode1 和 NameNode2 都处于 Active 状态,都可以对外提供服务。这种情况称为脑裂

脑裂对于 NameNode 这类对数据一致性要求非常高的系统来说是灾难性的,数据会发生错乱且无法恢复。Zookeeper 社区对这种问题的解决方法叫做 fencing,中文翻译为隔离,也就是想办法把旧的 Active NameNode 隔离起来,使它不能正常对外提供服务。

在进行 fencing 的时候,会执行以下的操作:

首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态。 如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施,通常会选择 sshfence:(1) sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死(2) shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离

8. 小文件过多会有什么危害, 如何避免

Hadoop 上大量 HDFS 元数据信息存储在 NameNode 内存中, 因此过多的小文件必定会压垮 NameNode 的内存

每个元数据对象约占 150byte,所以如果有 1 千万个小文件,每个文件占用一个 block,则 NameNode 大约需要 2G 空间。如果存储 1 亿个文件,则 NameNode 需要 20G 空间

显而易见的解决这个问题的方法就是合并小文件, 可以选择在客户端上传时执行一定的策略先合并, 或者是使用 Hadoop 的 CombineFileInputFormat 实现小文件的合并

9. 请说下 HDFS 的组织架构

1)Client:客户端(1)切分文件。文件上传 HDFS 的时候,Client 将文件切分成一个一个的 Block,然后进行存储(2)与 NameNode 交互,获取文件的位置信息(3)与 DataNode 交互,读取或者写入数据(4)Client 提供一些命令来管理 HDFS,比如启动关闭 HDFS、访问 HDFS 目录及内容等2)NameNode:名称节点,也称主节点,存储数据的元数据信息,不存储具体的数据(1)管理 HDFS 的名称空间(2)管理数据块(Block)映射信息(3)配置副本策略(4)处理客户端读写请求3)DataNode:数据节点,也称从节点。NameNode 下达命令,DataNode 执行实际的操作(1)存储实际的数据块(2)执行数据块的读 / 写操作4)Secondary NameNode:并非 NameNode 的热备。当 NameNode 挂掉的时候,它并不能马上替换 NameNode 并提供服务(1)辅助 NameNode,分担其工作量(2)定期合并 Fsimage 和 Edits,并推送给 NameNode(3)在紧急情况下,可辅助恢复 NameNode

10. 请说下 MR 中 Map Task 的工作机制

简单概述:inputFile 通过 split 被切割为多个 split 文件,通过 Record 按行读取内容给 map(自己写的处理逻辑的方法),数据被 map 处理完之后交给 OutputCollect 收集器,对其结果 key 进行分区(默认使用的 hashPartitioner),然后写入 buffer,每个 map task 都有一个内存缓冲区(环形缓冲区),存放着 map 的输出结果,当缓冲区快满的时候需要将缓冲区的数据以一个临时文件的方式溢写到磁盘,当整个 map task 结束后再对磁盘中这个 maptask 产生的所有临时文件做合并,生成最终的正式输出文件,然后等待 reduce task 的拉取

详细步骤:

读取数据组件 InputFormat (默认 TextInputFormat) 会通过 getSplits 方法对输入目录中的文件进行逻辑切片规划得到 block, 有多少个 block 就对应启动多少个 MapTask. 将输入文件切分为 block 之后, 由 RecordReader 对象 (默认是 LineRecordReader) 进行读取, 以 \n 作为分隔符, 读取一行数据, 返回 . Key 表示每行首字符偏移值, Value 表示这一行文本内容 读取 block 返回 , 进入用户自己继承的 Mapper 类中,执行用户重写的 map 函数, RecordReader 读取一行这里调用一次 Mapper 逻辑结束之后, 将 Mapper 的每条结果通过 context.write 进行 collect 数据收集. 在 collect 中, 会先对其进行分区处理,默认使用 HashPartitioner 接下来, 会将数据写入内存, 内存中这片区域叫做环形缓冲区 (默认 100M), 缓冲区的作用是 批量收集 Mapper 结果, 减少磁盘 IO 的影响. 我们的 Key/Value 对以及 Partition 的结果都会被写入缓冲区. 当然, 写入之前,Key 与 Value 值都会被序列化成字节数组 当环形缓冲区的数据达到溢写比列 (默认 0.8),也就是 80M 时,溢写线程启动, 需要对这 80MB 空间内的 Key 做排序 (Sort). 排序是 MapReduce 模型默认的行为, 这里的排序也是对序列化的字节做的排序 合并溢写文件, 每次溢写会在磁盘上生成一个临时文件 (写之前判断是否有 Combiner), 如果 Mapper 的输出结果真的很大, 有多次这样的溢写发生, 磁盘上相应的就会有多个临时文件存在. 当整个数据处理结束之后开始对磁盘中的临时文件进行 Merge 合并, 因为最终的文件只有一个, 写入磁盘, 并且为这个文件提供了一个索引文件, 以记录每个 reduce 对应数据的偏移量

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringDataJPA实体类关系映射配置方式
下一篇:边界元法的功能、案例与相关的后处理技巧
相关文章

 发表评论

暂时没有评论,来抢沙发吧~