> 技术文档 > Spark 性能调优的黄金法则总结

Spark 性能调优的黄金法则总结

以下是针对 Spark 性能调优黄金法则的深度解析与补充,结合 Catalyst 优化器Tungsten 执行引擎的核心优化技术,形成完整的调优体系:


1. 避免 Shuffle(最高优先级)

  • Broadcast Join:
    • 条件: 小表需能放入 Executor 内存(默认 < 10MB,可通过 spark.sql.autoBroadcastJoinThreshold 调整至 100MB)。
    • 手动指定: df1.join(broadcast(df2), \"key\")
  • reduceByKey vs groupByKey:
    • groupByKey 全量数据 Shuffle → OOM 风险高
    • reduceByKey 本地 Combine 后再 Shuffle → 网络传输量减少 90%+
  • partitionBy分区
    • 对需多次 Join/聚合的 RDD 提前按 Key 分区,避免后续操作触发 Shuffle。

2. 优化无法避免的 Shuffle

  • 调整分区数:
    • 参数:
      • spark.sql.shuffle.partitions (默认 200)→ SQL/DataFrame
      • spark.default.parallelism (默认 CPU 核心数)→ RDD
    • 公式参考: 分区数 = min(集群总核心数 * 4, 数据量 / 128MB)
  • Kryo 序列化:
    spark.conf.set(\"spark.serializer\", \"org.apache.spark.serializer.KryoSerializer\")spark.conf.set(\"spark.kryo.registrationRequired\", \"true\")spark.conf.registerKryoClasses(Array(classOf[MyCustomClass]))
    • 效果: 序列化速度 2-5x 提升,体积 减少 50-80%

3. 内存优化与 GC 调优

  • 内存结构:#mermaid-svg-FqAqLBTNUQal6voa {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FqAqLBTNUQal6voa .error-icon{fill:#552222;}#mermaid-svg-FqAqLBTNUQal6voa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FqAqLBTNUQal6voa .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-FqAqLBTNUQal6voa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FqAqLBTNUQal6voa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FqAqLBTNUQal6voa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FqAqLBTNUQal6voa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FqAqLBTNUQal6voa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FqAqLBTNUQal6voa .marker.cross{stroke:#333333;}#mermaid-svg-FqAqLBTNUQal6voa svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FqAqLBTNUQal6voa .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FqAqLBTNUQal6voa .cluster-label text{fill:#333;}#mermaid-svg-FqAqLBTNUQal6voa .cluster-label span{color:#333;}#mermaid-svg-FqAqLBTNUQal6voa .label text,#mermaid-svg-FqAqLBTNUQal6voa span{fill:#333;color:#333;}#mermaid-svg-FqAqLBTNUQal6voa .node rect,#mermaid-svg-FqAqLBTNUQal6voa .node circle,#mermaid-svg-FqAqLBTNUQal6voa .node ellipse,#mermaid-svg-FqAqLBTNUQal6voa .node polygon,#mermaid-svg-FqAqLBTNUQal6voa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FqAqLBTNUQal6voa .node .label{text-align:center;}#mermaid-svg-FqAqLBTNUQal6voa .node.clickable{cursor:pointer;}#mermaid-svg-FqAqLBTNUQal6voa .arrowheadPath{fill:#333333;}#mermaid-svg-FqAqLBTNUQal6voa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FqAqLBTNUQal6voa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FqAqLBTNUQal6voa .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-FqAqLBTNUQal6voa .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-FqAqLBTNUQal6voa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FqAqLBTNUQal6voa .cluster text{fill:#333;}#mermaid-svg-FqAqLBTNUQal6voa .cluster span{color:#333;}#mermaid-svg-FqAqLBTNUQal6voa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FqAqLBTNUQal6voa :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}Executor JVM HeapStorage MemoryExecution MemoryRDD CachingShuffle BuffersSort/Merge Joins
  • 关键配置: 参数 默认值 优化建议 spark.memory.fraction 0.6 缓存需求大 → 0.7 spark.memory.storageFraction 0.5 计算内存不足 → 0.3 spark.memory.offHeap.enabled false 堆外内存 > 32GB 时启用 spark.memory.offHeap.size 0 建议不超过物理内存 70%
  • 持久化级别选择: 级别 适用场景 MEMORY_ONLY 内存充足的小数据集 MEMORY_ONLY_SER 内存受限 + 可接受 CPU 开销 MEMORY_AND_DISK_SER 超大数据集 + 允许磁盘溢出

4. 并行度与数据分区

  • 分区大小黄金法则: 128MB - 256MB/分区
  • 计算公式:
    目标分区数 = 数据总大小 / 128MB
  • 场景调整:
    // 读取时调整spark.read.option(\"maxPartitionBytes\", \"134217728\") // 128MB// 处理中调整df.repartition(1000) // 触发 Shuffledf.coalesce(100) // 无 Shuffle 合并

5. 数据倾斜解决方案

方法 1:随机盐(通用方案)
// 大表加盐val saltedDF = bigDF.withColumn(\"salted_key\", concat($\"key\", lit(\"_\"), floor(rand() * 100)))// 小表扩容val saltedSmallDF = smallDF .withColumn(\"salt\", explode(lit((0 until 100).toArray))) .withColumn(\"salted_key\", concat($\"key\", lit(\"_\"), $\"salt\"))// 盐化后 JoinsaltedDF.join(saltedSmallDF, \"salted_key\")
方法 2:分离倾斜 Key(精准打击)
// 1. 识别倾斜 Key(TOP 1)val skewKeys = df.groupBy(\"key\").count() .orderBy(desc(\"count\")).limit(1).select(\"key\").collect()// 2. 拆解数据集val skewedDF = df.filter($\"key\" === skewKeys(0)) // 倾斜部分val normalDF = df.filter($\"key\" =!= skewKeys(0)) // 正常部分// 3. 分别处理 + 合并val resultSkew = skewedDF.join(broadcast(smallDF), \"key\")val resultNormal = normalDF.join(broadcast(smallDF), \"key\")resultSkew.union(resultNormal)

6. 资源分配策略(YARN 示例)

参数 计算公式 示例(100 核集群) Executor 数量 总核数 / 单 Executor 核数 100 / 5 = 20 Executor 核数 3-5(避免过多导致 HDFS 连接竞争) --executor-cores 5 Executor 内存 (容器内存 - 1GB) * 0.9 --executor-memory 24G Driver 内存 广播大表时需增加 --driver-memory 8G

⚠️ 避坑指南: Executor 堆内存 > 32GB 时,启用 G1GC:

spark-submit --conf \"spark.executor.extraJavaOptions=-XX:+UseG1GC\"

7. Catalyst & Tungsten 深度优化

Catalyst 优化器(逻辑优化)
  • 优化触发条件: 使用 DataFrame/Dataset/SQL API(非 RDD!)
  • 核心优化技术: 技术 效果 示例 谓词下推 提前过滤数据 df.filter(\"age>30\").join(...) → 先过滤再 Join 列剪裁 跳过无关列读取 SELECT name FROM tb → Parquet 只读 name 列 常量折叠 预先计算常量 SELECT 1+1 AS two → 直接输出 2 成本优化 自动选择 Join 策略 小表 → BroadcastJoin;大表 → SortMergeJoin
Tungsten 执行引擎(物理优化)
  • 三大革新:
    1. 二进制内存格式:
      • 数据以 字节数组 存储,避免 JVM 对象开销
      • 减少 GC 压力 + 内存占用降低 40%
    2. 全阶段代码生成:
      • 将查询编译为 JVM 字节码(非逐行解释执行)
      • 性能提升 10-100x(尤其聚合、过滤操作)
    3. Cache Locality:
      • 优化 CPU 缓存命中率,利用向量化指令

调优实践路线图

#mermaid-svg-bztEoeCfzmwfqHl8 {font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 .error-icon{fill:#552222;}#mermaid-svg-bztEoeCfzmwfqHl8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bztEoeCfzmwfqHl8 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-bztEoeCfzmwfqHl8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bztEoeCfzmwfqHl8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bztEoeCfzmwfqHl8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bztEoeCfzmwfqHl8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bztEoeCfzmwfqHl8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bztEoeCfzmwfqHl8 .marker.cross{stroke:#333333;}#mermaid-svg-bztEoeCfzmwfqHl8 svg{font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bztEoeCfzmwfqHl8 .label{font-family:\"trebuchet ms\",verdana,arial,sans-serif;color:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 .cluster-label text{fill:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 .cluster-label span{color:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 .label text,#mermaid-svg-bztEoeCfzmwfqHl8 span{fill:#333;color:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 .node rect,#mermaid-svg-bztEoeCfzmwfqHl8 .node circle,#mermaid-svg-bztEoeCfzmwfqHl8 .node ellipse,#mermaid-svg-bztEoeCfzmwfqHl8 .node polygon,#mermaid-svg-bztEoeCfzmwfqHl8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-bztEoeCfzmwfqHl8 .node .label{text-align:center;}#mermaid-svg-bztEoeCfzmwfqHl8 .node.clickable{cursor:pointer;}#mermaid-svg-bztEoeCfzmwfqHl8 .arrowheadPath{fill:#333333;}#mermaid-svg-bztEoeCfzmwfqHl8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-bztEoeCfzmwfqHl8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-bztEoeCfzmwfqHl8 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-bztEoeCfzmwfqHl8 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-bztEoeCfzmwfqHl8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-bztEoeCfzmwfqHl8 .cluster text{fill:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 .cluster span{color:#333;}#mermaid-svg-bztEoeCfzmwfqHl8 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\"trebuchet ms\",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-bztEoeCfzmwfqHl8 :root{--mermaid-font-family:\"trebuchet ms\",verdana,arial,sans-serif;}Shuffle 量大GC 时间长Task 倾斜资源闲置监控 Web UI发现瓶颈避免/优化 Shuffle内存调优 + 数据结构优化数据倾斜治理提升并行度利用 Catalyst/Tungsten验证性能提升

黄金法则: 始终从 Web UI 指标出发,结合业务逻辑选择针对性优化策略。Catalyst 和 Tungsten 是 Spark 的“自动驾驶系统”,但开发者需通过优化代码和配置为其铺平道路。