相似优化表关联小表耗时差异过大技术求助
两张Liquid Clustered表与小表Broadcast Join的性能差异问题
我有两张已优化的表,二者均基于同一列做Liquid Clustered,关联列相同。使用Broadcast Join将它们分别与另一张小表执行Inner Join时,耗时差异显著:
- Table1:8TB、151列,耗时约2小时
- Table2:5.5TB、90列,耗时仅约9分钟
另外我发现,Table1关联的执行摘要中没有针对大表的ColumnarToRow节点和第二个WholeStageCodegen节点。
表信息
Table1
- 大小:8TB
- 列数:151
Table2
- 大小:5.5TB
- 列数:90
执行摘要
与Table1关联的执行摘要
Completed in 6292595ms Node(row) *WholeStageCodegen(1) -*(5):ColumnarToRow(253,461) -*(6):Filter(253,461) (1):Scan parquet **table1** (10,305,644,145) (2):Filter(9,622,877,502) (3):Project (4):Scan parquet **smaller_table** (253,461) (7):Exchange (9):BroadcastHashJoin(28,206,425) (10):Project (11):WriteFiles (12):ExecuteWriteIntoDeltaCommand (13):ResultQueryStage (23):AdaptiveSparkPlan
与Table2关联的执行摘要
Completed in 567963 ms Node (row) *WholeStageCodegen(1) -*(6):ColumnarToRow (253,461) -*(7):Filter (253,461) *WholeStageCodegen(2) -*(2):ColumnarToRow (19,364,719,597) -*(3):Filter (17,548,253,303) -*(4):Project -*(10):BroadcastHashJoin (65,712,508) -*(11):Project (1):Scan parquet **table2** (19,364,719,597) (5):Scan parquet **smaller_table** (253,461) (8):Exchange (12):WriteFiles (13):ExecuteWriteIntoDeltaCommand (14):ResultQueryStage (24):AdaptiveSparkPlan
问题分析与解决思路
核心差异:代码生成与列处理路径不同
从执行计划对比可以看出,Table2的大表扫描、过滤、投影、Join全被打包进第二个WholeStageCodegen节点,而Table1的大表处理(Scan、Filter、Project)是单独的非Codegen阶段,且无大表的ColumnarToRow转换,这是性能差距的关键:
- WholeStageCodegen缺失的影响:WholeStageCodegen会将多个算子合并生成原生Java代码,避免算子间的序列化/反序列化和数据拷贝,大幅提升执行效率。Table1的大表处理未进入Codegen,每个算子单独处理数据,带来大量额外开销。
- 列处理模式差异:Table2的大表先转成行式数据再走Codegen优化,而Table1直接以列式数据处理后续算子。虽然列式存储适合扫描,但Spark对行式数据的Codegen优化更成熟,无Codegen的列式处理性能反而下降。
可能的触发原因
- 列数过多导致Codegen失败:Table1有151列,远多于Table2的90列。Spark的WholeStageCodegen对生成的代码大小有隐性限制(默认JVM方法大小64KB),列数过多会导致生成代码超出限制,自动禁用Codegen,回退到非Codegen的列式处理路径。
- 统计信息不准确:尽管两张表都用了Liquid Clustered,但Table1的统计信息可能偏差较大,导致Spark优化器选择了非最优执行路径。
- 投影列复杂度高:如果Table1关联后的投影涉及大量列或复杂计算,也可能导致Codegen无法生效。
验证与解决步骤
- 检查Codegen日志:开启
spark.sql.codegen.logLevel=DEBUG,运行查询后查看是否有Codegen failed日志,确认是否因列数过多导致Codegen失败。 - 调整Codegen参数:若为方法大小限制问题,可调大
spark.sql.codegen.maxMethodCodeSize(如设为128000),允许生成更大的方法代码,让Table1的大表处理进入Codegen阶段。 - 裁剪列:查询中只保留需要的列,减少Table1的列处理数量,降低Codegen的代码大小压力。
- 更新表统计信息:执行
ANALYZE TABLE table1 COMPUTE STATISTICS FOR ALL COLUMNS,让Spark优化器能更准确地选择执行计划。
内容的提问来源于stack exchange,提问作者user3648566
相关产品推荐
相关产品推荐

