大数据面试问答-Spark

1. Spark

1.1 Spark定位

"Apache Spark是一个基于内存的分布式计算框架,旨在解决Hadoop MapReduce在迭代计算和实时处理上的性能瓶颈。

1.2 核心架构

Spark架构中有三个关键角色:

Driver:解析代码生成DAG,协调任务调度(TaskScheduler)。

Executor:在Worker节点执行具体Task,通过BlockManager管理数据缓存。

Cluster Manager:支持Standalone/YARN/Kubernetes资源调度。

1.3 基础概念

1.3.1 RDD

RDD本质上是一个逻辑抽象,而非物理存储的实际数据。

RDD 不直接存储数据本身,而是通过四个核心元数据描述如何生成数据:

1.数据来源(数据块指针)

若从HDFS读取:记录文件路径、分块偏移量(如hdfs://data.log, [0-128MB])。

若从父RDD转换而来:记录依赖关系(如父RDD ID + map函数)。

2.依赖关系(Dependencies)

记录父RDD与当前RDD的依赖类型(宽/窄依赖),用于容错恢复和Stage划分。

示例:RDD2 = RDD1.filter(...).map(...) 的依赖链为 Narrow -> Narrow

3.计算函数(Compute Function)

定义从父RDD或外部数据生成当前RDD分区的逻辑(如map(func)中的func)。

惰性执行:函数不会立即执行,直到遇到Action操作触发计算。

4.分区策略(Partitioner)

决定数据在计算过程中的分布方式:
HashPartitioner:按Key哈希值分配(用于groupByKey)。
RangePartitioner:按Key范围分配(用于排序操作)。

1.3.1.1 RDD中的分区

分区的本质

分区(Partition) 是 RDD 的最小计算单元,表示数据在逻辑上的分块。

虽然 RDD 不存储实际数据,但它通过分区描述数据的分布方式(类似"目录"记录"文件"的位置)。

分区的来源

初始 RDD:从外部数据源(如 HDFS、本地文件)加载时,分区规则由数据源决定。

例如:HDFS 文件按 128MB 分块,每个块对应一个 RDD 分区。

转换后的 RDD:由父 RDD 的分区规则和转换算子类型决定。

窄依赖(如 map):子 RDD 分区与父 RDD 一一对应。

宽依赖(如 groupByKey):子 RDD 分区由 Shuffle 重新分配。

维度 分区的核心作用
并行计算 每个分区由一个 Task 处理,分区数决定并行度(如 100 个分区 → 100 个 Task)
数据本地性 调度器优先将 Task 分配到数据所在节点(避免跨节点传输)
容错 分区是容错的最小单元,丢失时只需重新计算该分区

1.3.2 宽窄依赖

窄依赖(Narrow Dependency)

父RDD的每个分区最多被一个子RDD分区依赖。(一对一

无需跨节点数据传输(数据局部性优化)。

容错成本低:若子分区数据丢失,只需重新计算对应的父分区。

示例操作:map、filter、union。

宽依赖(Wide Dependency / Shuffle Dependency)

父RDD的每个分区可能被多个子RDD分区依赖。(多对多

必须跨节点传输数据(Shuffle过程)。

容错成本高:若子分区数据丢失,需重新计算所有相关父分区。

示例操作:groupByKey、reduceByKey、join(非相同分区情况)。

维度 窄依赖 宽依赖
Stage划分 允许合并到同一Stage(流水线执行) 强制划分Stage边界(需等待Shuffle完成)
数据移动 无Shuffle,本地计算优先 需跨节点Shuffle,网络IO开销大
Stage划分 可合并操作(如map+filter合并执行) 需手动调优(如调整分区数或Partitioner)

1.3.3 算子

算子的定义

在 Spark 中,算子(Operator) 是指对 RDD 或 DataFrame 进行操作的函数。所有数据处理逻辑均通过算子组合实现,类似于烹饪中的"步骤"(如切菜、翻炒、调味)。

算子分为两类:

转换算子(Transformations):定义数据处理逻辑,生成新的 RDD(惰性执行,不立即计算)。比如mapfilter

python 复制代码
# 转换算子链(无实际计算)
rdd = sc.textFile("data.txt") \
        .filter(lambda line: "error" in line) \  # 过滤错误日志
        .map(lambda line: line.split(",")[0])    # 提取第一列

宽窄依赖与算子对照表,只涉及转换算子,行动算子不涉及宽窄依赖

依赖类型 转换算子 含义及示例 触发Shuffle?
窄依赖 map(func) 对每个元素应用函数,一对一转换。例:rdd.map(x => x*2)
窄依赖 filter(func) 过滤满足条件的元素。例:rdd.filter(x => x > 10)
窄依赖 flatMap(func) 一对多转换(输出为多个元素)。例:rdd.flatMap(x => x.split(" "))
窄依赖 mapPartitions(func) 对每个分区迭代处理(更高效)。例:rdd.mapPartitions(it => it.map(_*2))
窄依赖 union(otherRDD) 合并两个RDD,保留各自分区。例:rdd1.union(rdd2)
窄依赖 glom() 将每个分区的元素合并为一个数组。例:rdd.glom() → 得到分区数组的RDD
窄依赖 mapValues(func) 仅对键值对的Value进行转换(保留原Key的分区)。例:pairRdd.mapValues(_+1)
宽依赖 groupByKey() 按键分组,相同Key的值合并为迭代器。例:pairRdd.groupByKey()
宽依赖 reduceByKey(func) 按键聚合(本地预聚合减少Shuffle量)。例:pairRdd.reduceByKey(+)
宽依赖 join(otherRDD) 按键关联两个RDD(相同Key的值连接)。例:rdd1.join(rdd2)
宽依赖 distinct() 去重(内部用reduceByKey实现)。例:rdd.distinct()
宽依赖 repartition(num) 显式调整分区数(触发全量Shuffle)。例:rdd.repartition(100)
宽依赖 sortByKey() 按键排序(需全局Shuffle)。例:pairRdd.sortByKey()
宽依赖 cogroup(otherRDD) 对多个RDD按Key联合分组(类似SQL全外连接)。例:rdd1.cogroup(rdd2)
宽依赖 intersection(otherRDD) 求两个RDD的交集。例:rdd1.intersection(rdd2)
宽依赖 subtractByKey(otherRDD) 按Key差集(移除与另一RDD匹配的Key)。例:rdd1.subtractByKey(rdd2)

行动算子(Actions):触发实际计算并返回结果或持久化数据(立即执行)。比如collectcount

python 复制代码
# 触发计算,返回结果
result = rdd.count()  # 统计过滤后的行数
print(result)
Action 算子 功能描述
collect() 将所有数据拉取到 Driver 内存
count() 统计 RDD 元素总数
take(n) 返回前 n 个元素
saveAsTextFile(path) 将结果写入 HDFS/Local 文件系统
foreach(func) 对每个元素应用函数(无返回值)
reduce(func) 全局聚合所有元素(需可交换、可结合的聚合函数)
first() 返回第一个元素
countByKey() 统计每个 Key 的出现次数

Action 触发计算的全流程
是 否 用户编写Driver程序 定义RDD转换链 调用Action操作 DAGScheduler解析DAG 是否存在宽依赖? 划分Stage边界 合并为单个Stage 生成TaskSet并调度 Executor执行Task 结果返回Driver或写入存储

1.3.3.1 reduceByKey 与 groupByKey
算子 功能描述 输出示例(输入:[("a",1), ("a",2), ("b",3)])
groupByKey 将相同 Key 的所有 Value 合并为一个 迭代器(Iterable) [("a", [1, 2]), ("b", [3])]
reduceByKey 对相同 Key 的 Value 按指定函数进行 聚合(如求和、求最大值),直接输出最终结果 [("a", 3), ("b", 3)](假设聚合函数为 lambda x,y: x + y)

groupByKey 的执行流程

Shuffle Write:将数据按 Key 分发到不同分区,所有 Value 直接传输。

Shuffle Read:每个节点拉取对应分区的数据,合并为迭代器。

输出:生成 (Key, Iterable[Value]) 形式的键值对。

reduceByKey 的执行流程

本地预聚合(Map-side Combine):

在 Map 任务所在节点,先对相同 Key 的 Value 进行局部聚合(如求和)。

Shuffle Write:仅传输 预聚合后的结果。

Shuffle Read:对预聚合结果进行全局聚合(如二次求和)。

输出:生成 (Key, AggregatedValue) 形式的键值对。

算子 优势 劣势 适用场景
reduceByKey Shuffle 数据量小,内存占用低 仅支持聚合操作 求和、计数、极值计算
groupByKey 保留所有 Value 的完整信息 Shuffle 开销大,易引发 OOM 需全量 Value 的非聚合操作

groupByKey 的 SQL 对应形式

Spark RDD 代码:

python 复制代码
rdd.groupByKey().mapValues(list)  # 输出 (Key, [Value1, Value2, ...])

Spark SQL 代码:

sql 复制代码
SELECT key, COLLECT_LIST(value) AS values
FROM table
GROUP BY key;

reduceByKey 的 SQL 对应形式

Spark RDD 代码:

python 复制代码
rdd.reduceByKey(lambda a, b: a + b)  # 输出 (Key, Sum_Value)

Spark SQL 代码:

sql 复制代码
SELECT key, SUM(value) AS total
FROM table
GROUP BY key;

1.3.4 DAGScheduler

定义:Spark 内部调度器,将逻辑 DAG 划分为物理执行的 Stage。

核心功能:

Stage 划分:以宽依赖(Shuffle)为边界切分 DAG。

任务调度:生成 TaskSet 提交给 TaskScheduler。

容错处理:重新提交失败的 Stage。

1.3.4.1 DAG(有向无环图)

定义:表示 RDD 转换操作的依赖关系图,无循环依赖。

作用:

优化执行顺序(如合并窄依赖操作)。

支持容错(通过血统重新计算丢失分区)。
textFile RDD map RDD filter RDD reduceByKey RDD Action

DAG 的节点是 RDD ,边是 RDD 之间的 依赖关系(窄依赖或宽依赖)。

DAG 的构建逻辑:

每个转换操作生成新的 RDD,并记录其父 RDD。

Action 触发时,Spark 根据 RDD 的血统(Lineage)构建 DAG。

python 复制代码
rdd1 = sc.textFile("data.txt")          # 初始 RDD
rdd2 = rdd1.map(lambda x: x.upper())    # 窄依赖
rdd3 = rdd2.filter(lambda x: "ERROR" in x)  # 窄依赖
rdd4 = rdd3.groupByKey()               # 宽依赖
rdd4.collect()                         # 触发 DAG 构建

对应的 DAG:
map filter groupByKey rdd1 rdd2 rdd3 rdd4

1.3.4.2 Stage

定义:DAG 的物理执行单元,分为 ShuffleMapStage(Shuffle 数据准备)和 ResultStage(最终计算)。

划分规则:

窄依赖操作合并为同一 Stage。

宽依赖触发 Stage 分割。
Shuffle map, filter reduceByKey

划分 Stage 的核心作用

优化执行效率:

窄依赖操作合并为流水线(Pipeline):同一 Stage 内的连续窄依赖操作(如 map→filter)合并为单个 Task 执行,避免中间数据落盘。

宽依赖强制划分 Stage:确保 Shuffle 前所有数据准备完成,明确任务执行边界。

容错管理:

Stage 是容错的最小单元,失败时只需重算该 Stage 及其后续 Stage。

Stage 划分规则:

以宽依赖为边界,将 DAG 切分为多个 Stage。

窄依赖操作合并到同一 Stage。
Shuffle Stage 1: map + filter Stage 2: groupByKey

map filter 是窄依赖,合并为 Stage 1。
groupByKey 是宽依赖,触发 Stage 2。

Shuffle 发生的环节

在 Spark 中,Shuffle 是连接不同 Stage 的桥梁,通常发生在以下操作中:

宽依赖操作:如 groupByKeyreduceByKey等需要跨分区重新分配数据的操作。

当触发 Shuffle 时,Spark 会将当前 Stage 的 Task 划分为 Shuffle Write(数据写出)和 Shuffle Read(数据读取)两个阶段,分属不同 Stage 的上下游 Task 3。

1.3.5 Driver 程序

Driver 是用户编写的应用主逻辑:

用户必须自行编写 Driver 程序(如 Python/Scala/Java 代码),在其中定义数据处理流程(创建 RDD/DataFrame,调用转换和行动操作)。

python 复制代码
# 用户编写的 Driver 程序(必须存在)
from pyspark import SparkContext
sc = SparkContext("local", "WordCount")
rdd = sc.textFile("hdfs://input.txt")
result = rdd.flatMap(lambda line: line.split(" ")) \
            .countByValue()  # Action 操作
print(result)

1.4 Spark计算流程

Executor Task1 Task2 Task3 Executor 内存计算 Stage 1: 窄依赖任务 Pipeline 执行 Executor 内存计算 Executor 内存计算 Stage 2: 下游任务执行 Driver 程序 创建初始 RDD 定义转换操作链 map/filter/join 触发 Action 操作 collect/save DAGScheduler 将 DAG 划分为 Stage Shuffle 阶段: 宽依赖数据重组 结果返回 Driver 或写入存储

Spark与MapReduce对比

对比维度 Spark MapReduce 优劣分析
执行模型 基于内存的DAG模型,多阶段流水线执行 严格的Map-Shuffle-Reduce两阶段模型 Spark减少中间数据落盘,MR依赖磁盘保证稳定性
容错机制 基于RDD血缘关系(Lineage)重建丢失分区 通过Task重试和HDFS副本机制 Spark重建成本低,MR依赖数据冗余保障可靠性
延迟性能 亚秒级到秒级(内存计算) 分钟级(多轮磁盘IO) Spark适合交互式查询/实时处理,MR仅适合离线批处理
适用场景 迭代计算(ML)、实时流处理、交互式分析 超大规模离线批处理(TB/PB级) Spark覆盖场景更广,MR在单一超大Job场景仍有优势

2. SparkSQL

Spark SQL 是 Apache Spark 的模块之一,核心功能是 处理结构化数据,其设计目标是:统一 SQL 与代码:允许开发者混合使用 SQL 和 DataFrame API(Python/Scala/Java),打破 SQL 与编程语言的界限。

高性能查询:通过 Catalyst 优化器生成逻辑/物理执行计划,结合 Tungsten 引擎的二进制内存管理,提升执行效率。

多数据源支持:可对接 Hive、JSON、Parquet、JDBC 等数据源,甚至自定义数据源。

Spark Sql转换成程序执行的过程
RDD与DAG 物理执行 Catalyst优化器 转换为RDD操作链 基于宽依赖划分Stage 生成DAG DAGScheduler调度Stage Task分发到Executor 生成物理计划 Tungsten引擎代码生成 生成未解析逻辑计划 SQL 解析 元数据绑定 解析后的逻辑计划 逻辑优化 优化后的逻辑计划 输入 SQL 或 DataFrame 操作 集群执行并返回结果

3. Spark Streaming

3.1 核心定位

Spark Streaming 是 Spark 生态中用于实时流数据处理的模块,采用 微批处理(Micro-Batch) 模式,将流数据切分为小批次(如1秒~数分钟),以类似批处理的方式实现近实时(Near-Real-Time)计算。其核心优势是与 Spark 生态的无缝集成,复用批处理代码和资源调度能力。

3.2 离散化流DStream与结构化流Structured Streaming

数据抽象:DStream

离散化流(Discretized Stream, DStream):是 Spark Streaming 的基础抽象,表示一个连续的数据流,内部由一系列 RDD 组成,每个 RDD 对应一个时间窗口内的数据。

处理流程:

输入源:从 Kafka、Flume、Kinesis、TCP Socket 等读取数据。

批处理窗口:按用户定义的批次间隔(如5秒)将数据划分为微批次。

转换操作:对每个批次的 RDD 应用 map、reduce、join 等操作。

输出结果:将处理后的数据写入 HDFS、数据库或实时看板。

执行模型

Driver 节点:负责调度任务,生成 DStream 的 DAG 执行计划。

Worker 节点:执行具体的任务(Task),处理每个微批的 RDD。

容错机制:基于 RDD 的血缘(Lineage)机制,自动恢复丢失的分区数据。

Structured Streaming(结构化流)

核心抽象:将流数据视为无界的 DataFrame/Dataset,支持与静态数据相同的 SQL 操作。

处理模式:

微批处理(默认):类似传统 Spark Streaming。

连续处理(实验性):实现毫秒级延迟(Spark 2.3+)。

关键特性:

端到端 Exactly-Once 语义:通过与 Kafka 等源的协同,保证数据不丢不重。

事件时间与水位线:支持基于事件时间的窗口聚合,处理乱序数据。

状态管理:内置状态存储(StateStore),支持 mapGroupsWithState 等复杂状态操作。

相关推荐
Elastic 中国社区官方博客1 分钟前
Elasticsearch:AI 助理 - 从通才到专才
大数据·数据库·人工智能·神经网络·elasticsearch·搜索引擎·全文检索
江湖十年3 分钟前
go-multierror: 更方便的处理你的错误列表
后端·面试·go
CopyLower1 小时前
Elasticsearch 查询优化:从原理到实践的全面指南
大数据·elasticsearch·搜索引擎
Gvemis⁹1 小时前
Spark-SQL
大数据·sql·spark
落寞的魚丶5 小时前
2022年全国职业院校技能大赛 高职组 “大数据技术与应用” 赛项赛卷(3卷)任务书
大数据·高职组·2022全国职业技能大赛·大数据技术与应用
神奇的黄豆7 小时前
spark-sql学习内容总结
大数据·sql·spark
恒拓高科WorkPlus8 小时前
BeeWorks:打造安全可控的企业内网即时通讯平台
大数据·人工智能·安全
软件测试曦曦10 小时前
16:00开始面试,16:08就出来了,问的问题有点变态。。。
自动化测试·软件测试·功能测试·程序人生·面试·职场和发展
恒拓高科WorkPlus10 小时前
一款安全好用的企业即时通讯平台,支持统一门户
大数据·人工智能·安全
Debug_TheWorld10 小时前
Kafka学习
大数据·中间件