1. 数据倾斜初探
我记得那是刚接手一个实时数仓项目不久,凌晨两点被值班电话叫醒。用户反馈说每天凌晨的ETL任务跑了三个小时还没结束,而正常情况下应该半小时搞定。我登录集群一看,80%的task早就跑完了,剩下几个task卡在那里一动不动,内存飙升,GC频繁——典型的“数据倾斜”症状。
说白了,数据倾斜就是分布式计算中“劳逸不均”的问题。你想想看,本来100个任务并行处理,大家分到的数据量差不多,很快就能一起收工。但现实往往不按剧本走——某个key的数据量特别大,导致一个task要处理90%的数据,其他task只能干等着。这就是数据倾斜。
核心定义:数据倾斜是指在分布式计算中,数据被分配到各个并行任务时,分布严重不均,导致部分任务负载过高、执行时间远超其他任务的现象。
1.1 数据倾斜的典型表现
我在项目中遇到过三种最常见的表现,你可以对照着排查一下:
- 任务卡死(Hang):大部分task已经完成,但少数几个task进度条卡在99%不动。这种情况在Spark和MapReduce中特别常见,尤其是reduce阶段。
- OOM(内存溢出):某个task处理的数据量太大,超出了分配给它的内存上限。我见过一个案例,某个join操作中,一个key关联了上亿条记录,直接撑爆了executor的堆内存。
- 长尾任务(Long-tail Task):任务整体运行时间被少数几个task拉长。比如100个task,99个在1分钟内完成,剩下1个跑了30分钟——整个作业的完成时间就是30分钟。
避坑指南:我曾经犯过一个错误——看到OOM就盲目加大内存。结果内存是够了,但GC时间反而更长,任务依然跑不动。后来才明白,数据倾斜的核心问题是数据分布,不是内存大小。
1.2 数据倾斜对分布式计算的影响
数据倾斜的影响不只是“慢一点”那么简单。它会影响整个集群的效率和稳定性:
| 影响维度 | 具体表现 | 后果 |
|---|---|---|
| 资源利用率 | 部分节点满载,其他节点空闲 | 集群整体吞吐量下降,资源浪费 |
| 任务延迟 | 长尾任务拖慢整个作业 | SLA无法保障,实时性要求高的场景直接失效 |
| 稳定性 | 倾斜节点容易OOM或GC频繁 | 任务失败、重试,甚至影响其他作业 |
| 成本 | 为了应对倾斜而过度配置资源 | 计算成本成倍增加 |
举个例子,你想想看:一个Spark作业需要处理100GB数据,正常情况下分配10个executor,每个处理10GB,10分钟跑完。但如果某个key占了50GB数据,那这个executor就要处理50GB,其他9个executor只处理剩下的50GB。结果就是:9个executor5分钟跑完,1个executor跑了25分钟——整个作业耗时25分钟,资源利用率只有40%。
1.3 数据倾斜的常见场景
根据我的经验,数据倾斜最容易出现在以下几个地方:
- Join操作:两个表关联时,如果关联键的分布不均匀,比如订单表按用户ID关联用户表,但某些“大客户”的订单量是普通用户的几千倍。
- Group By操作:按某个字段聚合时,如果该字段的值分布极不均匀,比如按城市统计销售额,一线城市的销售额可能是小城市的几百倍。
- Distinct Count:去重计数时,如果某个key对应的数据量特别大,会导致单个task处理压力剧增。
- Reduce端操作:MapReduce或Spark的shuffle阶段,数据从map端传输到reduce端时,如果分区策略不当,很容易造成数据倾斜。
小技巧:我习惯在任务启动前先做一次数据探查——用df.groupBy("key").count().orderBy(desc("count")).show(10)看看top N key的数据量分布。如果最大key的数据量是平均值的10倍以上,基本可以断定会有倾斜问题。
1.4 数据倾斜的本质:分区策略与数据分布的不匹配
为什么会出现数据倾斜?说白了,就是分区策略没有考虑到数据的实际分布。默认的Hash分区假设数据是均匀分布的,但现实中的数据往往遵循“二八定律”——20%的key包含了80%的数据。
我画了一张图,帮你理解数据倾斜的本质:
1.5 什么时候该警惕数据倾斜?
嗯,这里要注意——不是所有数据分布不均都是数据倾斜。分布式计算本身就有一定的随机性,10%以内的偏差是正常的。我个人习惯用以下几个指标来判断:
- 任务执行时间方差:如果最慢task的执行时间是最快task的5倍以上,基本可以确认有倾斜。
- 数据量分布:某个分区的数据量是平均值的3倍以上,需要关注。
- GC频率:某个task的GC时间占比超过20%,而且其他task正常,大概率是倾斜导致的内存压力。
实用命令:在Spark UI中,查看“Stages”页面,按“Duration”排序。如果看到某个task的Shuffle Read Size明显大于其他task,那就是倾斜的“罪魁祸首”。