4. 数据倾斜量化指标
聊了这么多倾斜的现象和原理,你可能会问:到底怎么才算倾斜? 光靠肉眼看 Spark UI 上的长尾任务,太主观了。我个人的习惯是——用三个量化指标,把倾斜程度变成数字。这样你调优前后一对比,效果一目了然。
今天我们就来拆解这三个核心指标:任务执行时间标准差、数据分区大小变异系数、Shuffle 数据量峰值/均值比。嗯,名字有点长,但用起来其实很简单。
4.1 任务执行时间标准差
先问个问题:为什么看平均值不够?
我记得有一次帮一个电商团队排查问题,他们看的是「任务平均执行时间」,只有 30 秒,觉得还行。但我一算标准差——45 秒。这说明什么?说明有的任务 5 秒跑完,有的跑了两分钟。平均值把极端情况全掩盖了。
标准差(Standard Deviation) 衡量的是任务执行时间的离散程度。公式不复杂:
σ = sqrt( Σ(xi - μ)² / N )
- xi:每个任务的执行时间
- μ:所有任务的平均执行时间
- N:任务总数
经验阈值:
- σ / μ < 0.3:任务分布均匀,基本无倾斜
- 0.3 ≤ σ / μ < 0.8:存在轻度倾斜,需要关注
- σ / μ ≥ 0.8:严重倾斜,必须干预
我在项目中一般用 变异系数(CV = σ / μ) 来归一化,这样不同规模的任务之间也能横向对比。你想想看,一个 100 个 task 的 Stage,和 1000 个 task 的 Stage,直接用标准差比不公平,但用 CV 就公平了。
实操建议: 在 Spark UI 的 Stages 页面,把每个任务的执行时间导出到 Excel,用 STDEV.P 函数一算就出来。或者写个简单的脚本解析 event log。
4.2 数据分区大小变异系数
这个指标更直接——看每个分区里到底装了多少数据。说白了,就是检查数据在分区之间的分配是否均匀。
我曾经遇到过一个极端案例:某个表按 user_id 分区,结果一个超级用户占了全表 40% 的数据。那个分区的大小是其他分区的 200 倍。你想想看,跑 Join 的时候,那个大分区就是整个作业的瓶颈。
计算方式跟上面类似:
CV_size = σ_size / μ_size
- σ_size:各分区大小的标准差
- μ_size:各分区大小的平均值
怎么拿到分区大小?我常用的方法:
- Spark UI → Storage 页面:直接看 RDD 或 DataFrame 各分区的大小
- 代码里用
glom()算子:把每个分区的数据收集成数组,然后算大小 - Shuffle 中间文件:看 Shuffle Write 阶段各分区的输出大小
注意: 用 glom() 的时候要小心,如果分区数据量太大,容易 OOM。我一般只在开发环境或者采样数据上用。
这里有个简单的采样脚本,你可以跑一下看看:
// 假设 df 是你的 DataFrame
val partitionSizes = df.rdd.glom().map(_.length).collect()
val mean = partitionSizes.sum.toDouble / partitionSizes.length
val std = math.sqrt(partitionSizes.map(x => math.pow(x - mean, 2)).sum / partitionSizes.length)
val cv = std / mean
println(s"分区大小变异系数: $cv")
如果 CV > 1.0,说明分区大小的离散程度已经超过平均值了——这基本就是重度倾斜。
4.3 Shuffle 数据量峰值/均值比
第三个指标,我把它叫做 「倾斜放大镜」。它专门用来衡量 Shuffle 阶段的数据倾斜。
公式很简单:
Ratio = Max(Shuffle_Write_Size) / Avg(Shuffle_Write_Size)
什么意思呢?就是看 Shuffle 写数据的时候,数据量最大的那个分区,是平均分区的多少倍。
我在项目中见过最夸张的一次,Ratio 达到了 150:1。也就是说,一个分区写了 150 份数据,其他分区平均只写 1 份。那个 Stage 跑了 40 分钟,而其他类似的 Stage 只要 2 分钟。
| Ratio 范围 | 倾斜程度 | 建议操作 |
|---|---|---|
| 1.0 - 2.0 | 正常 | 无需处理 |
| 2.0 - 5.0 | 轻度倾斜 | 观察,可考虑加盐或调整分区键 |
| 5.0 - 10.0 | 中度倾斜 | 建议干预,使用两阶段聚合或倾斜 Join 优化 |
| > 10.0 | 重度倾斜 | 必须处理,否则作业可能失败 |
这个指标的好处是——你不需要写代码。直接在 Spark UI 的 Stages 页面,点进某个 Stage,看「Shuffle Write Size」那一列,最大值和平均值一目了然。
我的诊断流程:
- 先看 任务执行时间 CV,快速判断有没有倾斜
- 如果有,再看 分区大小 CV,定位是哪个 Stage 倾斜
- 最后看 Shuffle 峰值/均值比,确认倾斜的严重程度
三步下来,倾斜的全貌就清楚了。
4.4 三个指标的关系与综合判断
这三个指标不是孤立的,它们从不同角度描述同一个问题:
- 任务执行时间标准差:看的是「结果」——任务跑得快慢差异大不大
- 分区大小变异系数:看的是「原因」——数据分布本身就不均匀
- Shuffle 峰值/均值比:看的是「过程」——Shuffle 阶段的数据倾斜有多严重
我一般会画一个这样的矩阵来判断优先级:
你看,这三个指标就像三盏灯——一盏亮了可能是小问题,三盏都亮了,那肯定是倾斜没跑了。
我的习惯: 每次调优前后,我都会把这几个指标记下来,做成一个简单的表格。比如:
- 调优前:CV=1.2, Ratio=25:1 → 重度倾斜
- 加盐后:CV=0.4, Ratio=3:1 → 轻度倾斜
- 优化后:CV=0.2, Ratio=1.5:1 → 正常
这样汇报的时候,领导一看就明白你做了什么,效果怎么样。