Spark核心知识点和面试高频考点
一、Spark 是什么Apache Spark 是一个开源的统一分布式计算引擎,它以内存计算为核心,提供了 Java、Scala、Python、R 等高级 API,并提供以下功能:批处理(Batch Processing)交互式查询(Spark SQL)流处理(Structured Streaming)机器学习(MLlib)图计算(GraphX)1.Spark 生态全景图Spark Core(核心基础):负责分布式任务调度、内存管理、容错、存储交互等基础功能。它引入了RDD(弹性分布式数据集)这一核心抽象,是所有其他组件的基石。Spark SQL(结构化数据处理):用于处理结构化与半结构化数据,提供了DataFrame、Dataset,以及SQL查询引擎。底层也是基于RDD进行了封装优化。Spark Streaming / Structured Streaming(流处理):基于 Spark SQL 的实时流数据处理引擎。MLlib(机器学习库):分布式机器学习库,包含丰富的可扩展机器学习算法(分类、回归、聚类、推荐等),以及特征工程、模型评估等工作流工具,支持在分布式集群上训练大规模模型。GraphX(图计算):用于图数据与图并行计算的 API 和算法库(如 PageRank、连通分量等),将图与集合操作统一,适合社交网络分析等场景。目前 Spark 生态中也推荐基于 DataFrame 的GraphFrames作为高级替代。2.Spark 与 MapReduce 对比对比维度Apache SparkMapReduce计算模型基于DAG(有向无环图)执行引擎,可将一个作业划分为多个阶段并行处理,并优化整体流程。严格执行Map → Shuffle → Reduce的线性过程,一个复杂任务需要拆分为多个 MapReduce 作业串联,中间结果全程落盘。速度中间结果可缓存于内存,迭代计算(如机器学习)快 10–100 倍以上。每次 Map/Reduce 结果都写入磁盘,导致大量 I/O 开销,速度较慢。易用性提供DataFrame/Dataset及丰富的算子(过滤、联接、聚合等),代码量远少于 MapReduce,并支持交互式查询。编程相对底层,需要严格实现 Mapper 和 Reducer 类,逻辑复杂时代码冗长。处理能力原生支持流处理(Structured Streaming)、SQL、机器学习(MLlib)、图计算(GraphX),实现“一个堆栈解决多种需求”。专为离线批处理设计,不支持实时处理,流处理需结合 Storm、Flink 等外部系统。容错机制RDD 维护血统信息,分区丢失可基于依赖关系重算,同时配合检查点保证可靠性。靠 Task 级别的重试机制,底层依赖于 HDFS 的副本实现容错。资源管理Executor 进程常驻,可跨作业复用缓存数据,降低启动开销。每个 Task 启动独立 JVM 进程,任务结束后即释放,无法共享内存数据。二、核心数据抽象RDD、DataFrame、DatasetSpark 有三种数据表示方式:1.RDD(Resilient Distributed Dataset弹性分布式数据集)Spark 最基础的分布式数据抽象,表示一个不可变、可分区的元素集合,支持并行操作。它的核心在于故障恢复:不通过数据复制,而是通过血统(Lineage)自动重算丢失的分区。弹性:可以基于内存存储也可以在磁盘中存储。1.1 RDD 五大核心属性每个 RDD 都具备以下 5 个内部属性:分区列表(Partitions):RDD 的数据在物理上并非一个整体,而是被切分成多个分区(Partition),分布在集群不同节点上。分区列表就是这些分区的集合。并行度:每一个分区对应一个 Task(任务),所以有多少个分区,Spark 就可以同时起多少个 Task 并行计算。分区数通常是并行度的上限。如果从 HDFS 读取,默认一个 HDFS Block(128MB)就是一个分区。计算函数(Compute):这是一个函数,Spark 会把它分发到每个分区所在的 Executor 上执行。你在代码里写的rdd.map(...)、rdd.filter(...)等算子,最终会层层组合成一个复合函数,由compute在分区上调用。这个函数只在行动算子触发时才真正执行(惰性计算)。依赖关系(Dependencies):记录父 RDD 与本 RDD 的关系(窄依赖/宽依赖),用于推断阶段划分和容错重算。分区器(Partitioner,可选):只有键值对 RDD才有分区器,它决定了每条记录应该放入哪个分区。常见的有:HashPartitioner(默认):根据 Key 的hashCode % 分区数决定分区号。数据分发的时候可以将相同的数据放入到一起。groupBykey、reduceBykey。RangePartitioner:按 Key 的范围排序后划分,通常用于排序相关操作(sortBykey)。首选位置(Preferred Locations,可选):返回每个分区数据所在的优选节点列表。Spark 调度器会尽可能把任务分配给这些节点。本地性优化:比如从 HDFS 读取时,首选位置就是该 Block 所在的数据节点,这样 Task 就可以直接读本地磁盘,省去网络传输。1.2计算函数的两种算子:Transformation、Action转换(Transformation):map、filter、flatMap、groupByKey、reduceByKey等,返回新 RDD,不触发实际计算。行动(Action):collect、count、saveAsTextFile等,真正触发作业运行,并将结果返回到 Driver 或存储。懒加载(Lazy Evaluation):Transformation 只记录依赖关系,不触发计算;Action 才会提交 Job。1.3 RDD的依赖关系:窄依赖/宽依赖窄依赖:父 RDD 的每个分区最多被子 RDD 的一个分区使用(如map、filter)。像一根管子的直连,重算一个丢失分区只需重算父分区中对应的那一个。宽依赖(Shuffle 依赖):父 RDD 的同一个分区被子 RDD 的多个分区使用(如groupByKey、join)。数据要重新洗牌分发shuffle,重算丢失分区需要重新从所有父分区读取和处理,代价大。这也是 Stage 划分的依据,遇到宽依赖就切一个新 Stage。实际意义:容错机制:当某个分区丢失,Spark 根据其Lineage(血统)从最近的持久化(若已缓存)或最窄的依赖链上重算该分区,而不是全量重跑整个作业。调度:Spark 根据依赖关系把作业切为多个 Stage,宽依赖是 Stage 分界线。2.DataFrame分布式的行列式数据集一种分布式的行列式数据集,可视为带有 Schema 的 RDD。数据按命名列组织,类似于关系数据库中的表,但没有编译时类型检查(弱类型)。DataFrame 执行时会经过 Spark SQL 的优化层:Catalyst 优化器:对逻辑计划进行规则优化(谓词下推、列裁剪、常量折叠等)并选择最佳物理计划。Tungsten 执行引擎:将数据以堆外二进制格式(UnsafeRow)直接操作,配合全阶段代码生成,将一连串算子编译为紧凑的 Java 函数,极大提升执行效率。3.Dataset强类型结构化数据抽象强类型的数据抽象。是DataFrame 与 RDD 的结合体,既保留 schema 信息,又支持类型安全(编译时类型检查)。在 Java 和 Scala 中可用。内部通过编码器(Encoder)在 JVM 对象和 Spark SQL 的 Tungsten 二进制格式之间高效转换。4.它们之间的关系维度RDDDataFrameDataset (强类型)内部数据结构Java/Kryo 序列化对象Tungsten 二进制格式的 Row 集合Encoder 转化的 Tungsten 二进制格式Schema(元数据)无,需人工维护有(列名和类型)有类型安全编译时类型安全(泛型)运行时检查(弱类型)编译时类型安全(强类型)优化引擎无(函数黑盒)Catalyst 优化器 + TungstenCatalyst 优化器 + Tungsten性能低(对象开销大,GC)高(二进制处理,代码生成)高(二进制处理,代码生成)编程范式函数式(map/filter等)声明式(select/where)+ SQL声明式 + 函数式(lambda)语言支持Scala, Java, Python, RScala, Java, Python, R仅 Scala 和 Java适用场景非结构化数据、底层操作、需要完全自定义逻辑结构化分析、数据仓库、BI 查询需要类型安全且复杂的 ETL、流处理API 示例rdd.filter(_.age18).map(_.name)df.select("name").where($"age"18)ds.filter(_.age18).map(_.name)三、Spark 整体架构Spark 采用主从架构 + 统一执行引擎,让各种上层组件(SQL、Streaming、MLlib)都能共享同一套分布式调度、内存管理和容错机制。