day75 saprk 调优


I know, i know
地球另一端有你陪我




一、代码优化


1、对多次使用的RDD进行缓冲

使用 cache,注意持久化策略
MEMORY_ONLY 和 MEMORY_AND_DISK_SER
序列化能够对数据进行压缩,减少数据的占用

2、使用高性能算子

1、reduceBykey 代替 groupBykey
在 map 端进行预聚合
并且 reduceBykey 能够通过修改函数,获得数据中的最大值或最小值
本质类似冒泡排序中一次遍历的相邻依次比较
或者通过返回一对二元组,计算平均值
(无法通过依次比较计算平均值)

2、mapPartiton 代替 map
数据库进行交互的时候一般会用到,会在分区上进行操作,避免创建过多链接
foreachPartition 一般只会在测试时使用到,不会在提交中使用

3、filter 后进行 coalesce 操作
一般过滤后获得的数据都会减少,会使用 coalesce 进行分区的缩减

repartition:coalesce(numPartitions,true)增多分区,有 shuffle
coalesce(numPartitions,false)减少分区,没有 shuffle,只有合并 partition

3、广播变量 broadcast

将数据先拉到 Driver 端,再广播(缓存)到 executor 中,
一般会对绝对小的数据进行广播(100MB)

executor 本身有一个申请到的内存,用于广播的容量默认允许占 60%
如给予的内存为 1G,能够用于广播的只用 600M

4、Kryo优化序列化性能

spark 三个地方涉及到序列化

1、算子里面用到外部变量
2、RDD 类型为自定义类型,使用 checkpoint 或使用 shuffle 类算子时会产生序列化
3、cache 缓存策略中包含 SER

Kryo序列化器介绍:
Spark支持使用Kryo序列化机制。Kryo序列化机制,比默认的Java序列化机制,速度要快 ,序列化后的数据要更小,大概是Java序列化机制的1/10。所以Kryo序列化优化以后,可 以让网络传输的数据变少;在集群中耗费的内存资源大大减少。

对于这三种出现序列化的地方,我们都可以通过使用Kryo序列化类库,来优化序列化和 反序列化的性能。Spark默认使用的是Java的序列化机制,也就是 ObjectOutputStream/ObjectInputStream API来进行序列化和反序列化。但是Spark同 时支持使用Kryo序列化库,Kryo序列化类库的性能比Java序列化类库的性能要高很多。 官方介绍,Kryo序列化机制比Java序列化机制,性能高10倍左右。Spark之所以默认没有 使用Kryo作为序列化类库,是因为Kryo要求最好要注册所有需要进行序列化的自定义类 型,因此对于开发者来说,这种方式比较麻烦

5、数据本地性

Application任务执行流程:
在Spark Application提交后,Driver会根据action算子划分成一个个的job,然后对每一 个job划分成一个个的stage,stage内部实际上是由一系列并行计算的task组成的,然后 以TaskSet的形式提交给你TaskScheduler,TaskScheduler在进行分配之前都会计算出 每一个task最优计算位置。Spark的task的分配算法优先将task发布到数据所在的节点上 ,从而达到数据最优计算位置。

数据本地化级别

进程本地化:PROCESS_LOCAL
节点本地化:NODE_LOCA
无本地化:NO_PREF
机架本地化:RACK_LOCAL
跨机架本地化:ANY

在这里插入图片描述


二、参数调优


–num-executors
直接决定任务可以执行多块
1、一般指定50-100比较合适
2、num-executors * executor-core 不能超过 yarn 总的 cpu 数
3、num-executors * executor-memory 不能超过yarn总的内存 一般最多使用一半左右

–executor-memory
每个 executor 分配到的内存大小
一般指定4G 到8G

–executor-cores
每个 executor 中分配的核数量
一般2-4个 保证每一个cpu 的内存分到的内存不低于2G

–driver-memory
driver 中的内存大小
Driver内,用于存储广播变量

–conf spark.default.parallelism=100
rdd shuffle之后reduce的数量

–confi spark.sql.shuffle.partitions=100
spark sql shuffle之后reduce的数量,默认是200

–conf spark.storage.memoryFraction=0.4
持久化可以用的内存

–conf spark.shuffle.memoryFraction0.4
shuffle阶段可以使用的内存

–conf spark.locality.wait=10
task再Executor中执行等待时间

–conf spark.network.timeout=600s
spark 网络链接的超时时间

–conf spark.yarn.executor.memoryOverhead=2048
堆外内存的大小

–conf spark.core.connection.ack.wait.timeout=300
等待连接的时间

–spark.shuffle.file.buffer
该参数用于设置shuffle write task的BufferedOutputStream的buffer缓冲大小。将数据写到磁盘文件之前,会先写入buffer缓冲中,待缓冲写满之后,才会溢写到磁盘

一个参数调优模板
spark-submit
–class com.shujia.Test
–master yarn-client
–num-executors 50
–executor-memory 4G
–executor-cores 2
–driver-memory 2G
–conf spark.storage.memoryFraction=0.6
–conf spark.shuffle.memoryFraction=0.2
–conf spark.locality.wait=10s
–conf spark.shuffle.file.buffer=64k
–conf spark.yarn.executor.memoryOverhead=2048
–conf spark.core.connection.ack.wait.timeout=300
–conf spark.network.timeout=120s \

在这里插入图片描述


三、数据倾斜优化

产生的原因
1、key分布不均
2、产生shuffle


1、双重聚合

1、加前缀进行第一次聚合
2、去掉前缀进行第二次聚合

package day75

import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

import scala.util.Random

object DoubleReduce {

  /**
    * 双重聚合
    * 一般适用于  业务不复杂的情况
    *
    */
  def main(args: Array[String]): Unit = {
    val conf: SparkConf 
    		= new SparkConf().setMaster("local").setAppName("app")
    val sc: SparkContext = new SparkContext(conf)
    val lines: RDD[String] = sc.textFile("data/word")

    val wordRDD: RDD[String] = lines
      .flatMap(_.split(","))
      .filter(!_.equals(""))

    // 对每一个key打上随机5以内前缀
    wordRDD.map(word => {
      val pix: Int = Random.nextInt(5)
      (pix + "-" + word, 1)
    })

      .groupByKey() //第一次聚合

      .map(t => (t._1, t._2.toList.sum))

      .map(t => {
        ///去掉随机前缀
        (t._1.split("-")(1), t._2)
      })

      .groupByKey() //第二次聚合

      .map(t => (t._1, t._2.toList.sum))
      .foreach(println)

    while (true) {

    }
  }
}

2、将 reduce join 转为 map join

利用广播变量,在 map 阶段完成 join,从而避免 shuffle
只适合大表关联一个绝对小的表(小于1G)

3、双重 join

将数据倾斜的 key 拆分出来
在这里插入图片描述

package day75

import org.apache.spark.broadcast.Broadcast
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}

object DoubleJoin {
  def main(args: Array[String]): Unit = {

    val conf: SparkConf = new SparkConf().setAppName("app").setMaster("local")
    val sc = new SparkContext(conf)
    val dataList1 = List(
      ("java", 1),
      ("shujia", 2),
      ("shujia", 3),
      ("shujia", 1),
      ("shujia", 1))

    val dataList2 = List(
      ("java", 100),
      ("java", 99),
      ("shujia", 88),
      ("shujia", 66))

    val RDD1: RDD[(String, Int)] = sc.parallelize(dataList1)
    val RDD2: RDD[(String, Int)] = sc.parallelize(dataList2)


    //采样倾斜的key
    val sampleRDD: RDD[(String, Int)] = RDD1.sample(false, 1.0)

    //skewedKey  导致数据倾斜的key   shujia
    val skewedKey: String = sampleRDD.map(x => (x._1, 1))
      .reduceByKey(_ + _)
      .map(x => (x._2, x._1))
      .sortByKey(ascending = false)
      .take(1)(0)._2


    //导致数据倾斜key的RDD
    val skewedRDD1: RDD[(String, Int)] = RDD1.filter(tuple => {
      tuple._1.equals(skewedKey)
    })

    //没有倾斜的key
    val commonRDD1: RDD[(String, Int)] = RDD1.filter(tuple => {
      !tuple._1.equals(skewedKey)
    })

    val skewedRDD2: RDD[(String, Int)] = RDD2.filter(tuple => {
      tuple._1.equals(skewedKey)
    })

    val commonRDD2: RDD[(String, Int)] = RDD2.filter(tuple => {
      !tuple._1.equals(skewedKey)
    })

    val n = 2

    //对产生数据倾斜的key 使用mapjoin

    val skewedMap: Map[String, Int] = skewedRDD2.collect().toMap

    val bro: Broadcast[Map[String, Int]] = sc.broadcast(skewedMap)

    val resultRDD1: RDD[(String, (Int, Int))] = skewedRDD1.map(kv => {
      val word: String = kv._1

      val i: Int = bro.value.getOrElse(word, 0)
      (word, (kv._2, i))
    })


    //没有数据倾斜的RDD  正常join
    val resultRDD2: RDD[(String, (Int, Int))] = commonRDD1.join(commonRDD2)

    //将两个结果拼接
    resultRDD1.union(resultRDD2)
      .foreach(println)

  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值