分享

压榨Spark程序性能的八大原则

本帖最后由 regan 于 2019-11-21 10:01 编辑


编写Spark程序需要注意哪些基本的原则?
遇到性能瓶颈如何优化?



1.        准则一:从同一个数据源尽量只创建一个RDD,后续不同的业务逻辑可以复用该RDD,而不是基于该数据源重新创建一个新的RDD
2.        准则二:如果需要对某个RDD进行多次不同的transformation和action操作以应用于不同的业务分析需求,可以考虑对该RDD进行持久化操作,以避免action操作触发作业时多次重复计算该RDD
3.        准则三:从数据源读取数据获得RDD后,要尽早进行filter过滤掉不需要的数据
4.        准则四:尽量避免使用需要shuffle的算子,且在必须shuffle时尽量减小shuffle的数据量
        以ReduceByKey/AggregateByKey与groupByKey的对比来说明map-side预聚合的重要性。ReduceByKey/aggregateByKey算子会使用用户自定义的函数对每个节点本地的含有相同key的记录进行预聚合。而groupByKey算子是不会进行预聚合的,全部的数据都会在集群的各个节点之间分发和传输,性能相对来说肯定比较差,其过程如下图所示。
5.        准则五:熟悉各个算子的背后机制,选择使用高性能的算子。
        可以使用mapPartitions替代普通map.因为mapPartitions类的算子,一次函数调用会处理一个partition所有的数据,而不是一次函数调用处理一条,所有性能相对来说会高一些。但是有的时候,使用mapPartitions会出现OOM(内存溢出)的问题。因为单次函数调用就要处理掉一个partition所有的数据,如果内存不够,垃圾回收时是无法回收掉太多对象的,很可能出现OOM异常。
6.        准则六:对大变量考虑使用广播机制
        在算子函数中使用到外部变量时,默认情况下,Spark会将该变量复制多个副本,通过网络传输到task中,此时每个task都有一个变量副本。如果变量本身比较大的话(比如100M,甚至1G),那么大量的变量副本在网络中传输的性能开销,以及在各个节点的Executor中占用过多内存导致的频繁GC,都会极大地影响性能。
因此对于上述情况,如果使用的外部变量比较大,我们建议使用Spark的广播功能,对该变量进行广播。广播后的变量,会保证每个Executor的内存中,只驻留一份变量副本,而Executor中的task执行时共享该Executor中的那份变量副本。这样的话,可以大大减少变量副本的数量,从而减少网络传输的性能开销,并减少对Executor内存的占用开销,降低GC的频率。
7.        准则七:尽可能使用Kryo优化序列化性能
8.        准则八:使用优化的数据结构
        Java语言的特性导致了有些数据结构会占用额外的空间,在Java中有三种类型的数据结构比较耗费内存:
Java的对象:每个Java对象都有对象头,对象头占用额外的16个字节(包含指向对象的指针等元数据信息),如果对象中只有一个int类型的变量,则此时会占据20个字节,也就是说对象的元数据占用了大部分的空间,所以在封装数据的时候尽量不要使用对象,可以使用JSON格式来封装数据。需要注意,Java中基本的数据类型会自动进行封箱操作,例如int会自动变成Integer,这也会额外增加对象头的空间占用;
        Java的字符串:每个字符串内部都有一个字符数组以及长度等额外信息,在实际占用内存方面要额外使用40个字节(每个字符串内部都使用字符数组来保存字符序列)。由于字符串中每个字符占用2个字节(UTF-16编码),所以如果字符串内部有5个字符的话,实际上会占用50个字节;
        Java中的集合类型比如HashMap、List等:集合的内部一般使用链表来实现,具体的每个数据则使用Entry等,这些也非常消耗内存。
        所以Spark官方建议我们,在Spark编码实现中,特别是对于算子函数中的代码,尽量使用字符串替代对象,使用原始类型(比如Int、Long)替代字符串,使用原生数组替代集合类型,这样尽可能地减少内存占用,从而降低GC频率,提升性能。例如说List<Integer> list = new ArrayList<Integer>,需要考虑改为使用int[] arrary = new int[]。
        如果内存少于32GB,可以在spark-env.sh中设置JVM参数-XX:+UseCompressedOops,以便使用4字节指针而不是8字节指针。与此同时,在Java 7或者更高版本,设置JVM参数-XX:+UseCompressedStrings以便采用8bit来编码每一个ASCII字符。




欢迎关注笔者微信公众号“三角兽”,了解更多数学、算法、大数据干货文章。
qrcode_for_gh_1f93dbc1c492_258.jpg

本帖被以下淘专辑推荐:

没找到任何评论,期待你打破沉寂

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关闭

推荐上一条 /2 下一条