1. Spark SQL中DISTINCT的执行原理剖析
作为一名长期使用Spark SQL进行大数据处理的数据工程师,我发现很多开发者对DISTINCT操作的理解仅停留在语法层面。今天我将结合多年实战经验,深入解析Spark SQL中DISTINCT的底层执行机制,并分享几个关键的性能优化技巧。
1.1 DISTINCT操作的本质实现
Spark SQL并没有为DISTINCT操作设计专门的执行计划,而是巧妙地将其转换为两阶段聚合(Two-Stage Aggregation)模式。这种设计虽然增加了执行步骤,但保证了执行引擎的统一性和扩展性。
具体来说,当我们在SQL中使用COUNT(DISTINCT column)时,Spark会将其转换为以下等效操作:
- 先按GROUP BY的所有字段加上DISTINCT字段进行分组
- 然后在外层再按GROUP BY字段进行二次聚合
这种转换带来的好处是复用现有的聚合算子,但代价是需要额外的shuffle操作。
1.2 不同场景下的执行计划对比
1.2.1 无DISTINCT的基本聚合
对于简单的GROUP BY聚合查询,如:
sql复制SELECT exp_id, exp_name, sum(expose_nums)
FROM table
GROUP BY exp_id, exp_name
Spark会生成一个单阶段聚合计划,只需一次shuffle即可完成计算。
1.2.2 单字段DISTINCT聚合
当引入一个DISTINCT操作时:
sql复制SELECT exp_id, count(DISTINCT imei)
FROM table
GROUP BY exp_id
执行计划会变为两阶段:
- 先按(exp_id, imei)分组
- 再按exp_id分组计数
1.2.3 多字段DISTINCT聚合
对于多个DISTINCT操作:
sql复制SELECT exp_id,
count(DISTINCT imei),
count(DISTINCT req_id)
FROM table
GROUP BY exp_id
Spark会使用Expand操作将数据"膨胀"为多条记录
