Scala | Spark性能优化 | 资源调优 | 代码调优 | 数据本地化 | 内存调优 | Spark Shuffle调优 | 数据倾斜 | Spark故障解决



一、资源调优

  下面了解在standalone模型下的资源调优,最大就最好。

  1. 在部署 spark 集群中指定资源分配的默认参数
    spark 安装包的 conf spark-env.sh

    SPARK_WORKER_CORES
    SPARK_WORKER_MEMORY
    SPARK_WORKER_INSTANCES 每台机器启动 worker 数
    
  2. 在提交 Application 的时候给当前的 Application 分配更多的资源
    提交命令选项:(在提交 Application 的时候使用选项)

    --executor-cores
    --executor-memory
    --total-executor-cores
    

    配置信息:(Application 的代码中设置或在 Spark-default.conf 中设置)

    spark.executor.cores
    spark. executor.memory
    spark.max.cores
    
  3. 动态分配资源

    spark.shuffle.service.enabled true //启用 External shuffle Service 服务
    spark.shuffle.service.port 7337 //Shuffle Service 服务端口,必须和yarn-site 中的一致
    spark.dynamicAllocation.enabled true //开启动态资源分配
    spark.dynamicAllocation.minExecutors 1 //每个Application最小分配的executor数
    spark.dynamicAllocation.maxExecutors 30 //每个Application最大并发分配的executor数
    spark.dynamicAllocation.schedulerBacklogTimeout 1s
    spark.dynamicAllocation.sustainedSchedulerBacklogTimeout 5s
    

yarn命令:

/opt/lzj/sparkhome/bin/spark-submit \
--class 主类 \
-master yarn-cluster \
-num-executors 100 \
-executor-cores 4 \
-driver-cores 4 \
-driver-memory 6g \
-executor-memory 8d \
-quene root. default \
--conf 
jar包路径
[主类运行所需参数]\

二、并行度调优

  并行度的合理调整,可以降低资源浪费,提高spark任务的运行效率。task的数量应该设置为sparkCPU cores(总核数)的2-3倍。并行度设置的是task的并行度。

  1. 如果读取的数据在 HDFS 中,降低 block 大小,相当于提高了 RDDpartition 个数 sc.textFile(xx,numPartitions)

  2. sc.parallelize(xxx, numPartitions)

  3. sc.makeRDD(xxx, numPartitions)

  4. sc.parallelizePairs(xxx, numPartitions)

  5. repartions/coalesce
    repartions增加分区;coalesce减小分区,一般与filter搭配

  6. redecByKey/groupByKey/join ---(xxx, numPartitions)
    这几个算子都是会产生shuffle的算子。

  7. spark.default.parallelism 500

  8. spark.sql.shuffle.partitions 200
    spark.default.parallelism并行度调节对sparksql无效(即这个并行度只要是没有使用sparkSqlstage中都会生效)。一般我们的操作就是使用repartition去增大sparksql查询出的rdd的分区数。

  9. 自定义分区器

  10. 如果读取数据是在 SparkStreaming
    Receiver: spark.streaming.blockInterval—200ms定义的是max
    Direct:读取的 topic 的分区数

三、代码调优

1.避免创建重复的 RDD

val rdd1 = sc.textFile(path1)
val rdd2 = sc.textFile(path1)

这就是创建了重复的 RDD。有什么问题? 对于执行性能来说没有问题,但是呢,代码乱。

2.对多次使用的 RDD 进行持久化

  如何选择一种最合适的持久化策略
  默认情况下,性能最高的当然是 MEMORY_ONLY,但前提是你的内存必须足够足够大,可以绰绰有余地存放下整个 RDD 的所有数据。因为不进行序列化与反序列化操作,就避免了这部分的性能开销;对这个 RDD 的后续算子操作,都是基于纯内存中的数据的操作,不需要从磁盘文件中读取数据,性能也很高;而且不需要复制一份数据副本,并远程传送到其他节点上。但是这里必须要注意的是,在实际的生产环境中,恐怕能够直接用这种策略的场景还是有限的,如果 RDD 中数据比较多时(比如几十亿),直接用这种持久化级别,会导致 JVMOOM 内存溢出异常。
  如果使用 MEMORY_ONLY 级别时发生了内存溢出,那么建议尝试使用MEMORY_ONLY_SER 级别。该级别会将 RDD 数据序列化后再保存在内存中,此时每个 partition 仅仅是一个字节数组而已,大大减少了对象数量,并降低了内存占用。这种级别比 MEMORY_ONLY 多出来的性能开销,主要就是序列化与反序列化的开销。但是后续算子可以基于纯内存进行操作,因此性能总体还是比较高的。此外,可能发生的问题同上,如果RDD 中的数据量过多的话,还是可能会导致 OOM 内存溢出的异常。
  如果纯内存的级别都无法使用 , 那么建议使用MEMORY_AND_DISK_SER 策略,而不是 MEMORY_AND_DISK 策略。因为既然到了这一步,就说明 RDD 的数据量很大,内存无法完全放下。序列化后的数据比较少,可以节省内存和磁盘的空间开销。同时该策略会优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘。通常不建议使用 DISK_ONLY后缀为_2 的级别:因为完全基于磁盘文件进行数据的读写,会导致性能急剧降低,有时还不如重新计算一次所有RDD后缀为_2 的级别,必须将所有数据都复制一份副本,并发送到其他节点上,数据复制以及网络传输会导致较大的性能开销,除非是要求作业的高可用性,否则不建议使用。

3.持久化算子:

  • cache:
    MEMORY_ONLY
  • persist
    MEMORY_ONLY
    MEMORY_ONLY_SER
    MEMORY_AND_DISK_SER
    一般不要选择带有_2 的持久化级别。
  • checkpoint:
    • 如果一个 RDD 的计算时间比较长或者计算起来比较复杂,一般将这个 RDD 的计算结果保存到 HDFS 上
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spark是指通过Spark应用程序的配置和代码,以提高其性能和效率。而数据倾斜则是指在Spark应用程序中,某些数据分区的数据量远远超过其他分区,导致任务执行时间过长,从而影响整个应用程序的性能。 解决数据倾斜的方法有很多,以下是一些常用的方法: 1. 均衡数据分区:通过对数据进行重新分区,使每个分区的数据量尽可能均衡,从而避免数据倾斜。 2. 使用随机前缀或哈希分桶:对于某些容易导致数据倾斜的操作,如groupByKey或reduceByKey,可以在键值对的键上添加随机前缀或哈希分桶,从而将数据均匀分布到不同的分区中。 3. 使用聚合操作代替join操作:在进行join操作时,如果其中一个表的数据量很大,容易导致数据倾斜。此时可以考虑使用聚合操作代替join操作,如使用reduceByKey或aggregateByKey进行聚合。 4. 使用广播变量:对于一些小数据集,可以将其广播到所有节点上,避免在每个节点上重复加载数据,从而减少数据倾斜。 下面是一个使用随机前缀解决数据倾斜代码示例: ```scala val rdd = sc.parallelize(Seq(("a", 1), ("b", 2), ("c", 3), ("d", 4), ("e", 5), ("f", 6), ("g", 7), ("h", 8), ("i", 9), ("j", 10)), 3) val prefixRdd = rdd.map{case (k, v) => (scala.util.Random.nextInt(3) + "_" + k, v)} val result = prefixRdd.reduceByKey(_ + _).map{case (k, v) => (k.split("_")(1), v)} result.foreach(println) ``` 在上面的代码中,我们使用了scala.util.Random.nextInt(3)生成一个0到2之间的随机数,并将其与键值对的键拼接起来,从而生成一个带有随机前缀的键。然后对带有随机前缀的键进行reduceByKey操作,最后再将随机前缀去掉,得到最终结果。这样可以将数据均匀分布到不同的分区中,避免数据倾斜

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幼稚的人呐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值