大数据面试问答-Hadoop/Hive/HDFS/Yarn
1. Hadoop
1.1 MapReduce
1.1.1 Hive语句转MapReduce过程
可分为 SQL解析阶段、语义分析阶段、逻辑计划生成阶段、逻辑优化阶段、物理计划生成阶段。
SQL解析阶段
词法分析(Lexical Analysis):使用Antlr3将SQL字符串拆分为有意义的token序列
语法分析(Syntax Analysis):根据Hive语法规则构建抽象语法树(AST)
示例转换:SELECT a FROM t WHERE b > 10
会被解析为:
TOK_QUERY
TOK_FROM (TOK_TABREF (TOK_TABNAME t))
TOK_INSERT
TOK_DESTINATION (TOK_DIR TOK_TMP_FILE)
TOK_SELECT
TOK_SELEXPR (TOK_TABLE_OR_COL a)
TOK_WHERE (> (TOK_TABLE_OR_COL b) 10)
语义分析阶段
元数据验证:检查表、列是否存在,类型是否匹配
隐式类型转换:如将字符串与数字比较时的自动转换
UDF解析:确定用户定义函数的实现类
权限验证:检查用户是否有执行该查询的权限
逻辑计划生成
Operator Tree构建:将AST转换为由TableScanOperator、FilterOperator、SelectOperator等组成的DAG
初始逻辑计划:对于SELECT a FROM t WHERE b > 10
会生成:
TableScanOperator(t)
↓
FilterOperator(b > 10)
↓
SelectOperator(a)
↓
FileSinkOperator(output)
逻辑优化阶段
优化规则应用:
列剪枝(Column Pruning):只读取需要的列
谓词下推(Predicate Pushdown):尽早过滤数据
分区裁剪(Partition Pruning):只扫描相关分区
MapJoin优化:自动将小表JOIN转换为Map端JOIN
合并操作:如合并相邻的FilterOperator
物理计划生成
Operator到MR任务的映射:
TableScanOperator → Map任务输入
FilterOperator/SelectOperator → Map任务处理逻辑
GroupByOperator → Reduce阶段聚合
JoinOperator → 根据大小决定CommonJoin或MapJoin
1.1.2 MapReduce过程
“MapReduce是Hadoop的核心计算模型,其过程可分为Map、Shuffle、Reduce三个阶段”
阶段一:Map阶段
输入分片:HDFS数据按块(默认128MB)划分成多个Split,每个Split启动一个Map Task
数据处理:
列剪枝:若查询仅涉及部分列,Hive优化器会跳过无关列解析(如ORC文件只读取所需列)
谓词下推:将WHERE条件推送到存储层,减少数据读取量(例如Parquet文件的行组过滤)
Map逻辑:执行投影、转换、打标签(Join时标记表来源),输出<k,v>对(k通常是group by/join key)
示例:处理SELECT user_id, SUM(order_amt) FROM orders WHERE dt='2023-01' GROUP BY user_id
时:
Map Task过滤dt='2023-01’的记录
提取user_id和order_amt,输出<user_id, order_amt>
阶段二:Shuffle阶段
环形缓冲区:Map输出先写入内存缓冲区(默认100MB),达到阈值后spill到磁盘
分区排序:
按key的hash值分区(确保相同key进入同一Reducer)
每个分区内按key排序(为Reduce阶段的归并排序做准备)
Combiner优化:在Map端进行本地聚合(如sum操作),减少网络传输量
调优经验:曾通过调整mapreduce.task.io.sort.mb提升shuffle效率20%
阶段三:Reduce阶段
数据拉取:Reducer通过HTTP从各Map节点抓取对应分区的数据
归并排序:对来自不同Map的相同key数据进行多路归并
聚合计算:执行最终聚合(如SUM)、Join关联等逻辑
结果输出:通过OutputFormat写入HDFS,Hive默认使用TextOutputFormat
总结:map阶段从hdfs取数据块,并根据查询语句输出kv键值对;shuffle阶段将相同k的键值对分到同一个reduce里;reduce阶段将键值对根据查询语句进行聚合得到结果。
1.1.2.1 MapReduce引申问题
MR过程中存在2次聚合:
1是map阶段生成键值对后,输出到磁盘之前,会先根据k值进行聚合,此过程为Combiner阶段,可以通过// 在MapReduce作业中显式设置Combiner类 job.setCombinerClass(SumCombiner.class);
开启,好处在于可以提前聚合,减少shuffle阶段的数据量,提升效率,但需注意,因为同一个k的键值对并不一定处在同一个map任务里,所以Combiner只能解决部分聚合,而不是所有。
2是在reduce阶段从shuffle阶段获取的键值对,对其进行聚合,可以理解reduce是全局聚合,而combiner是局部聚合。
2. Hive
Hive 不是传统的关系型数据库,而是一个基于Hadoop的数据仓库工具,提供类SQL(HiveQL)的查询功能。
核心差异:
存储与计算:Hive依赖HDFS存储数据,依赖MapReduce/Tez/Spark进行计算。
延迟:Hive适用于高延迟的批处理场景(OLAP),而非低延迟的实时事务处理(OLTP)。
功能特性:支持表结构定义、数据加载、复杂查询,但不支持事务和实时更新(Hive 3+支持有限的事务)。
2.1 内部表和外部表
特性 | 内部表 | 外部表 |
---|---|---|
数据管理 | Hive管理数据和元数据 | Hive仅管理元数据,数据由用户维护 |
删除表时 | 删除元数据和HDFS数据 | 仅删除元数据,HDFS数据保留 |
语法 | CREATE TABLE table_name (…) | CREATE EXTERNAL TABLE table_name (…) LOCATION ‘/path’ |
适用场景 | 临时数据、测试场景 | 多系统共享数据(如Spark、Presto) |
2.2 SORT BY vs ORDER BY vs DISTRIBUTE BY vs CLUSTER BY
ORDER BY
全局排序但性能低,SORT BY
局部排序,CLUSTER BY
为分桶,DISTRIBUTE BY
为分区,分区和分桶在性能优化文章有详细介绍。
2.3 Join的类型
Join 类型 | 别称 | 触发条件 | 核心原理 | 适用场景 | 资源消耗 |
---|---|---|---|---|---|
Map Join | Broadcast Join | 小表 + 大表 | 将小表广播到所有 Map 任务内存中 | 小表可完全装入内存 | 低(无 Shuffle) |
Bucket Map Join | 分桶优化 Join | 分桶对齐的大表 + 大表 | 利用分桶的物理分布,跳过 Shuffle | 大表预先分桶且 JOIN Key 对齐 | 低(无 Shuffle) |
Sort-Merge Join | SMB Join | 分桶且排序对齐的大表 + 大表 | 分桶排序后按序合并 | 大表分桶且排序,JOIN Key 对齐 | 中(部分排序开销) |
Common Join | Reduce Join | 默认策略(无优化条件时) | Shuffle + Reduce 阶段处理 | 通用场景 | 高(Shuffle 开销大) |
Map Join 和 Bucket Map Join 是避免 Shuffle 的核心优化手段,前者依赖小表广播,后者依赖分桶对齐。
Sort-Merge Join 是对分桶场景的进一步优化,适合排序对齐的大表。
Common Join 是保底策略,但应尽量避免其触发(通过分桶、分区、过滤条件优化)。
实际场景中,分桶 + Bucket Map Join 是大表 Join 的黄金组合,而 Map Join 是小表 Join 大表的标准方案。通过合理设计表结构和参数配置,可显著提升 Hive 查询性能。
3. Hdfs
HDFS(Hadoop Distributed File System)是Hadoop生态的分布式文件系统,设计用于存储超大规模数据集(PB级以上),并在廉价硬件集群上提供高容错性和高吞吐量访问。
特点是:
容错优先:硬件故障是常态而非异常,HDFS通过自动冗余和故障恢复保障数据可靠性。
流式数据访问:适合批处理(如MapReduce),而非低延迟的随机读写。
大文件存储:优化大文件存储(如TB级),避免小文件导致的元数据膨胀。
3.1 核心架构与组件
主从架构:
NameNode(主节点):
管理文件系统元数据(命名空间、块映射、访问控制)。
单点问题(早期版本)通过HA(High Availability)解决:双NameNode(Active/Standby)+ JournalNodes(元数据同步)。
DataNode(从节点):
存储实际数据块,定期向NameNode发送心跳和块报告。
Secondary NameNode(辅助角色):
合并FsImage与EditLog(非HA方案下),防止EditLog过大。
数据存储机制:
分块存储:文件按块(Block,默认128MB/256MB)切分,分散存储在多台DataNode。
副本机制:默认3副本,按机架感知策略(Rack Awareness)分布(同机架→跨机架),平衡容错与网络效率。
3.2 读写流程与原理
写入流程:
客户端向NameNode申请写入,获取可写入的DataNode列表。
数据按管道(Pipeline)写入:客户端→DN1→DN2→DN3,确保副本顺序写入。
写入成功后,NameNode更新元数据。
读取流程:
客户端向NameNode获取文件块的位置信息。
直接从最近的DataNode读取数据(就近原则),支持并行读取多个块。
3.2 小文件
HDFS 默认块大小确实为 128MB(或 256MB,取决于版本),但实际应用中仍然存在大量小文件,主要原因如下:
(1)用户直接上传小文件
场景:用户直接将大量小文件(如日志、图片、CSV 文件)上传到 HDFS,未做合并。
原因:业务需求需要保留原始文件(如用户上传的图片需按原文件访问)。
缺乏对小文件合并的意识或工具支持。
(2)日志切割
场景:日志收集工具(如 Flume、Logstash)按时间或大小切割日志文件。
示例:Flume 配置按小时滚动日志文件,若每小时日志量仅 10MB,则会生成大量 10MB 的小文件。
(3)ETL 过程的分区输出
场景:数据处理任务(如 Hive、Spark)按分区字段(如日期、城市)输出结果。
示例:
某 Hive 任务按 dt=20231001、city=beijing 分区,若每个分区的数据量仅 1MB,则每个分区生成一个小文件。
危害:即使文件只有1KB,也会在NameNode中占用元数据,并且存储时占用一个块的空间,导致资源浪费
小文件合并的方法:
Hadoop Archive (HAR)
原理:
将多个小文件打包成一个HAR文件,减少NameNode元数据条目。
HAR文件在HDFS中表现为一个索引文件(.har.index)和数据文件(part-0…part-N),通过har://协议访问内部文件。
命令示例:
hadoop archive -archiveName output.har -p /input/dir /output/dir
优点:
显著减少NameNode元数据量。
兼容HDFS原生操作,无需修改上层应用。
缺点:
访问HAR内文件时需额外解析索引,性能略低。
不支持修改,仅适用于归档冷数据。
Hive表分区与ORC/Parquet格式
原理:
将小文件导入Hive表,按分区(如日期、类型)组织数据,并使用列式存储格式(ORC/Parquet)合并文件。
列式格式自动合并小文件为更大的块,并支持压缩。
实现方式:
-- 插入数据时合并小文件
SET hive.merge.mapfiles = true;
SET hive.merge.size.per.task = 256000000; -- 合并后目标大小
INSERT OVERWRITE TABLE target_table PARTITION(dt='20231001')
SELECT * FROM source_table;
优点:同时优化存储和计算性能,适合数仓场景。
3.3 Secondary NameNode
Secondary NameNode(SNN)是 HDFS 早期版本(非 HA 架构)中的一个关键角色,主要用于辅助 NameNode 管理元数据,但其名称容易引起误解——它并不是 NameNode 的热备节点,无法直接接管 NameNode 的工作。
非 HA 集群:Secondary NameNode 是元数据管理的“辅助清洁工”,负责定期合并 FsImage 和 EditLog,防止日志文件过大占用空间增加风险。
HA 集群:其职责由 Standby NameNode 承担,实现高可用性与实时元数据同步。
4. Yarn
YARN(Yet Another Resource Negotiator)是Hadoop 2.0引入的分布式资源管理框架,核心职责是解耦资源管理与计算框架,实现统一的集群资源调度。
4.1 核心架构与组件
(1)核心角色
ResourceManager(RM):
全局资源调度器:负责整个集群的资源分配(通过Scheduler模块)。
核心功能:
接收客户端提交的应用(如MapReduce/Spark作业)。
启动ApplicationMaster(AM)并监控其状态。
处理NodeManager(NM)心跳,管理节点资源状态。
高可用(HA):通过Active/Standby双节点避免单点故障。
NodeManager(NM):
单节点资源代理:管理单个节点的资源(如CPU、内存、磁盘)。
职责:
向RM汇报资源状态。
启动/监控Container(资源容器),执行具体任务(如Map Task)。
ApplicationMaster(AM):
应用级管家:每个应用(如一个Spark作业)对应一个AM。
职责:
向RM申请资源(Container)。
与NM协作启停任务,监控任务状态。
容错处理(如任务失败重试)。
4.2 Yarn的工作流程
以提交一个MapReduce作业为例:
作业提交:客户端向RM提交应用(包含JAR包、配置、资源需求)。
启动AM:RM分配一个Container启动AM。
资源申请:AM向RM申请运行任务的Container资源。
任务分配:RM根据调度策略(如容量调度器)分配资源,AM与NM协作启停任务。
任务执行与监控:AM监控任务状态,失败时重试或上报RM。
作业完成:AM向RM注销,释放资源。
4.2 Yarn的资源调度策略
(1)调度器类型
FIFO Scheduler:先进先出,简单但易导致资源饥饿(大作业阻塞小作业)。
Capacity Scheduler(默认):划分多个队列,每个队列设置资源容量,队列内采用FIFO。
适用场景:多租户共享集群,需资源隔离。
Fair Scheduler:动态平衡资源分配,保证所有作业公平共享资源。
适用场景:交互式分析(如临时查询需快速响应)。
(2)资源分配模型
基于资源预留:AM可预先申请资源,避免资源碎片。
资源超卖:支持配置虚拟核(vCore),物理核可超量分配。