Spark调优

本文详细介绍了Spark性能调优的方法,包括常规性能调优、JVM调优、shuffle调优和算子调优等方面的内容。重点讲解了如何通过调整资源分配、并行度、RDD架构与缓存等方式来优化Spark应用的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

性能调优分类

  1. 常规性能调优:分配资源、并行度等等方式。

  2. JVM 调优(Java虚拟机):JVM相关的参数。通常情况下,如果你的硬件配置、基础的 JVM 的配置都 ok 的话,JVM 通常不会造成太严重的性能问题,反而更多的是在 troubleshooting 中 JVM 占了很重要的地位, JVM 造成线上的 spark 作业的运行报错,甚至失败(比如OOM)。

  3. shuffle 调优(相当重要,但1.2之后默认SortShuffleManager,基本不用调):spark 在执行 groupByKey、reduceByKey 等操作时的 shuffle 环节的调优,这个很重要。shuffle 调优其实对 spark 作业的性能的影响是相当之高!经验总结:在 spark 作业的运行过程中只要一牵扯到有 shuffle 的操作,基本上 shuffle 操作的性能消耗要占到整个 spark 作业的 50%~90%。

  4. spark 操作调优(spark算子调优,比较重要):原来使用groupByKey进行的操作现在使用 countByKey 或 aggregateByKey 来重构实现,用foreachPartition替代foreach。有些算子的性能是比其他一些算子的性能要高的。如果一旦遇到合适的情况,效果还是不错的。

调优顺序最好依照:

  1. 分配资源、并行度、RDD架构与缓存;
  2. shuffle调优;
  3. spark算子调优;


----------------------     常规性能调优     ----------------------


******     性能调优之在实际项目中分配更多资源     ******
http://blog.youkuaiyun.com/lxhandlbb/article/details/52390317


spark-submit \
--class cn.spark.sparktest.WordcountCluster \
--num-executors 30 \
--driver-memory 100m \
--executor-memory 100m \   \\增加内存,可以降低序列化的必要性;减少jvm的gc
--executor-cores 6 \       \\并行度为30*2=60,即同时执行的task数量,也即core的总数
/usr/local/sparkTest-0.0.1-SNAPSHOT-jar-with-dependecies.jar


第一种standalone 模式,每次执行一个spark任务,那么执行任务时最大化资源占用:
总的资源为:20台机器,每台机器4G内存,2个core
--num-executors 20 \
--executor-memory 4G \
--executor-cores 2 


第二种yarn模式,




******     性能调优之在实际项目中调节并行度     ******


spark作业拆分:每个action操作产生一个job;job被划分为多个stage;stage中有多个task并行执行
https://wongxingjun.github.io/2015/05/25/Spark%E4%BD%9C%E4%B8%9A%E8%B0%83%E5%BA%A6%E4%B8%ADstage%E7%9A%84%E5%88%92%E5%88%86/


官方推荐task数量==num-executors*executor-cores的2到3倍。原因是task执行速度不同,
SparkConf conf = new SparkConf().set("spark.default.parallelism",500)
每一个stage都能分出500个task并行执行






******     性能调优之在实际项目中重构RDD架构以及RDD持久化     ******
重构:尽量少生成新的RDD
持久化:对被重复使用的RDD进行持久化。持久化后,当被再次请求使用时,BlockManager提供持久化后的RDD,而不用重复计算。
持久化能够被序列化。


RDD cache()  VS  RDD checkpoint()
https://vimsky.com/article/949.html


问题1:哪些 RDD 需要 checkpoint?
运算时间很长或运算量太大才能得到的 RDD,computing chain 过长或依赖其他 RDD 很多的 RDD。 实际上,将 ShuffleMapTask 的输出结果存放到本地磁盘也算是 checkpoint,只不过这个 checkpoint 的主要目的是去 partition 输出数据。


问题2:什么时候 checkpoint?
cache 机制是每计算出一个要 cache 的 partition 就直接将其 cache 到内存了。但 checkpoint 没有使用这种第一次计算得到就存储的方法,而是等到 job 结束后另外启动专门的 job 去完成 checkpoint 。也就是说需要 checkpoint 的 RDD 会被计算两次。因此,在使用 rdd.checkpoint() 的时候,建议加上 rdd.cache(),这样第二次运行的 job 就不用再去计算该 rdd 了,直接读取 cache 写磁盘。其实 Spark 提供了 rdd.persist(StorageLevel.DISK_ONLY) 这样的方法,相当于 cache 到磁盘上,这样可以做到 rdd 第一次被计算得到时就存储到磁盘上,




******     性能调优之在实际项目中广播大变量     ******


当RDD操作中使用一个大的外部变量时:
默认情况下,为每个task copy一个副本变量
使用广播机制,为每个节点拷贝一份副本变量




******     性能调优之在实际项目中使用Kryo序列化     ******
https://my.oschina.net/rosetta/blog/777842
Kryo序列化机制,比默认的Java序列化机制,速度要快,序列化后的数据要更小,大概是Java序列化机制的1/10。所以Kryo序列化优化以后,可以让网络传输的数据变少;在集群中耗费的内存资源大大减少。


实际使用中(1)在sparkconf中设置spark.serializer属性(2)注册自定义类,例如二次排序key
SparkConf conf = new SparkConf().set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
......
.registerKryoClasses(new Class[]{
CategorySortKey.class,
IntList.class}); 




******     性能调优之在实际项目中使用fastutil优化数据格式    ******
http://blog.youkuaiyun.com/hutao_hadoop/article/details/52694435


fastutil是扩展了Java标准集合框架(Map、List、Set;HashMap、ArrayList、HashSet)的类库,
fastutil能够提供更小的内存占用,更快的存取速度;


如果算子函数使用了外部变量;那么第一,你可以使用Broadcast广播变量优化;第二,可以使用Kryo序列化类库,提升序列化性能和效率;第三,如果外部变量是某种比较大的集合,那么可以考虑使用fastutil改写外部变量,首先从源头上就减少内存的占用,通过广播变量进一步减少内存占用,再通过Kryo序列化类库进一步减少内存占用。




******     性能调优之在实际项目中调节数据本地化等待时长     ******
https://andone1cc.github.io/2017/03/12/Spark/%E6%95%B0%E6%8D%AE%E6%9C%AC%E5%9C%B0%E6%80%A7/


数据本地化级别,spark默认从高到低采用数据本地化级别
1、PROCESS_LOCAL:数据和计算它的代码在同一个JVM进程中。
2、NODE_LOCAL:数据和计算它的代码在一个节点上,但是不在一个进程中,比如在不同的executor进程中,或者是数据在HDFS文件的block中。
3、NO_PREF:数据从哪里过来,性能都是一样的。
4、RACK_LOCAL:数据和计算它的代码在一个机架上。
5、ANY:数据可能在任意地方,比如其他网络环境内,或者其他机架上。


Spark默认会等待一会儿,期望task要处理的数据所在的节点上的executor空闲出一个cpu,从而将task分配过去。如果超时,Spark就会将task分配到其他任意一个空闲的executor上。
可以设置参数,spark.locality系列参数,来调节Spark等待task可以进行数据本地化的时间。spark.locality.wait(3000毫秒)、spark.locality.wait.node、spark.locality.wait.process、spark.locality.wait.rack。




----------------------     JVM调优     ----------------------
先看spark 内存管理
https://www.ibm.com/developerworks/cn/analytics/library/ba-cn-apache-spark-memory-management/index.html




******      JVM调优之原理概述以及降低cache操作的内存占比      ******
http://blog.youkuaiyun.com/u012318074/article/details/70740022


内存不充足的时候会导致的问题: 
1. 频繁minor gc 也会导致频繁spark停止工作; 
2. 老年代囤积大量活跃对象(短生命周期的对象)导致频繁full gc,full gc时间很长,短则数十秒,长则数分钟,甚至数小时,可能导致 spark 长时间停止工作。 


spark 中堆内存被划分成两块,一块儿是专门用来给 RDD 的cache、persist 操作进行 RDD 数据缓存用的;另外一块儿是用来给 spark 算子函数的运行使用的,用来存放函数中自己创建的对象。


.set("spark.storage.memoryFraction", "0.6")


******      JVM调优之调节executor堆外内存与连接等待时长      ******
https://www.2cto.com/net/201612/574859.html


******      JVM调优:增加off heap内存上限        ******
什么时候需要调节Executor的堆外内存大小?
当出现一下异常时:
shuffle file cannot find,executor lost、task lost,out of memory


出现这种问题的现象大致有这么两种情况:
(1)Executor挂掉了,对应的Executor上面的block manager也挂掉了,找不到对应的shuffle map output文件,Reducer端不能够拉取数据
(2)Executor并没有挂掉,而是在拉取数据的过程出现了问题。


默认情况下,off heap内存上限默认是executor的内存大小的10%;真正处理大数据的时候,这里都会出现问题,导致spark作业反复崩溃,无法运行;此时就会去调节这个参数,至少1G(1024M),甚至说2G、4G,通常这个参数调节上去以后,就会避免掉某些JVM OOM的异常问题,同时呢,会让整体spark作业的性能,得到较大的提升。


--conf spark.driver.memoryOverhead=1G


******      JVM调优:增加连接时长     ******
executor,优先从自己本地关联的BlockManager中获取某份数据。如果本地BlockManager没有,会通过TransferService,去远程连接其他节点上executor
的BlockManager去获取,尝试建立远程的网络连接,并且去拉取数据。
task创建的对象特别大,特别多频繁的让JVM堆内存满溢,进行垃圾回收。正好碰到那个exeuctor的JVM在垃圾回收。导致暂时连接不上。


--conf spark.core.connection.ack.wait.timeout=300




----------------------     shuffle调优     ----------------------


******      Shuffle调优之原理概述      ******


spark DAGScheduler 根据“宽依赖”和“窄依赖”划分stage。
http://blog.youkuaiyun.com/houmou/article/details/52531205
narrow就是独生子女,一个父亲只有一个孩子;wide是一个父亲多个孩子,此时需要shuffle,即父亲RDD的每个partition需要为孩子RDD的每个partition准备一个输出。


shuffle原理
http://sharkdtu.com/posts/spark-shuffle.html


*** 合并map端输出文件
http://blog.youkuaiyun.com/lxhandlbb/article/details/78880559
shuffle的map中间文件数量惊人,如下
map中间文件数量==num_map * num_reduce
SparkSession.builder().enableHiveSupport().config("spark.shuffle.consolidateFiles","true")




******      map端内存缓冲和reduce端内存缓冲占比      ******      


看Spark UI,如果你的公司是决定采用standalone模式,那么很简单,你的spark跑起来,
会显示一个Spark UI的地址,4040的端口,进去看,依次点击进去,可以看到,你的每个stage的详情,
有哪些executor,有哪些task,每个task的shuffle write和shuffle read的量,shuffle的磁盘和内存,
读写的数据量;如果是用的yarn模式来提交,课程最前面,从yarn的界面进去,点击对应的application,
进入Spark UI,查看详情。


如果发现shuffle 磁盘的write和read,很大。这个时候,就意味着最好调节一些shuffle的参数。
进行调优。首先当然是考虑开启map端输出文件合并机制。


调节上面说的那两个参数(spark.shuffle.file.buffer和spark.shuffle.memoryFraction)。调节原则:spark.shuffle.file.buffer,每次扩大一倍,
然后看看效果,64,128;spark.shuffle.memoryFraction,每次提高0.1,看看效果。




******      shuffle调优之HashShuffleManager与SortShuffleManager(Spark 1.2之后的默认ShuffleManager)      ******      


http://blog.youkuaiyun.com/lxhandlbb/article/details/78880649
SortShuffleManager与HashShuffleManager两点不同:
1、SortShuffleManager会对每个reduce 
task要处理的数据,进行排序(默认的)。 
2、SortShuffleManager会避免像HashShuffleManager那样,默认就去创建多份磁盘文件。一个task,只会写入一个磁盘文件,不同reduce 
task的数据,用offset来划分界定。之前讲解的一些调优的点,比如consolidateFiles机制、map端缓冲、reduce端内存占比。 


200以下不sort,好处是:避免sort排序,减少性能开销
spark.shuffle.sort.bypassMergeThreshold 200




----------------------     算子调优     ----------------------


******      算子调优之MapPartitions提升Map类操作性能      ******


优点:一次处理一个分区,执行效率高
缺点:内存占用多,导致OOM。相比而言,map函数逐条处理数据,当内存不足时,spark会对已经处理完的数据进行垃圾回收,内存占用少。
适用:数据量不太大,


actionRDD.mapToPair(new PairFunction<Row,String,Row>(){


@Override
public Tuple2<String, Row> call(Row t) throws Exception {
// TODO Auto-generated method stub
return null;
}

});


actionRDD.mapPartitionsToPair(new PairFlatMapFunction<Iterator<Row>,String,Row>(){


private static final long serialVersionUID = 1L;


@Override
public Iterable<Tuple2<String, Row>> call(Iterator<Row> t) throws Exception {
// TODO Auto-generated method stub
return null;
}

});




******      算子调优之filter过后使用coalesce减少分区数量      ******
经过filter后,可能出现两个问题
(1)partition的数据减少了很多
(2)有的分区中RDD数量很大,有的分区中RDD数量很小
coalesce(numPartitions) Decrease the number of partitions in the RDD to numPartitions. Useful for running operations more efficiently after filtering down a large dataset.




******      算子调优之使用foreachPartition优化写数据库性能      ******
extractSessionDetailRDD.foreach(new VoidFunction<Tuple2<String,Tuple2<String,Row>>>(){


@Override
public void call(Tuple2<String, Tuple2<String, Row>> t) throws Exception {
// TODO Auto-generated method stub

}

});


extractSessionDetailRDD.foreachPartition(new VoidFunction<Iterator<Tuple2<String,Tuple2<String,Row>>>(){


@Override
public void call(Iterator<Tuple2<String, Tuple2<String, Row>>> t) throws Exception {
// TODO Auto-generated method stub
若有写数据库操作,可以使用insertBatch提高效率

}

});
 


******      算子调优之使用repartition解决Spark SQL低并行度的性能问题      ******


http://www.yang-yao.com/archives/244
Spark SQL的stage的并行度默认根据hive表对应的hdfs文件的block,自动设置Spark SQL查询所在的那个stage的并行度。
spark.default.parallelism参数指定的并行度,只会在没有Spark SQL的stage中生效。


repartition(numPartitions) 算子函数
Reshuffle the data in the RDD randomly to create either more or fewer partitions and balance it across them. This always shuffles all data over the network.




******      算子调优之reduceByKey本地聚合介绍      ******


内容概要:本文档详细介绍了Analog Devices公司生产的AD8436真均方根-直流(RMS-to-DC)转换器的技术细节及其应用场景。AD8436由三个独立模块构成:轨到轨FET输入放大器、高动态范围均方根计算内核和精密轨到轨输出放大器。该器件不仅体积小巧、功耗低,而且具有广泛的输入电压范围和快速响应特性。文档涵盖了AD8436的工作原理、配置选项、外部组件选择(如电容)、增益节、单电源供电、电流互感器配置、接地故障检测、三相电源监测等方面的内容。此外,还特别强了PCB设计注意事项和误差源分析,旨在帮助工程师更好地理解和应用这款高性能的RMS-DC转换器。 适合人群:从事模拟电路设计的专业工程师和技术人员,尤其是那些需要精确测量交流电信号均方根值的应用开发者。 使用场景及目标:①用于工业自动化、医疗设备、电力监控等领域,实现对交流电压或电流的精准测量;②适用于手持式数字万用表及其他便携式仪器仪表,提供高效的单电源解决方案;③在电流互感器配置中,用于检测微小的电流变化,保障电气安全;④应用于三相电力系统监控,化建立时间和转换精度。 其他说明:为了确保最佳性能,文档推荐使用高质量的电容器件,并给出了详细的PCB布局指导。同时提醒用户关注电介质吸收和泄漏电流等因素对测量准确性的影响。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值