像很多Spark性能上的挑战一样,这些症状也会出现程序处理数据增加而增加.
什么是数据倾斜
在理想的Spark程序,当运行join操作, join key将要平均分布到不同分区, 非常漂亮的组织不同分区来处理. 真实的业务数据是非常罕见这么整齐的组织. 通常结束的数据不是理想组织那样,Spark集群性能被数据倾斜降级.
数据倾斜不是Spark本身问题,而是数据问题.引起数据倾斜是不均匀分布底层数据.在所有数据分布或是自然查询不均匀的分区有时是不可避免的.
对于join或是聚合操作,spark需要使相同key在一个分区.相同key的记录总是在同一个分区.类似的,其它key会分布到其它分区.如果其中一个分区变得非常大,这样会引起数据倾斜.如果没有特殊处理,会的查询引擎是个问题.
处理数据倾斜
数据倾斜在数据需要移改组非常容易出现,像join和聚合.改组操作被spark完成通过把关联数据放到同一个分区(相关的数据移动的相同key). 因为这样,spark需要在cluster上移动数据.所以数据改组是最消耗性能的操作.
通常会引起数据倾斜的症状:
- 运行在指定stage和task
- cpu利用率低
- 内存溢出错误
有几种技巧我位可以利用处理spark当中的数据倾斜.
确认和解决数据倾斜
Spark用户通常注意到大部分task都在合理的时间内完成,仅仅有一个任务一起在执行. 十分可能, 这表明是数据倾斜. 这也导致集群利用率低. 在去上运行spark这是个突出的问题. 集群上的资源浪费和昂贵的.
如果数据倾斜在数据源层(如果hive分区为_month key,并且表有非常多条记录在这个分区中),这会引起倾斜处理在读表的stage中. 在这种情况下表重建,用不同的分区key会有帮助. 然而一些情况是行不通的,在企业中表是被其它数据管道在用.
在这样例子中,有几种方式我位可以避免数据倾斜.
广播数据
如果在做join操作时有数据倾斜,一种方式是增加spark.sql.autoBroadcastJoinThreshold
值大小,这样小表的数据会被广播. 这要确保driver和执行节点上的内存够用.
数据处理
如果有很多null值在join或是group-by key中,它们将要导致数据倾斜操作. 试着用随机id预处理null值, 并且在程序中处理.
加盐
在SQL join中,join key被改变导致平均重新分配这些数据,这样处理分区不会花非常多的时间. 这个技术被称为加盐.用一个例子可以更好了解.在join或是group-by,spark map key到一个分区是通过在key上的hash code值,再把数据分组到不同分区.
假设有两张表
让我们考虑一个例子, 有一个特别的值数据倾斜非常严重,例如. key = 1, 并且想要join两张表,做一个group来得到数量.如:
在数组分组到stage是通过join操作之后,用这个key所有行都被分到相同的分区中.看上图,所有带有key 1的数据在分区1.类似的,所有为key 2的在分区2. 十分清楚处理分区1的时间大小分区2的时间,并且分区包含更多数据.看下上图 Spark UI关于分组数据stagef运行的时间.
正如你看到的,一个任务比其它任务运行更长时间.用更多数据会更明显.并且这也会引起程序的不稳定性,在一个分区的内存使用过大.
我们可以加入一些其它东西来使我们的数据集分区的更平均吗?大部分用户使用加盐方式来处理数据倾斜. 加盐技术是增加一个随机数到表中join的key. 在另外的表,需要复制行来匹配随机keys. 这个想法是如果join条件满足key1=key1,它也应该满足key_1=key1_.这个值可以帮助数据集更分区均匀.
例如看我的例子是怎样做的.看20行.使用当做一个随机函数, 炸开数据集. 这不同与除法数,是我想要处理数据倾斜的数.这是一个基础的例子,可以提高包含仅是几个key数据倾斜的.
现在再看Spark UI. 处理时间更平均了
注意小数据的性能差异不会有很大不同.有时压缩改组在整个运行中也扮演着角色.对于数据倾斜,数组改组可以被大力压缩由于重复的数据.因此,磁盘IO/网络IO占用会减少.我们需要运行我的程序没有盐和有盐,来最终达到合适自己的结果.
垃圾回收
Spark运行在JVM上.因为Spark可以存放大量数据在内存中, 非常依赖Java内存管理和GC.因此,GC是一个非常大的问题,可以影响Spark应用.
在Spark中执行GC常见状态:
- 应用运行速度
- 执行心跳超时
- GC过载限制导致错误
Spark以内存为中中和处理大量数据的应用使它比其它java应用更容易出问题.多亏非常容易诊断出Spark 遭受GC问题. Spark UI标记执行节点为红钯诮是花了太多时间在GC上.
Spark执行节点花费大量CPU执行垃圾回收. 通过查看Spark UI Executors 页面.spark红色表示花费超过10%的时间是垃圾回收,可以看下图
定位垃圾回收问题
有一些基本的事情,可以尝试定位垃圾回收问题.
数据结构
如果使用RDD开发程序,数据结构少用对象. 例如,用数组来代替list
序列化数据
如果你处理基本数据类型,考虑使用序列化数据结构像Koloboke或fastutil. 这些结构基本类型优化内存使用.
排序数据非堆存储
Spark执行引擎和Spark存储都存在非堆上. 可以使用转换堆存储使用如下命令:
- -conf spark.memory.offHeap.enabled = true
- -conf spark.memory.offHeap.size = Xgb
当使用堆存储时需要小心,不会影响堆内存大小,如你不能减少堆内存大小. 所以定义整体内存限制,分配一个小的堆内存.
内嵌 vs 用户自定义函数(UDFs)
如果你使用Spark SQL, 尽可能使用内建函数,而不是udfs. 大多数UDFs可以在不安全的行工作,并且不需要转换为包裹数据类型.这样避免了创建GC,很好的生成代码.
要吝啬创建对象
记住我们可能操作数亿行数据. 如果创建一个临时的对象,每行只用 100 byte大小, 这样会创建1百亿*100 byte的垃圾.
总结
到目前为至,只是专注内存管理,数据倾斜,垃圾回收做为引起spark应用程序变慢或失败.