Spark 程序优化建议

1 RDD 缓存

image_1ddn6q6gpo121btsit718k8ode9.png-68.6kB

Persist 到内存的 RDD,比较多,9T左右,Excutor 一共分配了25T内存。剩下给 Shuffle 的空间不算大了,所以会引起频繁的 GC。

建议:

  1. 减少缓存级别,可以适当持久化到磁盘的方式保存 RDD。
  2. 采用 Kryo 的序列化方式,减少所需要的内存空间

合理的 Cache 会带来性能的飞跃…

image_1ddn84hkgcb81edklqtaod9cv13.png-205.5kB

5T纯内存操作,非常快。

image_1ddn8aj60rc4ff1eme1rvo1v5s2g.png-61.3kB

RDD cache 的原则

  1. Reusing them in an iterative loop (ie. ML algos)
  2. Reuse the RDD multiple times in a single application, job, or notebook.
  3. When the upfront cost to regenerate the RDD partitions is costly (ie. HDFS, after a complex set of map(), filter(), etc.) This helps in the recovery process if a Worker node dies.

2 并行度

image_1ddn79tjo1b0joei18si15gi1mp9m.png-180.9kB

部分算子使用了 map,想对而言,map 是针对 RDD 的每个元素调用一次方法,mapPartion 则是每个 partition 调用一次,效率会更高。

image_1ddn9ik0n1u4d1papr8c1a5fsph4a.png-160.6kB

// 不高效的方法
val firstLayer = nodes.leftOuterJoin(edges, partitioner)
  .map {
    case (u, (pid, dataitr)) => {
      (pid, (u, dataitr.getOrElse(-1L)))
    }
  }

// 高效的方法,自定义迭代器
class ChangeMeIterator(iter: Iterator[(Long, (Int, Option[Long]))]) extends Iterator[(Long, (Int,
  Long))] {
  def hasNext: Boolean = {
    iter.hasNext
  }

  def next(): (Long, (Int, Long)) = {
    val cur = iter.next()
    (cur._1, (cur._2._1, cur._2._2.getOrElse(-1L)))
  }
}

val firstLayer = nodes.leftOuterJoin(edges, partitioner).mapPartitions(
  v => new ChangeMeIterator(v)
)

另外还有一种采用 xxxPartion 但是相对没那么高效的情况。

val firstLayer = nodes.leftOuterJoin(edges, partitioner)
  .map { case (u, (pid, dataitr)) => (pid, (u, dataitr.getOrElse(-1L))) }
  .partitionBy(new HashPartitioner(numSubsets))
  .mapPartitionsWithIndex { case (pid, dataitr) =>
    // TODO: 内部维护了一个数据结构,不是很高效的做法
    // 如果采用自定义迭代器的话,是不需要这个数据结构b
    val sourceNodes = mutable.HashSet.empty[Long]
    val data = dataitr.toArray.map(_._2)

    data.foreach { case (u, v) => if (!sourceNodes.contains(u)) sourceNodes.add(u) }

    val tmpEdges = data.filter(x => x._2 != -1L)

    if (tmpEdges.nonEmpty) {
      this.MetisLocal(tmpEdges, numParts, relativePathPrefix)
        .filter(x => sourceNodes.contains(x._1)).map { case (u, i) => (u, (pid, i)) }.toIterator
    } else Iterator.empty
  }.partitionBy(partitioner)
  .persist(StorageLevel.MEMORY_AND_DISK).setName("firstLayer")

image_1ddnbt7noqdoilmss1dc148s4n.png-200kB

3 Jobs/Stages 太多

image_1ddni2vbfk2uhu11c891og81hki6h.png-159.1kB

虽然 RDD 都有缓存,相对来说 transformation 都是内存操作,但是对于程序本身的没有太大意义的,设计 shuffle 的操作,建议还是通过调试部分数据后,在正式环境,或者定时任务中去掉,这样可以减少任务运行的时间。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值