PySpark 面试问题

作者 Aaron Cao · 更新于

PySpark 面试问题
PySpark 面试的重点集中在执行模型和性能上。你需要能解释转换(transformation)与动作(action)的区别、判断哪些操作会触发 shuffle、知道何时选择广播 join、诊断数据倾斜、说明缓存的取舍,并描述如何调优一个内存耗尽的作业。

PySpark 面试的重点集中在执行模型和性能上。你需要能解释转换(transformation)与动作(action)的区别、判断哪些操作会触发 shuffle、知道何时选择广播 join、诊断数据倾斜、说明缓存的取舍,并描述如何调优一个内存耗尽的作业。

面试官会怎么问执行模型?

即使你能写出跑得通的 PySpark 代码,这类问题也可能让你卡壳,因为它们问的是引擎在做什么,而不是你的代码写了什么。面试官喜欢用这类问题开场,正是因为它能区分出真正调优过作业的人和只是跑过一次的人。本节梳理执行模型相关的问题,以及一个完整答案应该包含哪些要点。

  • 转换(transformation)和动作(action)有什么区别? 转换操作会构建执行计划并惰性返回一个新的 DataFrame;而 countcollect 这类动作,或者一次写入操作,才会触发真正的执行。在动作要求结果之前,什么都不会被计算。
  • 惰性求值有什么用? 优化器能在真正执行之前看到完整的操作链,因此可以重新排列过滤条件、裁剪不需要的列,并合并多个步骤。
  • 窄依赖转换还是宽依赖转换?filterselect 这样的窄依赖操作,每个输出分区只依赖一个输入分区。而 groupByjoindistinct 这样的宽依赖操作会在分区之间重新分发数据,也就是 shuffle。
  • 什么是 shuffle,为什么它很重要? 数据会跨网络传输并落盘,形成一个 stage 边界。这通常是一个作业里开销最大的环节。
  • 解释一下 job、stage 和 task。 一个动作会启动一个 job,shuffle 边界会把它拆分成多个 stage,每个 stage 会为每个分区运行一个 task。
  • RDD、DataFrame 还是 Dataset? 优先使用 DataFrame,因为它能享受到 Catalyst 优化器和列式执行的优化。RDD 仍然保留用于底层控制。带类型的 Dataset 是 JVM 层面的概念,所以在 Python 里,诚实的答案是它并不适用。

该用 shuffle 和 stage 这些词的时候就要用出来。面试官会把它们当作一个捷径,用来判断你有没有真正看过 Spark UI。

性能相关的问题应该怎么回答?

大多数高级 PySpark 面试本质上都是性能面试。这些问题往往以场景的形式出现,而不是让你背定义。

  • 一个 join 很慢,你会先检查什么? 先看两边数据的大小。如果有一侧能放进 executor 内存,就把它广播出去,完全跳过 shuffle。否则要先看分区和数据倾斜情况,再考虑集群规模。
  • 什么是数据倾斜,怎么解决? 少数几个 key 承载了大部分数据行,导致一个 task 在其他任务都结束后还在运行。解决办法包括给热点 key 加盐、把较小的一侧广播出去,或者过滤掉那些会被哈希到同一分区的空值。诊断信号是 Spark UI 里 task 运行时长的分布差异。
  • 什么时候该用 cache 或 persist? 当一个 DataFrame 会被多个动作复用、且重新计算成本较高时。对只用一次的数据做缓存是在浪费内存,而在长时间运行的作业里,及时 unpersist 也很重要。
  • 用 repartition 还是 coalesce? repartition 会触发 shuffle,可以把分区数均匀地增加或减少;coalesce 则不需要完整的 shuffle 就能合并分区,是减少输出文件数量更廉价的方式。
  • 为什么要尽量避免 Python UDF? 数据行需要在 JVM 和 Python 进程之间序列化,而且优化器看不到函数内部的逻辑。应优先使用内置函数,只有在没有对应内置函数时才考虑向量化 UDF。
  • 为什么 collect 很危险? 它会把完整结果拉到 driver 端,可能耗尽 driver 的内存。
  • 一个作业因为内存不足失败了,你的排查顺序是什么? 先确认是 driver 还是 executor 内存不足,然后看数据倾斜,再看分区大小设置,最后才是内存配置本身。一上来就直接调大内存,是经验不足的信号。

有一位面试平台团队的数据工程师被问到,一个已经稳定跑了一年的夜间作业为什么突然要花四个小时才能跑完。真正说到点子上的答案不是调整配置,而是某个上游合作方开始在 join key 里传空值,导致所有空值行都被哈希到了同一个分区。面试官看重的正是这种排查顺序:先看数据,再看集群。

按岗位划分的相关题库见 按岗位分类的面试问题

还会遇到哪些实战和数据处理相关的问题?

剩下的这些问题是在检验你是不是真正上线过一条数据管道,而不只是跟着教程走了一遍。

  • 如何高效地读取数据? 使用 Parquet 这样的列式格式、对过滤列做分区裁剪、以及谓词下推。要能说清楚为什么少读字节比事后优化更划算。
  • 为什么要显式定义 schema,而不是让系统推断? 推断 schema 需要额外扫描一遍数据,而且不同次运行猜出来的类型还可能不一致。
  • 如何处理空值和重复数据? 说明相关的函数用法,并指出大量空值的 join key 会造成数据倾斜这一点。
  • 窗口函数都用在哪些场景? 排名、累计求和,以及按 key 去重保留最新一条记录,这是数据管道里非常常见的任务。
  • 如何在写出结果时避免产生成千上万个小文件? 写出之前先做 coalesce 或 repartition,并按一个基数合理的列对输出做分区。
  • 如何测试 PySpark 代码? 用带有固定测试数据的小型本地 session,并把业务逻辑拆分成接收和返回 DataFrame 的函数。
  • 如何提交和配置一个作业? 涉及 executor 数量、核数和内存的配置,以及为什么太多小 executor 和太少的大 executor 都会浪费资源这一点。

面试前应该怎么练习?

PySpark 的回答说出口时失败起来很有辨识度:候选人知道 shuffle 开销大,却说不清到底是哪些操作会触发它,于是答案就变成了一堆形容词的堆砌。刷题库能带来一种“看着眼熟”的感觉,但那和在别人等待作答时清楚地给出解释,完全是两回事。

找一条你亲手搭建过的数据管道,从头到尾完整讲一遍:读取数据、每一步转换、stage 边界落在哪里,以及如果变慢了你会先检查什么。大声讲出来,直到不再频繁重讲为止。用会追问的 AI 面试官来练习这些问题,比反复翻看笔记更接近真实的面试现场,这正是 模拟面试 模式存在的意义。

SubcueAI 的创始人 Aaron Cao 把练习的重点放在了这个“说出来”的差距上,而不是一味堆更多题目。在真实面试中,桌面应用和浏览器扩展的侧边栏能在面试官说话的同时把要点结构呈现出来,这对你已经练习过的内容帮助最大。设置过程只需要几分钟,具体步骤见 教程 页面。

常见问题

PySpark 面试会包含现场写代码吗?

很常见。常见任务是做一个 join 加聚合,或者用窗口函数按 key 去重、保留最新一行。面试官会关注你是不是优先想到内置函数而不是 UDF,以及你会不会主动提到分区。

做 PySpark 相关岗位需要掌握多少 SQL?

需要掌握很多。Spark SQL 和 DataFrame API 能表达同样的操作,很多团队直接用 SQL 写 join 和窗口函数。至少会遇到一道题,你可以用这两种方式中的任意一种作答。

Spark 面试需要学 Scala 吗?

如果是 PySpark 岗位,不需要。但了解 Spark 运行在 JVM 上、Python UDF 需要跨越这个边界付出序列化开销会有帮助,这也正是为什么内置函数更受青睐。

PySpark 面试中最常见的失误是什么?

一遇到性能问题就想着扩大集群规模。面试官希望你先检查数据本身:分区大小、数据倾斜、join 策略,以及读取的数据量。第一反应就是加 executor,说明生产环境经验有限。

AI 助手能在真实的数据工程面试中帮到我吗?

它可以在面试官说话的同时把结构呈现出来,这对你已经熟悉的内容最有帮助。但它不能替代练习,而且屏幕共享、录制的面试、有监考的测评,以及公司管理的电脑,都不在其适用范围内。

相关问题

← 更多关于 按岗位与主题分类的面试问题