Spark RDD JOIN 调优

Spark RDD JOIN 调优

一、 大表关联小表

1. 小表数据量非常小时

  • 一般当副表的数据比较小时,可以考虑将这部分的小表数据直接加载到内存中,如加工成为一个Map 结构的对象,在使用的时候将这个对象广播到各个Executor中。
  • 在广播时需要注意广播的这个数据集的大小,如果太大就会得不偿失,因为广播的实质其实就是把同一份数据集对象复制多份,然后通过序列化的方式将数据集分发到各个Executor中,所以如果集合太大那么会占用过多的网络带宽和内存。
  • 在使用广播变量的时候还需要注意,如果直接在一个map算子中使用广播变量时会导致频繁的去操作广播变量,导致性能问题,所以一般使用广播变量时需要配合着mapPartitions算子使用。
  • 代码示例:
val mainDF = sparkSession.read.parquet("").repartition(128)
    val dimDF = sparkSession.read.parquet("").repartition(128)

    val dimMap = dimDF
      .rdd
      .collect()
      .map(
        row => {
          row.getAs[String]("id") -> row
        }
      )
      .toMap

    val bcDimMap = sparkSession.sparkContext.broadcast(dimMap)

    val mainRDD = mainDF
      .rdd
      .map(
        row => {
          (row.getAs[String]("id"), row)
        }
      )

    val resultRDD= mainRDD
      .mapPartitions(
        items => {
          val dimDict = bcDimMap.value
          items
            .map(
              item => {
                val thatRow = dimDict.get(item._1)
                val thisRow = item._2

                if (thatRow.nonEmpty){
                  // matched
                }else{
                  // not matched
                }

                thisRow
              }
            )
        }
      )

2. 小表数据量比较大时

  • 当副表的数据量比较大的时候,这个时候我们就不能够使用前面那种广播的方式进行关联了
  • 那么比较好的方式是什么呢,就是将两个数据集的关联键以相同的分区策略进行分区,然后再在每个小的分区中进行mapping
  • 代码示例:
    val mainDF = sparkSession.read.parquet("").repartition(128)
    val dimDF = sparkSession.read.parquet("").repartition(128)


    val defPartitioner: Partitioner = new Partitioner {

      val headLen: Int = 3

      override def numPartitions: Int = 256

      override def getPartition(key: Any): Int = {
        if (key != null){
          val keyString = key.toString
          if (keyString.length >= headLen){
            keyString.substring(0, headLen).sum % numPartitions
          } else{
            scala.util.Random.nextInt(numPartitions - 1)
          }
        } else{
          scala.util.Random.nextInt(numPartitions - 1)
        }
      }
    }


    val dimRDD = dimDF
      .rdd
      .map(
        row => {
          row.getAs[String]("id") -> row
        }
      )
      .partitionBy(defPartitioner)


    val mainRDD = mainDF
      .rdd
      .map(
        row => {
          (row.getAs[String]("id"), row)
        }
      )
      .partitionBy(defPartitioner)


    mainRDD
      .zipPartitions(dimRDD){
        (i1, i2) => {
          val dimMap = i2.toMap
          i1.map(
            item => {
              val thatRow = dimMap.get(item._1)
              val thisRow = item._2

              if (thatRow.nonEmpty){
                // matched
              }else{
                // not matched
              }

              thisRow
            }
          )
        }
      }

二、大表关联大表

1.探查数据是否存在倾斜

  • 事先观察数据集是否有数据分布不均匀的情况,如果有分布不均匀的情况可以考虑使用和前面一样的方式去自定义分区策略,打乱数据分区
SELECT 
  field
  ,count(1) AS cnt 
FROM test_table 
GROUP BY field
ORDER BY cnt DESC 
LIMIT 10 

2.分区数量是否合理

  • 通过查看yarn-webUI 的方式查看各个job以及stage中 的task情况,如果task数量太少,不足num-executor * executor-cores 的 2~3 倍,建议通过repartition()的方式将数据集重新划分分区数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值