考察点
这是考察实战经验的开放题,面试官想看你的排查方法论是否成体系,而不是背参数。追问方向:explain 输出怎么看、怎么确认是数据倾斜、调参之外还有什么手段。
参考答案
第一步:先问清楚「慢」在哪
慢有三种形态,排查路径完全不同:
- 排队慢:提交后半天不跑,是资源问题——队列满了、集群负载高、申请的 executor 拿不到。看调度器(YARN ResourceManager)而不是 SQL。
- 某个阶段慢:跑起来了但某个 stage 卡住,多半是倾斜或数据量问题。
- 整体都慢:每个阶段都中规中矩但就是久,多半是扫描量太大或计划不对。
先定位形态再动手,能省一半时间。
第二步:看执行计划
Hive 用 explain(加 extended 更详细),Spark SQL 用 explain formatted 或看 Web UI 的 SQL 标签页。重点看四件事:
- 分区裁剪有没有生效。计划里 TableScan 的分区列表是不是只有预期的分区。裁剪没生效常见原因:分区字段上套了函数(
where substr(ds,1,6)='202607'裁不掉,要改写成范围条件)、动态分区裁剪(DPP)没开。 - join 策略对不对。该 broadcast 的走了 sort merge join?查小表是不是超了
spark.sql.autoBroadcastJoinThreshold,或者统计信息缺失导致优化器误判。 - 有没有意外的大 shuffle。比如 distinct 和 group by 叠加产生多次 shuffle,或者 join key 类型不一致(string join bigint)导致隐式转换后无法优化。
- 谓词有没有下推。Filter 应该贴在 TableScan 上,浮在上层说明下推失败,白读了很多数据。
第三步:定位数据倾斜
执行起来之后看任务的 UI:同一个 stage 里 task 耗时分布,中位数 30 秒、最大值 3 小时,就是倾斜。确认方法:
- 看倾斜 task 的输入记录数,比中位数大几个数量级就坐实了。
- 对疑似 key 跑
select key, count(*) from t group by key order by 2 desc limit 20,看 top key 占比。
处理手段按场景选:null key 过滤或打散、热点 key 加盐、开 Hive 的 hive.optimize.skewjoin 或 Spark AQE 的 skew join 自动处理(spark.sql.adaptive.skewJoin.enabled,3.x 默认开)。AQE 的倾斜判定参数是分区大小中位数的倍数(默认 5 倍且大于 256MB),特别小的倾斜它不管,得手动。
第四步:数据与配置层排查
计划没问题、没有倾斜,但还是慢,就往底层看:
- 统计信息过期:CBO 靠统计信息选计划,
analyze table长期没跑,行数差几个数量级,join 顺序和 broadcast 决策全错。先跑一遍 analyze 再看计划。 - 文件问题:小文件太多导致 task 数爆炸、list 目录慢;或者文件格式是 text 而不是 ORC/Parquet,列裁剪和谓词下推(PPM)都用不上,扫描量是全量。
- 资源配置:单个 executor 内存不够频繁 spill 到磁盘(UI 里看 spill 指标)、并行度太低(大表只有几十个 task)。调
spark.executor.memory、spark.sql.shuffle.partitions(默认 200,大表要调大,小结果集要调小,不然 200 个 task 写 200 个小文件)。 - 数据本身涨了:昨天 1 亿今天 10 亿,上游全量同步改成了没加分区过滤,SQL 没动但数据变了。对比历史输入行数是最快的验证方式。
方法论沉淀
我的一般顺序:UI 看耗时分布 → explain 看计划 → count 验证数据量和倾斜 → analyze 补统计信息 → 最后才动参数。参数是最后手段,因为大部分慢 SQL 的病根是写法或数据,不是配置。还有一个习惯:优化前后各跑一次记录耗时和扫描量,用数字说话,别凭感觉宣布「快多了」。
可能的追问
- explain 和实际执行不一致怎么办? Hive 的 explain 是编译期计划,Spark 的 adaptive 计划运行时会变,要看
explain里的 adaptive 标记或直接看 UI 上的最终物理计划。运行时 broadcast 降级、join 策略切换都是 AQE 干的。 - 怎么看 shuffle 数据量是否正常? UI 里看每个 stage 的 shuffle read/write 字节数,和输入数据量对比。join 后数据量暴增(一对多笛卡尔放大)说明 join 条件有问题——多对多 join 是慢 SQL 的经典病根。
- 生产上能随便 analyze table 吗? analyze 本身是扫描全表的统计任务,大表跑起来不便宜。一般挂在表元数据变更后的例行任务里,或只对新分区做
analyze table t partition(ds=...) compute statistics,增量维护。