{count, plural, other {课程}}
Apache Spark 是一个面向大规模数据工程、数据科学与机器学习的统一分析引擎。它可以与 Python、SQL、R、Java 或 Scala 搭配使用。Spark 最初于 2009 年在加州大学伯克利分校启动,并于 2013 年捐赠给 Apache 基金会。如今,它已成为最广泛使用的可扩展计算引擎,相关岗位需求成千上万。鉴于其在数据工程领域的重要性,以下是一些面试问题,帮助您在求职或招聘具备 Spark 经验的人才时参考。示例代码答案将使用 Python 提供。
基础 Spark 面试题
这些问题涵盖 Spark 的基础概念,适合仅有入门经验的读者。如果您需要复习,我们的Python 中的 Spark SQL 入门课程是理想的起点。
1. 什么是 Apache Spark?为什么在数据处理中要使用它?
此题考察候选人对 Apache Spark 的整体理解及其在大数据生态中的作用。
答案:
Apache Spark 是一种开源的分布式计算系统,提供了在具有隐式数据并行性和容错性的集群上进行编程的接口。与传统的 MapReduce 相比,它在大规模数据处理中具有速度快、易用性强的优势。
关键特性:
- 内存计算:将数据存储在内存中以加速处理。
- 可扩展性:通过机器集群处理 PB 级数据。
- 易用性:提供 Java、Scala、Python 和 R 的 API。
- 统一分析引擎:支持 SQL、流处理、机器学习与图计算。
2. 解释弹性分布式数据集(RDD)的概念
此题考察您对 Apache Spark 基本概念的理解。RDD 是使 Spark 如此强大的关键组件之一。
弹性分布式数据集(RDD)是 Apache Spark 的基本构件。它是不可变的、分布式的对象集合,可在集群上并行操作。以下是与 RDD 相关的关键特性和概念:
- 不可变:RDD 一经创建内容不可更改。只能通过转换操作生成新的 RDD。这种不可变性简化了容错,并支持 Spark 的惰性求值模型。
- 分布式:RDD 跨集群多节点分布,从而支持并行操作。每个 RDD 被划分为多个分区,分区可在不同节点上独立处理。
- 弹性:“弹性”指容错能力。Spark 通过记录每个 RDD 的血缘(lineage)信息来保证容错。如果某个分区因节点故障丢失,Spark 可根据血缘和对原始数据的转换重算该分区。
- 数据集:RDD 是数据的分布式表示,可承载任意类型的数据,包括结构化与非结构化数据。Spark 在多种语言(如 Scala、Java、Python、R)提供 RDD API,适用于多样的场景与数据类型。
- 惰性求值:RDD 的转换操作并不会立即执行。Spark 会构建一张描述计算的有向无环图(DAG),直到触发某个行动(如 collect 或 count)时才真正执行。这有助于优化执行计划并提升性能。
3. 什么是 YARN?
YARN 是 Hadoop 的分布式容器管理器,负责资源管理。Spark 在 Hadoop 集群上运行时可以利用 YARN,实现更有效的资源管理。YARN 的关键能力包括高效的集群资源分配、作业调度以及在节点故障时的容错。这是促使 Spark 成为强大工具的诸多组件之一。
4. Spark RDD 中的 map 与 flatMap 转换有何区别?
此题用于判断您是否理解 Spark RDD(弹性分布式数据集)中的不同转换类型。
答案:
.map():将 RDD 中的每个元素转换为恰好一个新元素。结果 RDD 的元素数量与输入一致。.flatMap():将每个元素转换为零个或多个新元素。结果 RDD 的元素数量可能与输入不同。
# Example of map
rdd = spark.sparkContext.parallelize([1, 2, 3])
mapped_rdd = rdd.map(lambda x: x * 2)
print(mapped_rdd.collect()) # Output: [2, 4, 6]
# Example of flatMap
flat_mapped_rdd = rdd.flatMap(lambda x: [x, x * 2])
print(flat_mapped_rdd.collect()) # Output: [1, 2, 2, 4, 3, 6]
上述代码通过对整数 RDD 的转换演示了 map 与 flatMap 的区别。
5. 如何使用 Spark SQL 从 DataFrame 中查询数据?
此题检查候选人使用 Spark SQL 进行查询的能力,这对数据分析任务至关重要。
答案:
# Register the DataFrame as a SQL temporary view
df.createOrReplaceTempView("table")
# Execute SQL query
result = spark.sql("SELECT column1, SUM(column2) FROM table GROUP BY column1")
# Show the results
result.show()
该示例展示了如何将 DataFrame 注册为临时视图,并使用 Spark SQL 执行分组聚合查询。
中级 Spark 面试题
对于已经掌握基础并在工作中应用的人,这些问题更为常见:
6. 解释 Spark 的惰性求值概念及其重要性
此题考察对 Spark 核心原则的理解,这对性能优化至关重要。
答案:
惰性求值意味着 Spark 在调用转换操作时不会立即执行,而是构建逻辑执行计划。只有当调用行动(如 collect 或 count)时,才会触发实际计算。
惰性求值的重要性体现在两方面:
- 使 Spark 能在执行前优化整个数据处理流程,合并操作以最小化数据洗牌(shuffle)。
- 减少对数据的遍历次数,从而提升性能。
7. 如何在 Spark 中持久化数据?可用的存储级别有哪些?
此题检查候选人对 Spark 数据持久化的了解,这对性能调优和迭代算法很重要。
答案:
可以使用 .persist() 或 .cache() 方法进行持久化。.cache() 是 .persist() 使用默认存储级别的简写。
存储级别:
- MEMORY_ONLY:以反序列化的 Java 对象形式存储在 JVM 中。若内存不足,部分分区不会被缓存。
- MEMORY_AND_DISK:以内存中的反序列化对象存储,内存放不下时写入磁盘。
- MEMORY_ONLY_SER:以序列化的 Java 对象形式存储在 JVM 中,降低内存占用,但增加序列化/反序列化的 CPU 开销。
- MEMORY_AND_DISK_SER:与 MEMORY_AND_DISK 类似,但存储序列化对象。
- DISK_ONLY:仅将 RDD 分区存储在磁盘上。
rdd = spark.sparkContext.parallelize([1, 2, 3, 4, 5])
rdd.persist(storageLevel=StorageLevel.MEMORY_AND_DISK)
8. 如何在 Spark 中处理数据倾斜?
此题评估对数据倾斜及其治理方法的理解,这对高效处理数据至关重要。
答案:
当某些分区的数据远多于其他分区时,会产生数据倾斜,导致性能瓶颈。常见策略包括:
- 加盐(Salting):为键添加随机前缀,使数据更均匀地分布到各分区。
- 重分区:增加分区数量,使数据分布更均匀。
- 广播变量:将小数据集广播到所有节点,避免对大数据集进行洗牌。
from pyspark.sql.functions import monotonically_increasing_id, col
# Example of salting
df = df.withColumn("salt", monotonically_increasing_id() % 10)
df = df.withColumn("new_key", col("original_key") + col("salt"))
9. 解释 Spark 中窄依赖与宽依赖(窄/宽转换)的区别
此题考察对 Spark 执行模型的理解,以及不同转换类型对性能的影响。
答案:
- 窄转换(Narrow Transformations):每个输入分区仅对应一个输出分区。例如
.map()、.filter()、.union()。由于无需洗牌,通常更快。 - 宽转换(Wide Transformations):每个输入分区会贡献给多个输出分区。例如
.groupByKey()、.reduceByKey()、.join()。需要进行跨网络的数据洗牌,耗时较长。
# Narrow transformation example
rdd1 = rdd.map(lambda x: x * 2)
# Wide transformation example
rdd2 = rdd.groupByKey()
10. Spark Streaming 在实时数据处理中的应用
Spark 擅长从Apache Kafka 或Amazon Kinesis 等来源进行实时数据流处理,因其具备良好的可扩展性与容错性。这通过 Spark Streaming 扩展实现。它通过输入 DStreams 与外部数据源交互,DStreams 表示来自这些源的连续数据流。
Spark Streaming 通过检查点(checkpointing)与预写日志(write-ahead logs)等技术保障容错与数据一致性。检查点会定期将流式应用的状态保存到可靠存储(如 HDFS)用于故障恢复;预写日志为从外部源接收的数据提供容错能力。
高级 Spark 面试题
这些问题适用于具备更多实战经验的用户,尤其是更高级的话题。如果需要复习,请参阅我们的Spark 机器学习教程。
11. 讨论如何将 Spark 用于机器学习
此题考察应试者对 Spark 环境及 MLlib 库的理解。
Spark 的 MLlib 库为大规模机器学习提供了丰富的工具与算法。在面向大数据集的特征工程与预处理方面,MLlib 提供了多项高级技术与优化:
- 特征变换与选择:MLlib 提供缩放、归一化、二值化、向量化(如独热编码)等特征变换方法,并提供基于相关性、信息增益、统计检验的筛选方法,以及主成分分析(PCA)等高级降维技术。
- 处理类别特征:MLlib 包含对类别特征的高效处理工具,如使用 StringIndexer 将类别变量转换为数值表示,使用 OneHotEncoder 转换为稀疏二进制向量。这些转换针对分布式集群的并行执行进行了优化。
- Pipeline API:Spark 的 Pipeline API 允许将多个特征工程与建模阶段串联为一个工作流,便于构建复杂的变换流水线,并确保在不同数据集与任务中的一致性与可复现性。
- 自定义转换器与估计器:MLlib 允许基于 DataFrame API 定义自定义的特征转换器与估计器,可将特定领域的方法或第三方库集成进 Spark ML 流水线,扩展其功能与灵活性。
12. 解释 Spark 如何与 Apache Hadoop HDFS 和 Apache Cassandra 等外部存储系统集成。在基于 Spark 的数据流水线中利用这些集成有什么优势?
此题检验对 Spark 系统底层功能及其与 HDFS、Apache Cassandra 协作方式的理解。既要了解通过代码获取数据的方法,也要理解数据在系统中的流转。
- Hadoop HDFS 集成:Spark 通过为各系统设计的连接器或库与外部存储集成。例如,Spark 原生支持 HDFS,可通过 Hadoop 的 InputFormat 与 OutputFormat API 直接读写 HDFS 数据。
- Apache Cassandra 集成:利用这些集成的优势包括(对 HDFS 而言)数据本地性带来的性能提升、更简便的数据访问与操作方式,以及与现有数据基础设施的兼容性。此外,Spark 能利用这些存储系统的分布式特性进行并行处理,从而实现可扩展的数据处理。
13. 解释 Spark 中的广播变量概念
广播变量是只读变量,会被缓存并提供给分布式 Spark 应用的所有工作节点。它用于高效地将大型只读数据集或值分发到各个工作节点,从而减少网络开销、提升任务性能。
广播变量经过序列化后仅在每个工作节点发送一次,并在内存中缓存,可在多个任务中复用。这样无需在每个任务中重复发送,尤其对较大数据集,能显著降低数据传输开销。
- 使用场景:在需要在多个任务或阶段间共享大型数据或值时常用。例如在一大一小的数据集连接时,将较小的 DataFrame/RDD 广播可显著减少网络洗牌量并加速执行。
- 典型收益场景:
- 连接操作:广播较小数据集以减少网络流量并加速任务执行。
- 查找表:广播用于富化或过滤的小型查找表/字典,避免重复数据传输。
- 机器学习:在分布式训练中广播特征向量或模型参数,若其相对数据集较小,可加速训练。
- 挑战:
- 内存开销:广播过大的变量会占用大量工作节点内存,若管理不当可能导致 OOM。
- 网络拥塞:在初始广播阶段,过大的变量可能引发网络拥塞,尤其在带宽有限的大集群中。
- 动态数据:广播变量一旦广播即不可变,不适合在作业执行期间需要动态更新的场景。
14. 如何通过分区与合并分区来优化 Spark 作业?
此题考察候选人优化 Spark 作业的能力,这是提升性能与效率的关键。通过性能调优,我们可以利用 Spark 的分布式框架,使用分区与合并分区更好地管理集群的工作负载分配,从而更快地完成数据操作。
答案:
- 分区(Partitioning):控制 RDD 或 DataFrame 的分区数。使用
.repartition()增加或均衡分区。该操作代价较高,应在确有需要实现均衡负载时使用。 - 合并分区(Coalescing):在不进行完全洗牌的情况下减少分区数,较重分区更高效。使用
.coalesce()完成。
# Increasing partitions (full shuffle)
df_repartitioned = df.repartition(10)
# Reducing partitions (no full shuffle)
df_coalesced = df.coalesce(2)
注意,后续追问可能涉及何时最适合使用这些操作。请说明:它们在处理大数据集时更有效,不应在小数据集上浪费计算资源。
15. 解释 Spark 与数据序列化格式的互操作性
数据从业者会接触多种数据格式,各有取舍。请说明 Spark 如何与这些格式交互,并从高层面给出性能与更大生态方面的考量。
- 序列化格式支持:Spark 通过内置支持或第三方库与 Avro、Parquet、ORC 等格式互操作。这些格式具备高效压缩、列式存储与模式演进等优势,适合在 Spark 流水线中进行数据处理与存储。
- 读写优化:Spark 使用针对性读取器/写入器,利用这些格式的内部结构与压缩技术优化读写。例如 Parquet 与 ORC 的列式存储可减少 I/O 开销、提升查询性能。
- 格式取舍:取舍包括存储效率(如压缩比)、性能(如读写吞吐)以及与其他工具的兼容性。选择合适的格式取决于数据特征、查询模式与流水线的集成需求。
Spark 编码面试题
以下编码题聚焦在使用 PySpark 与 Spark 环境交互。
16. 在大型文本文件中找出出现频率最高的前 N 个单词
此题检查您与 Spark 的交互能力以及对 Spark 中映射操作的理解。
from pyspark import SparkContext
# create your spark context
sc = SparkContext("local", "WordCount")
# import a text file from a local path
lines = sc.textFile("path/to/your/text/file.txt")
# split and map the words
# then reduce by using the words as keys and add to the count
word_counts = lines.flatMap(lambda line: line.split(" ")) \
.map(lambda word: (word, 1)) \
.reduceByKey(lambda a, b: a + b)
# order the words and take only the top N frequent words
top_n_words = word_counts.takeOrdered(N, key=lambda x: -x[1])
print(top_n_words)
17. 计算给定 RDD 中数值的平均值
此题可很好地展示是否掌握创建与操作简单 RDD。计算平均值是数据从业者常见任务,关键在于理解如何在 Spark 环境中构建与处理数据。
from pyspark import SparkContext
# Create sparkContext and name it “Average”
sc = SparkContext("local", "Average")
# Generate Spark RDD
data = sc.parallelize([1, 2, 3, 4, 5])
# Sum the RDD, count the number of values in RDD
total_sum = data.sum()
count = data.count()
# divide sum by count to get average
average = total_sum / count
print("Average:", average)
18. 对两个 RDD 执行左外连接
执行连接等数据处理与转换任务是 SparkSQL 的关键能力。这使得可跨不同来源整合数据以进行分析。
from pyspark import SparkContext
# Create SparkContext
sc = SparkContext("local", "LeftOuterJoin")
# Create two RDDs with tuples sharing keys
rdd1 = sc.parallelize([(1, 'a'), (2, 'b'), (3, 'c')])
rdd2 = sc.parallelize([(1, 'x'), (2, 'y')])
# Use the .leftOuterJoin() method to join the first rdd to the second rdd
joined_rdd = rdd1.leftOuterJoin(rdd2)
# Use the .collect() method to show the rdd
print(joined_rdd.collect())
19. 从 Kafka 读取数据,执行转换后写入 HDFS
此题考察您从外部数据源引入数据的能力,以及 Spark 连接外部数据源的方式。重点把握通用概念,例如为特定数据流导入扩展/工具,而非死记具体代码。注意,在 SparkContext 中我们将应用名(KafkaWordCount)作为可选参数指定,有助于理清进程。
# Import the sparkcontext, additionally import streaming context and Kafka
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
from pyspark.streaming.kafka import KafkaUtils
# Create context
sc = SparkContext("local", "KafkaWordCount")
# Use streaming context to bring in data at 10 second intervals
ssc = StreamingContext(sc, 10) # 10-second batch interval
# Use Kafka param dictionary in order to connect to the stream using the streaming context, the topic of interest, and the parameters
kafka_params = {"metadata.broker.list": "broker1:9092,broker2:9092"}
kafka_stream = KafkaUtils.createDirectStream(ssc, ["topic"], kafka_params)
# save the results of this stream to lines
# perform MapReduce in order to generate dictionary and count by keys
lines = kafka_stream.map(lambda x: x[1])
word_counts = lines.flatMap(lambda line: line.split(" ")) \
.map(lambda word: (word, 1)) \
.reduceByKey(lambda a, b: a + b)
# save to external file
word_counts.saveAsTextFiles("hdfs://path/to/save")
# start context until you terminate
ssc.start()
ssc.awaitTermination()
20. 如何在 Spark DataFrame 上执行基本的转换与行动?
此题评估对 Spark DataFrame 操作的理解。
转换是返回新 DataFrame 的操作,如 select、filter、groupBy。行动是触发计算并返回结果的操作,如 show、count、collect。
下面的示例展示了选取列、过滤行以及分组聚合。
# Select specific columns
selected_df = df.select("column1", "column2")
# Filter rows based on a condition
filtered_df = df.filter(df["column1"] > 100)
# Group by a column and perform aggregation
grouped_df = df.groupBy("column2").agg({"column1": "sum"})
# Show the results
selected_df.show()
filtered_df.show()
grouped_df.show()
结语
掌握这些面试题是成为数据专业人士的重要一步。Spark 是众多组织处理大数据流水线的常用基础设施。理解 Spark 的优势与挑战,将帮助您脱颖而出。这只是开始!获取 Spark 的动手实践,是学习的最佳方式。
您可以从 DataCamp 上以下 PySpark 课程与教程开始:
Spark 面试常见问答
如果我刚接触大数据技术,如何开始学习 Spark?
可先学习 Datacamp 的课程,例如PySpark 入门、Python 中的 Spark SQL 入门,以及使用 PySpark 的大数据来入门。
Spark 在真实应用中有哪些常见用例?
Spark 常用于 ETL 流水线、数据探索、实时分析、机器学习与数据仓库等场景。掌握 Spark 能胜任多个行业的岗位。
Spark 与 Hadoop MapReduce 等其他大数据处理框架相比如何?
Spark 尽可能将结果保存在内存中,而 MapReduce 会将中间结果写入磁盘。不过,Spark 可利用 Hadoop 的基础设施(如 YARN)进行资源管理,因此两者常常协同工作。
Spark 是否适用于小规模数据处理,还是仅适用于大数据?
可以。Spark 可根据数据处理需求进行扩展。某些为性能优化设计的 Spark 功能在小数据集上可能会浪费算力,因此需相应调整流水线。
除了 Python 之外,我还能用其他语言使用 Spark 吗?
可以。Spark 可使用 Scala、Java、R 和 SQL。
