Spark AQE(自适应查询执行)核心使用场景与示例

一. AQE是什么?

1、 产生背景

Spark 2.x 在遇到有数据倾斜的任务时,需要人为地去优化任务,比较费时费力;如果任务在Reduce阶段,Reduce Task 数据分布参差不齐,会造成各个excutor节点资源利用率不均衡,影响任务的执行效率;Spark 3新特性AQE极大地优化了以上任务的执行效率。

2、 Spark 为什么需要AQE? (Why)

RBO(Rule Based Optimization,基于规则的优化),它往往基于一些规则和策略实现,如谓词下推、列裁剪,这些规则和策略来源于数据库领域已有的应用经验。

RBO实际上算是一种经验主义。经验主义的弊端就是对待相似的问题和场景都使用同一类套路。

Spark 社区正是因为意识到了 RBO 的局限性,因此在 2.x 版本中推出了CBO(Cost Based Optimization,基于成本的优化)。

 

CBO 是基于数据表的统计信息(如表大小、数据列分布)来选择优化策略。CBO 支持的统计信息很丰富,比如数据表的行数、每列的基数(Cardinality)、空值数、最大值、最小值等。因为有统计数据做支持,所以 CBO 选择的优化策略往往优于 RBO 选择的优化规则。
但是,CBO 也有三个方面的不足:
(1)、适用面太窄,CBO 仅支持注册到 Hive Metastore 的数据表,但在其他的应用场景中,数据源往往是存储在分布式文件系统的各类文件,如 Parquet、ORC、CSV 等。
(2)、统计信息的搜集效率比较低。对于注册到 Hive Metastore 的数据表,开发者需要调用 ANALYZE TABLE COMPUTE STATISTICS 语句收集统计信息,而各类信息的收集会消耗大量时间。
(3)、静态优化,RBO、CBO执行计划一旦制定完成,就会按照该计划坚定不移地执行;如果在运行时数据分布发生动态变化,先前制定的执行计划并不会跟着调整、适配。

 

3、 AQE 到底是什么?(What)

考虑到 RBO 和 CBO 的种种限制,Spark 在 3.0 版本推出了 AQE(Adaptive Query Execution,自适应查询执行)。用一句话来概括,AQE 是 Spark SQL 的一种动态优化机制,在运行时,每当 Shuffle Map 阶段执行完毕,AQE 都会结合这个阶段的统计信息,基于既定的规则动态地调整、修正尚未执行的逻辑计划和物理计划,来完成对原始查询语句的运行时优化。
AQE 赖以优化的统计信息与 CBO 不同,这些统计信息并不是关于某张表或是哪个列,而是 Shuffle Map 阶段输出的中间文件。

 

二. AQE如何使用?

AQE三大特性:自动分区合并 、自动数据倾斜处理、Join 策略调整。

1. 自动分区合并(Partition Coalescing)‌

‌场景‌:当Shuffle后产生大量小分区(如文件数过多或数据分布不均),导致任务调度开销过大或资源浪费。
‌示例‌:

  • 对日志数据进行GROUP BY聚合操作时,若原始数据被分为1000个小文件,AQE会动态合并相邻小分区,减少下游Task数量,避免因Task过多导致资源争抢或调度延迟‌
-- 启用AQE后,Spark自动合并小分区
SET spark.sql.adaptive.enabled=true;
SELECT user_id, COUNT(*) FROM logs GROUP BY user_id;

‌2. Join策略动态切换(Switch Join Strategy)‌

‌场景‌:静态优化器选择的Join策略(如Sort-Merge Join)在运行时因数据量变化不再高效。
‌示例‌:

  • 表A(1亿条)与表B(过滤后仅1万条)进行Join时,AQE在运行时检测到表B数据量极小,自动将Sort-Merge Join切换为Broadcast Join,减少Shuffle开销‌
-- 动态切换为Broadcast Join
SELECT /*+ MERGE(a, b) */ * FROM large_table a JOIN small_table b ON a.id = b.id;

‌3. 倾斜Join优化(Optimize Skew Join)‌

‌场景‌:Join操作中某些Key的数据量极大(如热门商品或用户行为数据倾斜),导致单个Task处理时间过长。
‌示例‌:

  • 用户行为表与商品表Join时,商品ID为“hot_item_123”的记录占总量80%。AQE将该Key对应的数据拆分为多个子分区,并行处理以平衡负载‌
-- 自动拆分倾斜Key
SET spark.sql.adaptive.skewJoin.enabled=true;
SELECT a.user_id, b.item_name FROM user_actions a JOIN items b ON a.item_id = b.item_id;

‌4. 动态优化聚合操作‌

‌场景‌:聚合操作(如GROUP BY)因数据分布不均导致部分Task内存不足。
‌示例‌:

  • 统计每个城市的订单总金额时,若北上广深数据量远超其他城市,AQE会动态调整Hash聚合为Sort聚合,减少内存压力‌

‌启用AQE的核心配置‌

-- 基础配置
SET spark.sql.adaptive.enabled=true;
SET spark.sql.adaptive.coalescePartitions.enabled=true; -- 合并小分区
SET spark.sql.adaptive.skewJoin.enabled=true; -- 倾斜Join优化

-- 高级调优(根据集群规模调整)
SET spark.sql.adaptive.advisoryPartitionSizeInBytes=64MB; -- 分区合并目标大小
SET spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes=256MB; -- 倾斜判定阈值


‌适用场景总结‌

‌场景‌‌问题特征‌‌AQE优化动作‌
小文件过多 大量小分区导致Task数量激增 动态合并相邻分区
Join策略选择错误 大表Join小表时静态优化器未选广播Join 切换为Broadcast Join
数据倾斜严重 单个Task处理时间远高于其他Task 拆分倾斜Key数据到多个Task
聚合内存压力大 哈希聚合导致OOM 切换为Sort聚合或调整分区大小

‌注‌:AQE在Spark 3.0+版本默认启用,需结合集群资源和数据特征调整参数以最大化性能‌

posted @ 2025-03-25 11:18  业余砖家  阅读(621)  评论(0)    收藏  举报