Spark SQL 性能调优大师

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

从数据倾斜七种场景诊断到 Spark 3.x AQE 自适应执行——"让每个 Spark Job 都跑出最优性能"。覆盖 Join 策略选择、Shuffle 优化、内存管理、Spark UI 诊断,附带 Docker 一键搭建 Spark+Jupyter 实验环境。

一句话选型结论

按资料字段整理

Spark SQL 性能调优大师是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_69009747/spark-sql-performance-tuner
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_69009747仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791618814
正文体量13.0 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
Spark SQL 性能调优大师当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

13.0 KB
模型实际读到的说明书原文,来自技能仓库

Spark SQL 性能调优大师

> 从数据倾斜到 Shuffle 优化,从 Spark UI 诊断到 AQE 自适应——"让每个 Spark Job 都跑出最优性能"。

一、Spark 性能调优全景图

                    ┌──────────────────────┐
                    │   Spark 性能调优      │
                    └──────────┬───────────┘
                               │
     ┌─────────────┬───────────┼───────────┬─────────────┐
     ▼             ▼           ▼           ▼             ▼
  数据倾斜      Shuffle     Join策略    内存管理      AQE自适应
  诊断修复      优化         选择         调优          执行

二、快速开始

# 启动 Spark 集群 + Jupyter 环境
docker-compose up -d

# 访问 Spark Master UI: http://localhost:8080
# 访问 Jupyter: http://localhost:8888 (token: spark)

# 运行性能诊断脚本
python scripts/diagnose_skew.py --table orders --key user_id

# 运行调优建议生成器
python scripts/tuning_advisor.py --app-id app-20260601-0001

三、数据倾斜:7种场景诊断与修复

3.1 倾斜诊断

# scripts/diagnose_skew.py
"""自动诊断数据倾斜:分析 key 分布,识别倾斜 key"""

def diagnose_skew(spark, table, key_column):
    """诊断指定表的指定 key 是否存在数据倾斜"""
    df = spark.table(table)

    # 统计 key 分布
    key_dist = df.groupBy(key_column).count()

    # 计算倾斜指标
    stats = key_dist.select(
        F.count(key_column).alias('total_keys'),
        F.max('count').alias('max_count'),
        F.min('count').alias('min_count'),
        F.avg('count').alias('avg_count'),
        F.stddev('count').alias('stddev_count'),
        F.percentile_approx('count', 0.99).alias('p99_count'),
    ).collect()[0]

    skew_ratio = stats['max_count'] / stats['avg_count']
    is_skewed = skew_ratio > 10  # 最大key数据量超过平均10倍

    # 找出倾斜 key
    threshold = stats['avg_count'] * 5
    skewed_keys = key_dist.filter(F.col('count') > threshold)

    return {
        'is_skewed': is_skewed,
        'skew_ratio': round(skew_ratio, 2),
        'skewed_key_count': skewed_keys.count(),
        'top_skewed_keys': skewed_keys.orderBy(F.desc('count')).limit(10).collect(),
        'recommendation': get_skew_fix_recommendation(skew_ratio)
    }

3.2 七种场景与修复方案

场景1:单 key 数据量极大(如 null 值倾斜)

# 方案:加盐(Salting)
def salt_join(df1, df2, join_key, salt_num=100):
    """对倾斜 key 加随机盐,分散到多个分区"""
    df1_salted = df1.withColumn(
        'salt', (F.rand() * salt_num).cast('int')
    ).withColumn(
        'salted_key', F.concat(F.col(join_key), F.lit('_'), F.col('salt'))
    )

    # df2 需要膨胀 salt_num 倍
    df2_exploded = df2.crossJoin(
        spark.range(0, salt_num).toDF('salt')
    ).withColumn(
        'salted_key', F.concat(F.col(join_key), F.lit('_'), F.col('salt'))
    )

    return df1_salted.join(df2_exploded, 'salted_key')

场景2:大表 join 小表(Broadcast Join)

# 方案:显式 Broadcast Hint
from pyspark.sql.functions import broadcast

result = large_df.join(
    broadcast(small_df),  # 小表广播到所有 Executor
    'join_key',
    'left'
)

# 自动 Broadcast 阈值
spark.conf.set('spark.sql.autoBroadcastJoinThreshold', 10 * 1024 * 1024)  # 10MB

场景3:两张大表 join 都倾斜

# 方案:分离倾斜 key 单独处理
def split_skew_join(df1, df2, join_key, skew_keys):
    """将倾斜 key 和非倾斜 key 分开 join"""
    # 倾斜部分:加盐 join
    df1_skew = df1.filter(F.col(join_key).isin(skew_keys))
    df2_skew = df2.filter(F.col(join_key).isin(skew_keys))
    result_skew = salt_join(df1_skew, df2_skew, join_key)

    # 非倾斜部分:正常 join
    df1_normal = df1.filter(~F.col(join_key).isin(skew_keys))
    df2_normal = df2.filter(~F.col(join_key).isin(skew_keys))
    result_normal = df1_normal.join(df2_normal, join_key)

    return result_skew.union(result_normal)

场景4:GroupBy 聚合倾斜

# 方案:两阶段聚合(局部聚合 + 全局聚合)
def two_phase_agg(df, group_key, agg_col):
    """先加盐局部聚合,再去盐全局聚合"""
    salt_num = 100

    # Phase 1: 加盐局部聚合
    phase1 = df.withColumn('salt', (F.rand() * salt_num).cast('int')) \
               .groupBy(group_key, 'salt') \
               .agg(F.sum(agg_col).alias('partial_sum'),
                    F.count(agg_col).alias('partial_count'))

    # Phase 2: 去盐全局聚合
    phase2 = phase1.groupBy(group_key) \
                   .agg(F.sum('partial_sum').alias('total_sum'),
                        F.sum('partial_count').alias('total_count'))

    return phase2

场景5:窗口函数倾斜

# 方案:限制窗口范围 + 增加分区数
result = df.withColumn(
    'row_num',
    F.row_number().over(
        Window.partitionBy('user_id')
              .orderBy(F.desc('amount'))
              .rowsBetween(Window.unboundedPreceding, Window.currentRow)
    )
).repartition(200)  # 增加分区数

场景6:读写小文件倾斜

# 方案:合并小文件
# 读取时合并
spark.conf.set('spark.sql.files.maxPartitionBytes', 128 * 1024 * 1024)  # 128MB
spark.conf.set('spark.sql.files.openCostInBytes', 4 * 1024 * 1024)      # 4MB

# 写入时合并(Coalesce)
df.coalesce(10).write.parquet('/output/')

# 或使用 AQE 自动合并
spark.conf.set('spark.sql.adaptive.coalescePartitions.enabled', 'true')
spark.conf.set('spark.sql.adaptive.coalescePartitions.minPartitionSize', '1MB')

场景7:笛卡尔积倾斜

# 方案:避免笛卡尔积,改用 Broadcast Nested Loop Join
# 错误写法(笛卡尔积)
df1.crossJoin(df2)

# 正确写法:如果 df2 很小
df1.join(broadcast(df2), how='cross')

# 如果必须笛卡尔积,增加并行度
spark.conf.set('spark.sql.crossJoin.enabled', 'true')
spark.conf.set('spark.sql.shuffle.partitions', 500)

四、Shuffle 优化

4.1 Shuffle 分区数调优

# 黄金公式:shuffle 分区数 = 总数据量 / 目标分区大小
# 目标分区大小:128MB ~ 256MB

total_data_size_mb = 100 * 1024  # 100GB
target_partition_mb = 200         # 200MB
optimal_partitions = total_data_size_mb / target_partition_mb  # 512

spark.conf.set('spark.sql.shuffle.partitions', optimal_partitions)

4.2 AQE 自适应查询执行

# Spark 3.x AQE 三大核心优化
spark.conf.set('spark.sql.adaptive.enabled', 'true')

# 1. 自动合并小分区
spark.conf.set('spark.sql.adaptive.coalescePartitions.enabled', 'true')
spark.conf.set('spark.sql.adaptive.coalescePartitions.minPartitionNum', 1)
spark.conf.set('spark.sql.adaptive.advisoryPartitionSizeInBytes', '128MB')

# 2. 自动切换 Join 策略(SortMerge → Broadcast)
spark.conf.set('spark.sql.adaptive.autoBroadcastJoinThreshold', '20MB')

# 3. 自动处理倾斜 Join
spark.conf.set('spark.sql.adaptive.skewJoin.enabled', 'true')
spark.conf.set('spark.sql.adaptive.skewJoin.skewedPartitionFactor', 5)
spark.conf.set('spark.sql.adaptive.skewJoin.skewedPartitionThresholdInBytes', '256MB')

4.3 动态分区裁剪(DPP)

# Spark 3.x 默认开启,无需额外配置
# 效果:大表 join 维度表时,自动裁剪大表分区

# 示例:orders (按日期分区) join dim_date (过滤最近7天)
# DPP 自动将 orders 只扫描最近7天的分区
result = spark.table('orders').join(
    spark.table('dim_date').filter("date >= '2026-05-25'"),
    'date_key'
)
# Spark 自动识别:orders 只需扫描 2026-05-25 ~ 2026-06-01 的分区

五、Join 策略选择

Join 类型 触发条件 性能 适用场景
Broadcast Hash Join 小表 < autoBroadcastJoinThreshold ★★★★★ 小表 < 10MB
Shuffle Hash Join 两表大小相近,可构建哈希表 ★★★★ 中等表 join
Sort Merge Join 默认策略,两表都大 ★★★ 大表 join 大表
Broadcast Nested Loop 非等值 join + 小表 ★★ 范围 join
Cartesian Product crossJoin ★ 尽量避免
# 强制指定 Join 策略
from pyspark.sql.functions import broadcast

# Broadcast Hint
df1.join(broadcast(df2), &#x27;key&#x27;)

# Shuffle Merge Hint
df1.join(df2.hint(&#x27;merge&#x27;), &#x27;key&#x27;)

# Shuffle Hash Hint
df1.join(df2.hint(&#x27;shuffle_hash&#x27;), &#x27;key&#x27;)

# Shuffle Replicate NL Hint(非等值 join)
df1.join(df2.hint(&#x27;shuffle_replicate_nl&#x27;), df1.col &gt; df2.col)

六、Spark UI 性能诊断

6.1 关键指标速查

UI 页面 关键指标 正常值 异常信号
Jobs Duration — 单个 Job > 1h
Stages Shuffle Read/Write — Shuffle > 100GB
Stages Task Duration 分布 均匀 个别 Task 远大于中位数
Stages Input Size / Records — 个别 Task 输入量远超平均
Executors GC Time < 10% GC Time > 30%
Executors Spill (Memory/Disk) 0 有 Spill 发生
SQL Scan / Exchange / Join — Exchange 数据量过大
Storage Cached RDD % — Cache 未命中

6.2 诊断脚本

# scripts/tuning_advisor.py
&quot;&quot;&quot;从 Spark History Server 拉取指标,自动生成调优建议&quot;&quot;&quot;

def analyze_application(app_id):
    &quot;&quot;&quot;分析 Spark Application 性能瓶颈&quot;&quot;&quot;
    app = get_app_metrics(app_id)
    issues = []

    # 检查1:数据倾斜
    for stage in app[&#x27;stages&#x27;]:
        task_times = [t[&#x27;duration&#x27;] for t in stage[&#x27;tasks&#x27;]]
        max_time = max(task_times)
        median_time = sorted(task_times)[len(task_times)//2]
        if max_time &gt; median_time * 3:
            issues.append({
                &#x27;type&#x27;: &#x27;DATA_SKEW&#x27;,
                &#x27;stage&#x27;: stage[&#x27;id&#x27;],
                &#x27;severity&#x27;: &#x27;HIGH&#x27;,
                &#x27;detail&#x27;: f&#x27;Max task {max_time}ms vs Median {median_time}ms&#x27;,
                &#x27;fix&#x27;: &#x27;使用加盐或两阶段聚合&#x27;
            })

    # 检查2:Shuffle 过大
    total_shuffle = sum(s[&#x27;shuffle_read&#x27;] for s in app[&#x27;stages&#x27;])
    if total_shuffle &gt; 100 * 1024 * 1024 * 1024:  # 100GB
        issues.append({
            &#x27;type&#x27;: &#x27;EXCESSIVE_SHUFFLE&#x27;,
            &#x27;severity&#x27;: &#x27;MEDIUM&#x27;,
            &#x27;detail&#x27;: f&#x27;Total shuffle: {total_shuffle/1024/1024/1024:.1f}GB&#x27;,
            &#x27;fix&#x27;: &#x27;增加 Broadcast Join、减少不必要的 Shuffle&#x27;
        })

    # 检查3:GC 压力
    for executor in app[&#x27;executors&#x27;]:
        gc_ratio = executor[&#x27;gc_time&#x27;] / executor[&#x27;total_time&#x27;]
        if gc_ratio &gt; 0.1:
            issues.append({
                &#x27;type&#x27;: &#x27;GC_PRESSURE&#x27;,
                &#x27;severity&#x27;: &#x27;HIGH&#x27;,
                &#x27;detail&#x27;: f&#x27;GC ratio: {gc_ratio:.1%}&#x27;,
                &#x27;fix&#x27;: &#x27;增加 executor memory 或减少 cache&#x27;
            })

    return issues

七、内存管理

# 内存分配公式
# Total Memory = spark.executor.memory
#   ├── Reserved Memory (300MB, 固定)
#   ├── User Memory (40% × (Total - 300MB))
#   └── Spark Memory (60% × (Total - 300MB))
#       ├── Storage Memory (50%)
#       └── Execution Memory (50%)

# 推荐配置
spark.conf.set(&#x27;spark.executor.memory&#x27;, &#x27;8g&#x27;)
spark.conf.set(&#x27;spark.executor.memoryOverhead&#x27;, &#x27;2g&#x27;)  # 堆外内存
spark.conf.set(&#x27;spark.memory.fraction&#x27;, 0.6)            # Spark 可用比例
spark.conf.set(&#x27;spark.memory.storageFraction&#x27;, 0.5)     # Storage 占比

# 避免 Spill
spark.conf.set(&#x27;spark.sql.windowExec.buffer.spill.threshold&#x27;, 4096)

八、生产 Checklist

  • [ ] spark.sql.adaptive.enabled = true(Spark 3.x 必开)
  • [ ] spark.sql.adaptive.skewJoin.enabled = true
  • [ ] spark.sql.shuffle.partitions 根据数据量合理设置
  • [ ] 小表 join 使用 broadcast() hint
  • [ ] 避免 collect() 大数据到 Driver
  • [ ] 避免 groupByKey,优先用 reduceByKey
  • [ ] 避免在 UDF 中使用外部连接
  • [ ] Kryo 序列化替代 Java 序列化
  • [ ] 合理设置 spark.dynamicAllocation.enabled

九、常见问题

问题 原因 解决
OOM (Out of Memory) Executor 内存不足 增加 executor.memory 或减少 shuffle.partitions
个别 Task 极慢 数据倾斜 加盐 / 两阶段聚合 / 分离倾斜 key
Shuffle 数据量巨大 Join key 分布不均 Broadcast Join / AQE Skew Join
GC 频繁 内存不足或对象过多 增加内存 / 使用 Kryo / 减少 cache
小文件过多 分区数过多 coalesce() 合并 / AQE 自动合并

常见问题

5 问
答案由本站依据条目字段整理
Spark SQL 性能调优大师怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_69009747/spark-sql-performance-tuner。安装方式为未标注。装完刷新技能列表或重开客户端。

Spark SQL 性能调优大师支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

Spark SQL 性能调优大师权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

Spark SQL 性能调优大师收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

Spark SQL 性能调优大师用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。