1 spark 参数调优
具体的参数在spark官网上都有描述。本文从项目调优的经验总结对性能影响比较大的几个参数。
1.1 Executor内存和Executor 核数
由产品规格决定,没什么好优化的
1.2 序列化
强烈建议使用kryoserializer
1.3 spark.storage.memoryFraction
该参数是设置永久带的比例,默认是总虚拟机内存的66%,即留给新生带的内存只有34%。该参数设置建议,根据程序中的广播变量的数据量和缓存的数据量实际调整该比例。否则新生带内存不够会出现full GC,甚至是内存溢出。导致程序无法运行。如果没有大广播对象和缓存,建议将此参数设置尽可能的小。
1.4 spark.default.parallelism
该参数是设置task数,官方建议设置为虚拟核数的2到3倍。它主要是影响单任务执行的数据量,数据分布已经最后文件写磁盘的part数。
设置建议
l 如果数据量大,分配到每个核每次执行的数据量会出现内存溢出,建议将此参数改大。
l 如果出现少数几个核运行的时间特别长,说明此时运行的数据存在数据倾斜。可以通过调节此参数,达到相对均衡,提升性能。
2 程序设计调优
对应spark程序来说,影响性能的最大因素是shuffle.因此在程序设计中一定重点考虑该因素。Shuffle涉及到磁盘读写,网络传输等,这些都是耗时的操作,因此要尽量避免shuffle,无法避免的情况一定要降低shuffle的数据量。
2.1 提前过滤不必要的数据,越早越好。
在实践中,相同环境,LTE定位没有提前过滤不需要的事件块的定位性能4G数据约10分钟,做了提前过滤性能提升到约2分20秒。
2.2 减少发生shuffle的操作,只保留必须的的shuffle操作,不要存在冗余的存在shuffle的操作。
例如:在定位中,开始存在大量的groupby,且key相同。严重影响性能,优化后只存在一个必须的groupby。
2.3 使用累加器替换count操作输出统计信息
2.4 慎重对待重复计算
在程序中,尽量不要出现宽依赖。一旦出现宽依赖就会出现重复计算。解决这种问题,有两种办法:一种是使用缓存避免出现重复计算,另一种是让程序出现重复计算。这两种方法适合不同场景,根据自身特点选择。
使用缓存避免重复计算
l 重复计算部分非常耗时,计算出来的结果数据小。建议使用缓存,避免重复计算,如果内存够大建议直接缓存到内存或者序列化后缓存到内存。
l 重复计算部分非常耗时,计算出来的结果数据较大。建议使用缓存,避免重复计算,如果内存不够大建议直接缓存到磁盘或序列化后缓存到磁盘。
让程序出现重复计算
当重复计算的成本比,数据写磁盘的效率高时,建议重复计算。一般是数据量大,计算速度快,建议按此方法处理。
两种方式的选择根据场景选择,定位中数据解析就是采用使用的让程序出现重复计算,因为实测数据解析的速度远比写磁盘的速度块。
2.5 本地读取数据替换广播
广播的特点是,每个节点都有一份相同的数据。它会在每个节点都占有相同的内存空间。当广播的对象,非常大时,广播的对象很多是,会出现,driver端内存不足,导致程序崩溃。建议
l 小对象,老生待内存够存储的场景,且对象中的每个元素都会在每个节点上使用,建议使用。
例如:定位参数,在定位中使用的广播。
l 大对象,并不是对象中每个元素在每个个几点都被使用的场景,建议把广播对象按固定算法分组,分文件存在。每个节点在计算时加载需要的数据,使用完立即从内存中清除。
例如:定位中的特征库加载。
2.6 本地读取数据替换join
Spark的join操作非常耗时,因为存在shuffle。可以将join的两份数据按固定的算法分组。将较小的数据按分组逐个加载处理,提升性能。该处理方法可以达到广播的效果,且避免的广播的缺点。
例如:定位中的结果回填。
2.7 数据均衡
调节spark的并行度参数spark.default.parallelism无法解决的数据倾斜的问题时。可自行实现简单的数据均衡算法。保证大部分任务的数据量相当即可。
3 数据结构调优
3.1 不要过度封装将多个原生类型封装为一个类
数据结构的选择直接影响数据在内存中的存储空间和shuffle时的数据量。直接影响程序性能。建议尽量使用原生类型,尽量不要使用类把少数几个原始对象包装起来。
例如:定位中的经纬度两个字段,之前是使用一个对象将其封装。一个类头占16个字节,经纬度两个字段使用的double总共是16字节,一包装数据量涨了一倍,当对象数非常多千万甚至上亿级数据量不可小觑。定位模块中,后来将该包装去除,直接使用2个double类型的变量,5分钟粒度的MR,性能提升了一分钟。
3.2 使用数值类型替换字符类型作Key
数值的比较效率高于字符的比较且占用的存储空间低于字符类型
3.3 正确使用数据类型。
Byte 够用不要使用short,short够用不要使用int,int够用不要使用long。依次类推
3.4 建议使用fastutil
减少对象装箱拆箱操作。
3.5 小数元素时(小于128个元素)时建议使用数组加二分查找,不建议使用HashMap。
例如:定位的特征库数据结构,使用数组替换HashMap,性能提升约1分钟
4 其他优化
在代码中,减少比必要的操作,在编程中减少不必要程序执行指令。
具体的参数在spark官网上都有描述。本文从项目调优的经验总结对性能影响比较大的几个参数。
1.1 Executor内存和Executor 核数
由产品规格决定,没什么好优化的
1.2 序列化
强烈建议使用kryoserializer
1.3 spark.storage.memoryFraction
该参数是设置永久带的比例,默认是总虚拟机内存的66%,即留给新生带的内存只有34%。该参数设置建议,根据程序中的广播变量的数据量和缓存的数据量实际调整该比例。否则新生带内存不够会出现full GC,甚至是内存溢出。导致程序无法运行。如果没有大广播对象和缓存,建议将此参数设置尽可能的小。
1.4 spark.default.parallelism
该参数是设置task数,官方建议设置为虚拟核数的2到3倍。它主要是影响单任务执行的数据量,数据分布已经最后文件写磁盘的part数。
设置建议
l 如果数据量大,分配到每个核每次执行的数据量会出现内存溢出,建议将此参数改大。
l 如果出现少数几个核运行的时间特别长,说明此时运行的数据存在数据倾斜。可以通过调节此参数,达到相对均衡,提升性能。
2 程序设计调优
对应spark程序来说,影响性能的最大因素是shuffle.因此在程序设计中一定重点考虑该因素。Shuffle涉及到磁盘读写,网络传输等,这些都是耗时的操作,因此要尽量避免shuffle,无法避免的情况一定要降低shuffle的数据量。
2.1 提前过滤不必要的数据,越早越好。
在实践中,相同环境,LTE定位没有提前过滤不需要的事件块的定位性能4G数据约10分钟,做了提前过滤性能提升到约2分20秒。
2.2 减少发生shuffle的操作,只保留必须的的shuffle操作,不要存在冗余的存在shuffle的操作。
例如:在定位中,开始存在大量的groupby,且key相同。严重影响性能,优化后只存在一个必须的groupby。
2.3 使用累加器替换count操作输出统计信息
2.4 慎重对待重复计算
在程序中,尽量不要出现宽依赖。一旦出现宽依赖就会出现重复计算。解决这种问题,有两种办法:一种是使用缓存避免出现重复计算,另一种是让程序出现重复计算。这两种方法适合不同场景,根据自身特点选择。
使用缓存避免重复计算
l 重复计算部分非常耗时,计算出来的结果数据小。建议使用缓存,避免重复计算,如果内存够大建议直接缓存到内存或者序列化后缓存到内存。
l 重复计算部分非常耗时,计算出来的结果数据较大。建议使用缓存,避免重复计算,如果内存不够大建议直接缓存到磁盘或序列化后缓存到磁盘。
让程序出现重复计算
当重复计算的成本比,数据写磁盘的效率高时,建议重复计算。一般是数据量大,计算速度快,建议按此方法处理。
两种方式的选择根据场景选择,定位中数据解析就是采用使用的让程序出现重复计算,因为实测数据解析的速度远比写磁盘的速度块。
2.5 本地读取数据替换广播
广播的特点是,每个节点都有一份相同的数据。它会在每个节点都占有相同的内存空间。当广播的对象,非常大时,广播的对象很多是,会出现,driver端内存不足,导致程序崩溃。建议
l 小对象,老生待内存够存储的场景,且对象中的每个元素都会在每个节点上使用,建议使用。
例如:定位参数,在定位中使用的广播。
l 大对象,并不是对象中每个元素在每个个几点都被使用的场景,建议把广播对象按固定算法分组,分文件存在。每个节点在计算时加载需要的数据,使用完立即从内存中清除。
例如:定位中的特征库加载。
2.6 本地读取数据替换join
Spark的join操作非常耗时,因为存在shuffle。可以将join的两份数据按固定的算法分组。将较小的数据按分组逐个加载处理,提升性能。该处理方法可以达到广播的效果,且避免的广播的缺点。
例如:定位中的结果回填。
2.7 数据均衡
调节spark的并行度参数spark.default.parallelism无法解决的数据倾斜的问题时。可自行实现简单的数据均衡算法。保证大部分任务的数据量相当即可。
3 数据结构调优
3.1 不要过度封装将多个原生类型封装为一个类
数据结构的选择直接影响数据在内存中的存储空间和shuffle时的数据量。直接影响程序性能。建议尽量使用原生类型,尽量不要使用类把少数几个原始对象包装起来。
例如:定位中的经纬度两个字段,之前是使用一个对象将其封装。一个类头占16个字节,经纬度两个字段使用的double总共是16字节,一包装数据量涨了一倍,当对象数非常多千万甚至上亿级数据量不可小觑。定位模块中,后来将该包装去除,直接使用2个double类型的变量,5分钟粒度的MR,性能提升了一分钟。
3.2 使用数值类型替换字符类型作Key
数值的比较效率高于字符的比较且占用的存储空间低于字符类型
3.3 正确使用数据类型。
Byte 够用不要使用short,short够用不要使用int,int够用不要使用long。依次类推
3.4 建议使用fastutil
减少对象装箱拆箱操作。
3.5 小数元素时(小于128个元素)时建议使用数组加二分查找,不建议使用HashMap。
例如:定位的特征库数据结构,使用数组替换HashMap,性能提升约1分钟
4 其他优化
在代码中,减少比必要的操作,在编程中减少不必要程序执行指令。