1 什么是数据倾斜
数据倾斜即指在大数据计算任务中某个处理任务的进程(通常是一个JVM进程)被分配到的任务量过多,导致任务运行时间超长甚至最终失败,进而导致整个大任务超长时间运行或者失败。外部表现的话,在HiveSQL任务里看到map或者reduce的进度一直是99%持续数小时没有变化;在SparkSQL里则是某个stage里,正在运行的任务数量长时间是1或者2不变。总之如果任务进度信息一直在输出,但内容长时间没有任何变化的时候,大概率是出现数据倾斜了。有个特例需要注意,有时候大家会看到SparkSQL的任务信息也显示有1到2个任务在运行中,但进度信息不再刷新而表现为假死很久的时候,这通常是在进行最后阶段的文件操作,并不是数据倾斜(虽然这通常意味着小文件问题严重)。
再细分一下,倾斜可以分为以下四类:
- 读倾斜。即某个map(HiveSQL)或者task(SparkSQL)在读取数据阶段长期无法完成。这通常是因为文件分块过大或者此分块数据有异常。这种场景出现频率较小。
- 算倾斜。即在某个需要排序(如开窗函数或者非广播关联时)或者聚合操作的时候,同一个key(通常是一个或者多个字段或者表达式的组合)的处理耗时过长。这通常是最多的情况,情况也较为复杂。
- 写倾斜。即某个操作需要输出大量的数据,比如超过几亿甚至几十亿行。主要出现在关联后数据膨胀及某些只能由一个task来操作(如limit)的情况。
- 文件操作倾斜。即数据生成在临时文件夹后,由于数量巨大,重命名和移动的操作非常耗时。这通常发生在动态分区导致小文件的情况。目前在国内和印度区域已经因为我们默认进行小文件合并而不再存在这个情况,新加坡还有(我们在推动解决)。
2 为什么会有数据倾斜
大数据计算依赖多种分布式系统,需要将所有的计算任务和数据经过一定的规则分发到集群中各个可用的机器和节点上去执行,最后可能还需要进行汇总到少数节点进行最后的聚合操作,以及数据写到HDFS/S3等分布式存储系统里以永储存。这个过程被设计来应对大多数情况,并不能应对所有的情况。它具有以下几个特点:
- 业务数据分布规律无法预知。比如系统无法不经过计算而提前知道某个表的某个字段的取值分布是否大致均匀。
- 计算结果数量无法预知。比如两表关联的结果对于某些key(关联的一个字段或者多个字段组合)的输出行数无法不经过计算而预知进而针对性处理;又比如对某个字段的值进行split操作或者explode等操作后产生的结果数量无法预知而进行针对性的应对。
- 某些操作只能由单一节点进行。一切需要维护一个全局状态的大多数操作,如排序,Limit,count distinct,全局聚合等,一般会安排到一个节点来执行。
上述三个主要特点导致单节点处理的数据量有概率出现巨量,造成了所谓的倾斜问题。当然,这些困难并不是不可解决的。随着时间的推移,越来越多的针对性的优化措施已逐渐出现,也许在不久的将来业务同学不会再被倾斜问题烦恼。
3 解决案例
由于未来在OPPO主推SparkSQL,因此以下案例将主要以SparkSQL的角度来展示。
3.1 事实表关联事实表数据膨胀
最近有两个业务同学提出一个比较麻烦的问题,就是事实表关联事实表,其中有若干个key的输出达数十亿行,数据膨胀严重,造成数据计算和输出的倾斜。
比如以下场景:
我们统计了两个表的倾斜KEY值分布:
a表:
b表: