Spark是一种强烈依赖内存的计算框架,结合其运行流程,可以有很多可以调优的地方

用reduceByKey 替代groupByKey

这两个转换都有shuffle过程发生,且都类似map reduce,但是reduceByKey会在map阶段会对相同的key进行聚合,极大的减少了map产生的数据量,进而减少了shuffle的数据量,提高了程序的执行效率
groupByBey
reduceByKey

避免shuffle

shuffle类算子会将多个节点的key,拉取到一个节点上,进行join或者聚合操作,代价很大。

关于缓存

如果一个rdd被多个rdd依赖,就要持久化该rdd,避免被生成多次,而持久化时又有一些小技巧,如下

用persist(MEMORY_ONLY_SER) 代替persist和cache

persist的默认持久化类型是 MEMORY_ONLY ,该类型在内存中会缓存原始的对象,而 MEMORY_ONLY_SER 将对象序列化后再缓存,这样会节省大量的内存。因为Spark模型的各个阶段都会耗内存,而且现在计算的瓶颈一般不在CPU而在IO上,节省了内存。会让Spark其他阶段拥有更多的内存,从而减少了和磁盘的交互,进而加快作业的执行速度

内存不够时

内存不够时,使用 MEMORY_AND_DISK_SER

避免使用DISK_ONLY和后缀为_2的持久化方式

DISK_ONLY将rdd缓存在磁盘上,基于磁盘的读写会严重影响性能
后缀为_2的持久化方式,会将rdd复制一份副本,发送到其他节点上,数据复制和网络传输的性能开销较大

使用Kryo序列化

该种序列化方式会比默认的java序列化方式节省2到5倍的空间

分解一个job之中的transformation

尽量避免在一个transformation中处理所有的逻辑, 尽量分解成map, filter等之类的操作,这样程序会更容易理解

包冲突

  1. 将spark自带的包设置成provided,这样就可以使用spark内核自带的相应类
  2. spark自带了很多包,比如org.json4s, guava等,因为这些包升级时没做向前兼容性,如果用户自己引入了这些包,很可能产生运行时异常。这个时候,有两种方式处理:不使用这些包,这样就将雷跳过去了;使用shade打包,改变包的名字,也可以将雷跳过去

参考

Spark性能优化指南——基础篇
Spark性能优化指南——高级篇
Spark Tuning