写点什么

2023 版最新大数据面试宝典

  • 2023-02-22
    北京
  • 本文字数:14263 字

    阅读完需:约 47 分钟

此套面试题来自于各大厂的真实面试题及常问的知识点,如果能理解吃透这些问题,你的大数据能力将会大大提升,进入大厂指日可待


参考链接:2023版最新最强大数据面试宝典,附答案解析


复习大数据面试题,看这一套就够了!


本文目录:


一、Hadoop


二、Hive


三、Spark


四、Kafka


五、HBase


六、Flink


七、Clickhouse


八、Doris


九、数据仓库


十、数据湖


十一、必备 SQL 题


十二、必备算法


十三、大数据算法设计题

前言

此版本面试题相较于之前增加了很多数仓以及算法相关的题,同时新增了数据湖,必备 SQL 题,Clickhouse,Doris 等面试题。


版本更新如下:


参考链接:2023版最新最强大数据面试宝典,附答案解析

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 上存放三份, 存储策略为本地一份,同机架内其它某一节点上一份, 不同机架的某一节点上一份


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

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

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

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


HDFS 读流程


  1. Client 向 NameNode 发送 RPC 请求。请求文件 block 的位置;

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


首次启动 NameNode


  1. 格式化文件系统,为了生成 fsimage 镜像文件

  2. 启动 NameNode:

  3. 读取 fsimage 文件,将文件内容加载进内存

  4. 等待 DataNade 注册与发送 block report

  5. 启动 DataNode:

  6. 向 NameNode 注册

  7. 发送 block report

  8. 检查 fsimage 中记录的块的数量和 block report 中的块的总数是否相同

  9. 对文件系统进行操作(创建目录,上传文件,删除文件等):

  10. 此时内存中已经有文件系统改变的信息,但是磁盘中没有文件系统改变的信息,此时会将这些改变信息写入 edits 文件中,edits 文件中存储的是文件系统元数据改变的信息。


第二次启动 NameNode


  1. 读取 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,配置有两个关键点,一是必须要保证这两个 NameNode 的元数据信息必须要同步的,二是一个 NameNode 挂掉之后另一个要立马补上。


  1. 元数据信息同步在 HA 方案中采用的是“共享存储”。每次写文件时,需要将日志同步写入共享存储,这个步骤成功才能认定写文件成功。然后备份节点定期从共享存储同步日志,以便进行主备切换。

  2. 监控 NameNode 状态采用 zookeeper,两个 NameNode 节点的状态存放在 zookeeper 中,另外两个 NameNode 节点分别有一个进程监控程序,实施读取 zookeeper 中有 NameNode 的状态,来判断当前的 NameNode 是不是已经 down 机。如果 Standby 的 NameNode 节点的 ZKFC 发现主节点已经挂掉,那么就会强制给原本的 Active NameNode 节点发送强制关闭请求,之后将备用的 NameNode 设置为 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 发送写请求,只要大多数的 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 的时候,会执行以下的操作:


  1. 首先尝试调用这个旧 Active NameNode 的 HAServiceProtocol RPC 接口的 transitionToStandby 方法,看能不能把它转换为 Standby 状态。

  2. 如果 transitionToStandby 方法调用失败,那么就执行 Hadoop 配置文件之中预定义的隔离措施,Hadoop 目前主要提供两种隔离措施,通常会选择 sshfence:

  3. sshfence:通过 SSH 登录到目标机器上,执行命令 fuser 将对应的进程杀死;

  4. shellfence:执行一个用户自定义的 shell 脚本来将对应的进程隔离。

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

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


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


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

9. 请说下 HDFS 的组织架构

  1. Client:客户端

  2. 切分文件。文件上传 HDFS 的时候,Client 将文件切分成一个一个的 Block,然后进行存储

  3. 与 NameNode 交互,获取文件的位置信息

  4. 与 DataNode 交互,读取或者写入数据

  5. Client 提供一些命令来管理 HDFS,比如启动关闭 HDFS、访问 HDFS 目录及内容等

  6. NameNode:名称节点,也称主节点,存储数据的元数据信息,不存储具体的数据

  7. 管理 HDFS 的名称空间

  8. 管理数据块(Block)映射信息

  9. 配置副本策略

  10. 处理客户端读写请求

  11. DataNode:数据节点,也称从节点。NameNode 下达命令,DataNode 执行实际的操作

  12. 存储实际的数据块

  13. 执行数据块的读/写操作

  14. Secondary NameNode:并非 NameNode 的热备。当 NameNode 挂掉的时候,它并不能马上替换 NameNode 并提供服务

  15. 辅助 NameNode,分担其工作量

  16. 定期合并 Fsimage 和 Edits,并推送给 NameNode

  17. 在紧急情况下,可辅助恢复 NameNode

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

简单概述


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


详细步骤


  1. 读取数据组件 InputFormat (默认 TextInputFormat) 会通过 getSplits 方法对输入目录中的文件进行逻辑切片规划得到 block,有多少个 block 就对应启动多少个 MapTask。

  2. 将输入文件切分为 block 之后,由 RecordReader 对象 (默认是 LineRecordReader) 进行读取,以 \n 作为分隔符, 读取一行数据, 返回 <key,value>, Key 表示每行首字符偏移值,Value 表示这一行文本内容。

  3. 读取 block 返回 <key,value>, 进入用户自己继承的 Mapper 类中,执行用户重写的 map 函数,RecordReader 读取一行这里调用一次。

  4. Mapper 逻辑结束之后,将 Mapper 的每条结果通过 context.write 进行 collect 数据收集。在 collect 中,会先对其进行分区处理,默认使用 HashPartitioner。

  5. 接下来,会将数据写入内存,内存中这片区域叫做环形缓冲区(默认 100M),缓冲区的作用是 批量收集 Mapper 结果,减少磁盘 IO 的影响。我们的 Key/Value 对以及 Partition 的结果都会被写入缓冲区。当然,写入之前,Key 与 Value 值都会被序列化成字节数组

  6. 当环形缓冲区的数据达到溢写比列(默认 0.8),也就是 80M 时,溢写线程启动,需要对这 80MB 空间内的 Key 做排序 (Sort)。排序是 MapReduce 模型默认的行为,这里的排序也是对序列化的字节做的排序。

  7. 合并溢写文件,每次溢写会在磁盘上生成一个临时文件 (写之前判断是否有 Combiner),如果 Mapper 的输出结果真的很大,有多次这样的溢写发生,磁盘上相应的就会有多个临时文件存在。当整个数据处理结束之后开始对磁盘中的临时文件进行 Merge 合并,因为最终的文件只有一个写入磁盘,并且为这个文件提供了一个索引文件,以记录每个 reduce 对应数据的偏移量。

11. 请说下 MR 中 Reduce Task 的工作机制

简单描述


Reduce 大致分为 copy、sort、reduce 三个阶段,重点在前两个阶段。


copy 阶段包含一个 eventFetcher 来获取已完成的 map 列表,由 Fetcher 线程去 copy 数据,在此过程中会启动两个 merge 线程,分别为 inMemoryMerger 和 onDiskMerger,分别将内存中的数据 merge 到磁盘和将磁盘中的数据进行 merge。待数据 copy 完成之后,copy 阶段就完成了。


开始进行 sort 阶段,sort 阶段主要是执行 finalMerge 操作,纯粹的 sort 阶段,完成之后就是 reduce 阶段,调用用户定义的 reduce 函数进行处理。


详细步骤


  1. Copy 阶段:简单地拉取数据。Reduce 进程启动一些数据 copy 线程(Fetcher),通过 HTTP 方式请求 maptask 获取属于自己的文件(map task 的分区会标识每个 map task 属于哪个 reduce task ,默认 reduce task 的标识从 0 开始)。

  2. Merge 阶段:在远程拷贝数据的同时,ReduceTask 启动了两个后台线程对内存和磁盘上的文件进行合并,以防止内存使用过多或磁盘上文件过多。

  3. merge 有三种形式:内存到内存;内存到磁盘;磁盘到磁盘。默认情况下第一种形式不启用。当内存中的数据量到达一定阈值,就直接启动内存到磁盘的 merge。与 map 端类似,这也是溢写的过程,这个过程中如果你设置有 Combiner,也是会启用的,然后在磁盘中生成了众多的溢写文件。内存到磁盘的 merge 方式一直在运行,直到没有 map 端的数据时才结束,然后启动第三种磁盘到磁盘的 merge 方式生成最终的文件。

  4. 合并排序:把分散的数据合并成一个大的数据后,还会再对合并后的数据排序。

  5. 对排序后的键值对调用 reduce 方法:键相等的键值对调用一次 reduce 方法,每次调用会产生零个或者多个键值对,最后把这些输出的键值对写入到 HDFS 文件中。

12. 请说下 MR 中 Shuffle 阶段

shuffle 阶段分为四个步骤:依次为:分区,排序,规约,分组,其中前三个步骤在 map 阶段完成,最后一个步骤在 reduce 阶段完成。


shuffle 是 Mapreduce 的核心,它分布在 Mapreduce 的 map 阶段和 reduce 阶段。一般把从 Map 产生输出开始到 Reduce 取得数据作为输入之前的过程称作 shuffle。


  1. Collect 阶段:将 MapTask 的结果输出到默认大小为 100M 的环形缓冲区,保存的是 key/value,Partition 分区信息等。

  2. Spill 阶段:当内存中的数据量达到一定的阀值的时候,就会将数据写入本地磁盘,在将数据写入磁盘之前需要对数据进行一次排序的操作,如果配置了 combiner,还会将有相同分区号和 key 的数据进行排序。

  3. MapTask 阶段的 Merge:把所有溢出的临时文件进行一次合并操作,以确保一个 MapTask 最终只产生一个中间数据文件。

  4. Copy 阶段:ReduceTask 启动 Fetcher 线程到已经完成 MapTask 的节点上复制一份属于自己的数据,这些数据默认会保存在内存的缓冲区中,当内存的缓冲区达到一定的阀值的时候,就会将数据写到磁盘之上。

  5. ReduceTask 阶段的 Merge:在 ReduceTask 远程复制数据的同时,会在后台开启两个线程对内存到本地的数据文件进行合并操作。

  6. Sort 阶段:在对数据进行合并的同时,会进行排序操作,由于 MapTask 阶段已经对数据进行了局部的排序,ReduceTask 只需保证 Copy 的数据的最终整体有效性即可。


Shuffle 中的缓冲区大小会影响到 mapreduce 程序的执行效率,原则上说,缓冲区越大,磁盘 io 的次数越少,执行速度就越快。

缓冲区的大小可以通过参数调整, 参数:mapreduce.task.io.sort.mb 默认 100M

13. Shuffle 阶段的数据压缩机制了解吗

在 shuffle 阶段,可以看到数据通过大量的拷贝,从 map 阶段输出的数据,都要通过网络拷贝,发送到 reduce 阶段,这一过程中,涉及到大量的网络 IO,如果数据能够进行压缩,那么数据的发送量就会少得多。


hadoop 当中支持的压缩算法:


gzip、bzip2、LZO、LZ4、Snappy,这几种压缩算法综合压缩和解压缩的速率,谷歌的 Snappy 是最优的,一般都选择 Snappy 压缩。谷歌出品,必属精品。

14. 在写 MR 时,什么情况下可以使用规约

规约(combiner)是不能够影响任务的运行结果的局部汇总,适用于求和类,不适用于求平均值,如果 reduce 的输入参数类型和输出参数的类型是一样的,则规约的类可以使用 reduce 类,只需要在驱动类中指明规约的类即可。

15. YARN 集群的架构和工作原理知道多少

YARN 的基本设计思想是将 MapReduce V1 中的 JobTracker 拆分为两个独立的服务:ResourceManager 和 ApplicationMaster。


ResourceManager 负责整个系统的资源管理和分配,ApplicationMaster 负责单个应用程序的的管理。


  1. ResourceManager:RM 是一个全局的资源管理器,负责整个系统的资源管理和分配,它主要由两个部分组成:调度器(Scheduler)和应用程序管理器(Application Manager)。


调度器根据容量、队列等限制条件,将系统中的资源分配给正在运行的应用程序,在保证容量、公平性和服务等级的前提下,优化集群资源利用率,让所有的资源都被充分利用应用程序管理器负责管理整个系统中的所有的应用程序,包括应用程序的提交、与调度器协商资源以启动 ApplicationMaster、监控 ApplicationMaster 运行状态并在失败时重启它。


  1. ApplicationMaster:用户提交的一个应用程序会对应于一个 ApplicationMaster,它的主要功能有:

  2. 与 RM 调度器协商以获得资源,资源以 Container 表示。

  3. 将得到的任务进一步分配给内部的任务。

  4. 与 NM 通信以启动/停止任务。

  5. 监控所有的内部任务状态,并在任务运行失败的时候重新为任务申请资源以重启任务。

  6. NodeManager:NodeManager 是每个节点上的资源和任务管理器,一方面,它会定期地向 RM 汇报本节点上的资源使用情况和各个 Container 的运行状态;另一方面,他接收并处理来自 AM 的 Container 启动和停止请求。

  7. Container:Container 是 YARN 中的资源抽象,封装了各种资源。一个应用程序会分配一个 Container,这个应用程序只能使用这个 Container 中描述的资源。不同于 MapReduceV1 中槽位 slot 的资源封装,Container 是一个动态资源的划分单位,更能充分利用资源。

16. YARN 的任务提交流程是怎样的

当 jobclient 向 YARN 提交一个应用程序后,YARN 将分两个阶段运行这个应用程序:一是启动 ApplicationMaster;第二个阶段是由 ApplicationMaster 创建应用程序,为它申请资源,监控运行直到结束。具体步骤如下:


  1. 用户向 YARN 提交一个应用程序,并指定 ApplicationMaster 程序、启动 ApplicationMaster 的命令、用户程序。

  2. RM 为这个应用程序分配第一个 Container,并与之对应的 NM 通讯,要求它在这个 Container 中启动应用程序 ApplicationMaster。

  3. ApplicationMaster 向 RM 注册,然后拆分为内部各个子任务,为各个内部任务申请资源,并监控这些任务的运行,直到结束。

  4. AM 采用轮询的方式向 RM 申请和领取资源。

  5. RM 为 AM 分配资源,以 Container 形式返回。

  6. AM 申请到资源后,便与之对应的 NM 通讯,要求 NM 启动任务。

  7. NodeManager 为任务设置好运行环境,将任务启动命令写到一个脚本中,并通过运行这个脚本启动任务。

  8. 各个任务向 AM 汇报自己的状态和进度,以便当任务失败时可以重启任务。

  9. 应用程序完成后,ApplicationMaster 向 ResourceManager 注销并关闭自己。

17. YARN 的资源调度三种模型了解吗

在 Yarn 中有三种调度器可以选择:FIFO Scheduler ,Capacity Scheduler,Fair Scheduler。


Apache 版本的 hadoop 默认使用的是 Capacity Scheduler 调度方式。CDH 版本的默认使用的是 Fair Scheduler 调度方式


FIFO Scheduler(先来先服务):


FIFO Scheduler 把应用按提交的顺序排成一个队列,这是一个先进先出队列,在进行资源分配的时候,先给队列中最头上的应用进行分配资源,待最头上的应用需求满足后再给下一个分配,以此类推。


FIFO Scheduler 是最简单也是最容易理解的调度器,也不需要任何配置,但它并不适用于共享集群。大的应用可能会占用所有集群资源,这就导致其它应用被阻塞,比如有个大任务在执行,占用了全部的资源,再提交一个小任务,则此小任务会一直被阻塞。


Capacity Scheduler(能力调度器):


对于 Capacity 调度器,有一个专门的队列用来运行小任务,但是为小任务专门设置一个队列会预先占用一定的集群资源,这就导致大任务的执行时间会落后于使用 FIFO 调度器时的时间。


Fair Scheduler(公平调度器):


在 Fair 调度器中,我们不需要预先占用一定的系统资源,Fair 调度器会为所有运行的 job 动态的调整系统资源。


比如:当第一个大 job 提交时,只有这一个 job 在运行,此时它获得了所有集群资源;当第二个小任务提交后,Fair 调度器会分配一半资源给这个小任务,让这两个任务公平的共享集群资源。


需要注意的是,在 Fair 调度器中,从第二个任务提交到获得资源会有一定的延迟,因为它需要等待第一个任务释放占用的 Container。小任务执行完成之后也会释放自己占用的资源,大任务又获得了全部的系统资源。最终的效果就是 Fair 调度器即得到了高的资源利用率又能保证小任务及时完成。

Hive

1. Hive 内部表和外部表的区别

未被 external 修饰的是内部表,被 external 修饰的为外部表。


区别


  1. 内部表数据由 Hive 自身管理,外部表数据由 HDFS 管理;

  2. 内部表数据存储的位置是hive.metastore.warehouse.dir(默认:/user/hive/warehouse),外部表数据的存储位置由自己制定(如果没有 LOCATION,Hive 将在 HDFS 上的/user/hive/warehouse文件夹下以外部表的表名创建一个文件夹,并将属于这个表的数据存放在这里);

  3. 删除内部表会直接删除元数据(metadata)及存储数据;删除外部表仅仅会删除元数据,HDFS 上的文件并不会被删除

2. Hive 有索引吗

Hive 支持索引(3.0 版本之前),但是 Hive 的索引与关系型数据库中的索引并不相同,比如,Hive 不支持主键或者外键。并且 Hive 索引提供的功能很有限,效率也并不高,因此 Hive 索引很少使用。


  • 索引适用的场景:


适用于不更新的静态字段。以免总是重建索引数据。每次建立、更新数据后,都要重建索引以构建索引表。


  • Hive 索引的机制如下:


hive 在指定列上建立索引,会产生一张索引表(Hive 的一张物理表),里面的字段包括:索引列的值、该值对应的 HDFS 文件路径、该值在文件中的偏移量。


Hive 0.8 版本后引入 bitmap 索引处理器,这个处理器适用于去重后,值较少的列(例如,某字段的取值只可能是几个枚举值)因为索引是用空间换时间,索引列的取值过多会导致建立 bitmap 索引表过大。


注意:Hive 中每次有数据时需要及时更新索引,相当于重建一个新表,否则会影响数据查询的效率和准确性,Hive 官方文档已经明确表示 Hive 的索引不推荐被使用,在新版本的 Hive 中已经被废弃了


扩展:Hive 是在 0.7 版本之后支持索引的,在 0.8 版本后引入 bitmap 索引处理器,在 3.0 版本开始移除索引的功能,取而代之的是 2.3 版本开始的物化视图,自动重写的物化视图替代了索引的功能。

3. 运维如何对 Hive 进行调度

  1. 将 hive 的 sql 定义在脚本当中;

  2. 使用 azkaban 或者 oozie 进行任务的调度;

  3. 监控任务调度页面。

4. ORC、Parquet 等列式存储的优点

ORC 和 Parquet 都是高性能的存储方式,这两种存储格式总会带来存储和性能上的提升。


Parquet:


  1. Parquet 支持嵌套的数据模型,类似于 Protocol Buffers,每一个数据模型的 schema 包含多个字段,每一个字段有三个属性:重复次数、数据类型和字段名。

  2. 重复次数可以是以下三种:required(只出现 1 次),repeated(出现 0 次或多次),optional(出现 0 次或 1 次)。每一个字段的数据类型可以分成两种:group(复杂类型)和 primitive(基本类型)。

  3. Parquet 中没有 Map、Array 这样的复杂数据结构,但是可以通过 repeated 和 group 组合来实现的。

  4. 由于 Parquet 支持的数据模型比较松散,可能一条记录中存在比较深的嵌套关系,如果为每一条记录都维护一个类似的树状结可能会占用较大的存储空间,因此 Dremel 论文中提出了一种高效的对于嵌套数据格式的压缩算法:Striping/Assembly 算法。通过 Striping/Assembly 算法,parquet 可以使用较少的存储空间表示复杂的嵌套格式,并且通常 Repetition level 和 Definition level 都是较小的整数值,可以通过 RLE 算法对其进行压缩,进一步降低存储空间。

  5. Parquet 文件是以二进制方式存储的,是不可以直接读取和修改的,Parquet 文件是自解析的,文件中包括该文件的数据和元数据。


ORC:


  1. ORC 文件是自描述的,它的元数据使用 Protocol Buffers 序列化,并且文件中的数据尽可能的压缩以降低存储空间的消耗。

  2. 和 Parquet 类似,ORC 文件也是以二进制方式存储的,所以是不可以直接读取,ORC 文件也是自解析的,它包含许多的元数据,这些元数据都是同构 ProtoBuffer 进行序列化的。

  3. ORC 会尽可能合并多个离散的区间尽可能的减少 I/O 次数。

  4. ORC 中使用了更加精确的索引信息,使得在读取数据时可以指定从任意一行开始读取,更细粒度的统计信息使得读取 ORC 文件跳过整个 row group,ORC 默认会对任何一块数据和索引信息使用 ZLIB 压缩,因此 ORC 文件占用的存储空间也更小。

  5. 在新版本的 ORC 中也加入了对 Bloom Filter 的支持,它可以进一步提升谓词下推的效率,在 Hive 1.2.0 版本以后也加入了对此的支持。

5. 数据建模用的哪些模型?

1. 星型模型


星形模式(Star Schema)是最常用的维度建模方式。星型模式是以事实表为中心,所有的维度表直接连接在事实表上,像星星一样。星形模式的维度建模由一个事实表和一组维表成,且具有以下特点:


a. 维表只和事实表关联,维表之间没有关联;


b. 每个维表主键为单列,且该主键放置在事实表中,作为两边连接的外键;


c. 以事实表为核心,维表围绕核心呈星形分布。

2. 雪花模型


雪花模式(Snowflake Schema)是对星形模式的扩展。雪花模式的维度表可以拥有其他维度表的,虽然这种模型相比星型更规范一些,但是由于这种模型不太容易理解,维护成本比较高,而且性能方面需要关联多层维表,性能比星型模型要低。

3. 星座模型


星座模式是星型模式延伸而来,星型模式是基于一张事实表的,而星座模式是基于多张事实表的,而且共享维度信息。前面介绍的两种维度建模方法都是多维表对应单事实表,但在很多时候维度空间内的事实表不止一个,而一个维表也可能被多个事实表用到。在业务发展后期,绝大部分维度建模都采用的是星座模式。


数仓建模详细介绍可查看:通俗易懂数仓建模

6. 为什么要对数据仓库分层?

  • 用空间换时间,通过大量的预处理来提升应用系统的用户体验(效率),因此数据仓库会存在大量冗余的数据。

  • 如果不分层的话,如果源业务系统的业务规则发生变化将会影响整个数据清洗过程,工作量巨大。

  • 通过数据分层管理可以简化数据清洗的过程,因为把原来一步的工作分到了多个步骤去完成,相当于把一个复杂的工作拆成了多个简单的工作,把一个大的黑盒变成了一个白盒,每一层的处理逻辑都相对简单和容易理解,这样我们比较容易保证每一个步骤的正确性,当数据发生错误的时候,往往我们只需要局部调整某个步骤即可。


数据仓库详细介绍可查看:万字详解整个数据仓库建设体系

7. 使用过 Hive 解析 JSON 串吗

Hive 处理 json 数据总体来说有两个方向的路走


  1. 将 json 以字符串的方式整个入 Hive 表,然后通过使用 UDF 函数解析已经导入到 hive 中的数据,比如使用LATERAL VIEW json_tuple的方法,获取所需要的列名。

  2. 在导入之前将 json 拆成各个字段,导入 Hive 表的数据是已经解析过的。这将需要使用第三方的 SerDe。


详细介绍可查看:Hive解析Json数组超全讲解

8. sort by 和 order by 的区别

order by 会对输入做全局排序,因此只有一个 reducer(多个 reducer 无法保证全局有序)只有一个 reducer,会导致当输入规模较大时,需要较长的计算时间。


sort by 不是全局排序,其在数据进入 reducer 前完成排序.因此,如果用 sort by 进行排序,并且设置 mapred.reduce.tasks>1, 则 sort by 只保证每个 reducer 的输出有序,不保证全局有序

9. 数据倾斜怎么解决

数据倾斜问题主要有以下几种:


  1. 空值引发的数据倾斜

  2. 不同数据类型引发的数据倾斜

  3. 不可拆分大文件引发的数据倾斜

  4. 数据膨胀引发的数据倾斜

  5. 表连接时引发的数据倾斜

  6. 确实无法减少数据量引发的数据倾斜


以上倾斜问题的具体解决方案可查看:Hive千亿级数据倾斜解决方案


注意:对于 left join 或者 right join 来说,不会对关联的字段自动去除 null 值,对于 inner join 来说,会对关联的字段自动去除 null 值。


小伙伴们在阅读时注意下,在上面的文章(Hive 千亿级数据倾斜解决方案)中,有一处 sql 出现了上述问题(举例的时候原本是想使用 left join 的,结果手误写成了 join)。此问题由公众号读者发现,感谢这位读者指正。

10. Hive 小文件过多怎么解决

1. 使用 hive 自带的 concatenate 命令,自动合并小文件

使用方法:


#对于非分区表alter table A concatenate;#对于分区表alter table B partition(day=20201224) concatenate;
复制代码

复制代码


注意:

1、concatenate 命令只支持 RCFILE 和 ORC 文件类型。

2、使用 concatenate 命令合并小文件时不能指定合并后的文件数量,但可以多次执行该命令。

3、当多次使用 concatenate 后文件数量不在变化,这个跟参数 mapreduce.input.fileinputformat.split.minsize=256mb 的设置有关,可设定每个文件的最小 size。

2. 调整参数减少 Map 数量

设置 map 输入合并小文件的相关参数(执行 Map 前进行小文件合并):


在 mapper 中将多个文件合成一个 split 作为输入(CombineHiveInputFormat底层是 Hadoop 的CombineFileInputFormat方法):


set hive.input.format=org.apache.hadoop.hive.ql.io.CombineHiveInputFormat; -- 默认
复制代码

复制代码


每个 Map 最大输入大小(这个值决定了合并后文件的数量):


set mapred.max.split.size=256000000;   -- 256M
复制代码

复制代码


一个节点上 split 的至少大小(这个值决定了多个 DataNode 上的文件是否需要合并):


set mapred.min.split.size.per.node=100000000;  -- 100M
复制代码

复制代码


一个交换机下 split 的至少大小(这个值决定了多个交换机上的文件是否需要合并):


set mapred.min.split.size.per.rack=100000000;  -- 100M
复制代码

复制代码

3. 减少 Reduce 的数量

reduce 的个数决定了输出的文件的个数,所以可以调整 reduce 的个数控制 hive 表的文件数量。


hive 中的分区函数 distribute by 正好是控制 MR 中 partition 分区的,可以通过设置 reduce 的数量,结合分区函数让数据均衡的进入每个 reduce 即可:


#设置reduce的数量有两种方式,第一种是直接设置reduce个数set mapreduce.job.reduces=10;#第二种是设置每个reduce的大小,Hive会根据数据总大小猜测确定一个reduce个数set hive.exec.reducers.bytes.per.reducer=5120000000; -- 默认是1G,设置为5G#执行以下语句,将数据均衡的分配到reduce中set mapreduce.job.reduces=10;insert overwrite table A partition(dt)select * from Bdistribute by rand();
复制代码

复制代码


对于上述语句解释:如设置 reduce 数量为 10,使用 rand(), 随机生成一个数 x % 10 ,这样数据就会随机进入 reduce 中,防止出现有的文件过大或过小。

4. 使用 hadoop 的 archive 将小文件归档

Hadoop Archive 简称 HAR,是一个高效地将小文件放入 HDFS 块中的文件存档工具,它能够将多个小文件打包成一个 HAR 文件,这样在减少 namenode 内存使用的同时,仍然允许对文件进行透明的访问。


#用来控制归档是否可用set hive.archive.enabled=true;#通知Hive在创建归档时是否可以设置父目录set hive.archive.har.parentdir.settable=true;#控制需要归档文件的大小set har.partfile.size=1099511627776;使用以下命令进行归档:ALTER TABLE A ARCHIVE PARTITION(dt='2021-05-07', hr='12');对已归档的分区恢复为原文件:ALTER TABLE A UNARCHIVE PARTITION(dt='2021-05-07', hr='12');
复制代码

复制代码


注意:

归档的分区可以查看不能 insert overwrite,必须先 unarchive


Hive 小文件问题具体可查看:解决hive小文件过多问题

11. Hive 优化有哪些

1. 数据存储及压缩:

针对 hive 中表的存储格式通常有 orc 和 parquet,压缩格式一般使用 snappy。相比与 textfile 格式表,orc 占有更少的存储。因为 hive 底层使用 MR 计算架构,数据流是 hdfs 到磁盘再到 hdfs,而且会有很多次,所以使用 orc 数据格式和 snappy 压缩策略可以降低 IO 读写,还能降低网络传输量,这样在一定程度上可以节省存储,还能提升 hql 任务执行效率;

2. 通过调参优化:

并行执行,调节 parallel 参数;


调节 jvm 参数,重用 jvm;


设置 map、reduce 的参数;开启 strict mode 模式;


关闭推测执行设置。

3. 有效地减小数据集将大表拆分成子表;结合使用外部表和分区表。
4. SQL 优化
  • 大表对大表:尽量减少数据集,可以通过分区表,避免扫描全表或者全字段;

  • 大表对小表:设置自动识别小表,将小表放入内存中去执行。


Hive 优化详细剖析可查看:Hive企业级性能优化

12. Tez 引擎优点?

Tez 可以将多个有依赖的作业转换为一个作业,这样只需写一次 HDFS,且中间节点较少,从而大大提升作业的计算性能。


Mr/tez/spark 区别:


Mr 引擎:多 job 串联,基于磁盘,落盘的地方比较多。虽然慢,但一定能跑出结果。一般处理,周、月、年指标


Spark 引擎:虽然在 Shuffle 过程中也落盘,但是并不是所有算子都需要 Shuffle,尤其是多算子过程,中间过程不落盘 DAG 有向无环图。 兼顾了可靠性和效率。一般处理天指标


Tez 引擎:完全基于内存。 注意:如果数据量特别大,慎重使用。容易 OOM。一般用于快速出结果,数据量比较小的场景

参考

参考链接:2023版最新最强大数据面试宝典,附答案解析

发布于: 刚刚阅读数: 8
用户头像

InfoQ签约作者 2020-11-10 加入

文章首发于公众号:五分钟学大数据。大数据领域原创技术号,深入大数据技术

评论

发布
暂无评论
2023 版最新大数据面试宝典_大数据_五分钟学大数据_InfoQ写作社区