文章目录
Spark的瓶颈一般来自于集群(standalone, yarn, mesos, k8s)的资源紧张,CPU,网络带宽,内存。通过都会将数据序列化,降低其内存memory和网络带宽shuffle的消耗。
Spark的性能,想要它快,就得充分利用好系统资源,尤其是内存和CPU:核心思想就是能用内存cache就别spill落磁盘,CPU 能并行就别串行,数据能local就别shuffle。
一、开发调优
1、避免创建重复的RDD
比如多次读可以persist;但如果input太大,persist可能得不偿失
2、尽可能复用用一个RDD
但是如果rdd的lineage太长,最好checkpoint下来,避免长重建
3、对多次使用的RDD进行持久化
使用cache()、persist()、checkpoint()
4、尽量避免使用shuffle类算子
- shuffle算子如distinct(实际调用reduceByKey)、reduceByKey、aggregateByKey、sortByKey、groupByKey、join、cogroup、repartition等,入参中会有一个并行度参数numPartitions
shuffle过程中,各个节点上的相同key都会先写入本地磁盘文件中,然后其他节点需要通过网络传输拉取各个节点上的磁盘文件中的相同key
5、使用map-side预聚合的shuffle操作
- reduceByKey(combiner),groupByKey(没有combiner)
6、使用高性能的算子
- 使用reduceByKey/aggregateByKey替代groupByKey
- 使用mapPartitions替代普通map
特别是在写DB的时候,避免每条写记录都new一个connection;推荐是每个partition new一个connection;更好的是new connection池,每个partition从中取即可,减少partitionNum个new的消耗 - 使用foreachPartitions替代foreach
- 使用filter之后进行coalesce操作
- 减少小文件数量
- 使用repartitionAndSortWithinPartitions替代repartition与sort类操作
一边进行重分区的shuffle操作,一边进行排序
7、广播大变量
- 广播变量是executor内所有task共享的,避免了每个task自己维护一个变量,OOM
8、使用Kryo优化序列化性能
9、优化数据结构
原始类型(Int, Long)
字符串,每个字符串内部都有一个字符数组以及长度等额外信息
对象,每个Java对象都有对象头、引用等额外的信息,因此比较占用内存空间
集合类型,比如HashMap、LinkedList等,因为集合类型内部通常会使用一些内部类来封装集合元素,比如Map.Entry
尽量使用字符串替代对象,使用原始类型(比如Int、Long)替代字符串,使用数组替代集合类型,这样尽可能地减少内存占用,从而降低GC频率,提升性能。
10、对数据进行压缩以及合并小文件
- 通过spark的coalesce()方法和repartition()方法
val rdd2 = rdd1.coalesce(8, true) (true表示是否shuffle)
val rdd3 = rdd1.repartition(8)
说明:
coalesce:coalesce()方法的作用是返回指定一个新的指定分区的Rdd,
如果是生成一个窄依赖的结果,那么可以不发生shuffle,
分区的数量发生激烈的变化,计算节点不足,不设置true可能会出错。
repartition:coalesce()方法shuffle为true的情况。
- 降低spark并行度,即调节spark.sql.shuffle.partitions