大数据优化方案----Spark案例优化(一)

China☆狼群 提交于 2020-01-24 02:38:51

“无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家。教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点这里可以跳转到教程。”。

大数据面试宝典目录,请点击

目录

一、需求

二、样例数据

三、实现方式一

四、实现方式二

      自定义分区取
      重写排序规则
      排序

五、实现方式三

     在shuffle是在每一个分区中实现排序
     另一种方式实现,使用ShuffleRDD



一、需求

       通过分析用户浏览新闻热门话题的日志,统计每个话题下被浏览量最多的用户topN,即按照话题分组,在每一个组内进行排序
在这里插入图片描述

二、样例数据

数据格式:话题,时间,被浏览的用户id

#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u011
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#高以翔去世#,2019-11-29,u008
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u017
#网易裁员#,2019-11-29,u014
#网易裁员#,2019-11-29,u014
#陈乔恩宣布恋情#,2019-11-29,u001
#陈乔恩宣布恋情#,2019-11-29,u001
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u002
#陈乔恩宣布恋情#,2019-11-29,u003
#陈乔恩宣布恋情#,2019-11-29,u003

三、实现方式一

       过程:调用groupBy按照话题进行分组,然后将value对应的迭代器toList,将数据全部加载到内存中,然后在调用List的sortBy方法进行排序,然后再调用take取TopN

       分析:分组后,每一个组内的数量比较大,将迭代器toList会造成内存溢出,所以如果分组后组内的数据量比较大,这样的方式不适合。

object HotIssue01 {

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

    val isLocal = args(0).toBoolean
    //创建SparkConf,然后创建SparkContext
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
    if (isLocal) {
      conf.setMaster("local[*]")
    }
    val sc = new SparkContext(conf)
    //指定以后从哪里读取数据创建RDD
    val lines: RDD[String] = sc.textFile(args(1))
    //排序的TopN
    val topN = args(2).toInt
    //对数据进行切分
    val issueUidAndOne = lines.map(line => {
      val fields = line.split(",")
      val issue = fields(0)
      val uid = fields(2)
      ((issue, uid), 1)
    })
    //聚合
    val reduced = issueUidAndOne.reduceByKey(_ + _)
    //分组
    val grouped: RDD[(String, Iterable[((String, String), Int)])] =
      reduced.groupBy(_._1._1)
    //排序,将一个组的数据,全部加载到内存
    // 如果在一些特殊场景下,一个组内的数据过多,可能会出现内存溢出
    val result: RDD[(String, List[((String, String), Int)])] =
      grouped.mapValues(_.toList.sortBy(-_._2).take(topN))
    //触发Action,打印
    val r = result.collect()
    println(r.toBuffer)
    sc.stop()
  }
}

四、实现方式二

      自定义分区器+TreeSet,将要计算的数据依次迭代出入放入TreeSet中排序,并且重新TreeSet的排序规则,需要注意的是并列的问题。

      自定义分区取

class IssuePartitioner(val issues: Array[String]) extends Partitioner {

  //初始化分器的分区规则
  val rules = new mutable.HashMap[String, Int]()
  var index = 0
  for(is <- issues) {
    rules(is) = index
    index += 1
  }

  override def numPartitions: Int = issues.length

  //该方法会在Executor中的Task中被调用
  override def getPartition(key: Any): Int = {
    val tuple = key.asInstanceOf[(String, String)]
    val issue = tuple._1
    //到实现初始化的规则中查找这个话题对应的分区编号
    rules(issue)
  }
}

     重写排序规则

/**
  * 定义封装数据的bean,并实现排序规则,同时考虑并列的问题
  */
class OrderingBean(val subject: String, val name: String, val count: Int)
  extends Ordered[OrderingBean] with Serializable {

  val equiv = new ArrayBuffer[(String, String, Int)]()
  equiv += ((subject, name, count))

  override def compare(that: OrderingBean): Int = {
    if (this.count == that.count) {
      equiv += ((that.subject, that.name, that.count))
      0
    } else {
      -(this.count - that.count)
    }
  }

  override def toString =
    if (equiv.size > 1) {
      equiv.toString()
    } else
      s"($subject, $name, $count)"
}

      排序

object HotIssue02 {
  def main(args: Array[String]): Unit = {
    val isLocal = args(0).toBoolean
    //创建SparkConf,然后创建SparkContext
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
    if (isLocal) {
      conf.setMaster("local[*]")
    }
    val sc = new SparkContext(conf)
    //指定以后从哪里读取数据创建RDD
    val lines: RDD[String] = sc.textFile(args(1))
    //排序的TopN
    val topN = args(2).toInt
    //对数据进行切分
    val issueUidAndOne = lines.map(line => {
      val fields = line.split(",")
      val issue = fields(0)
      val uid = fields(2)
      ((issue, uid), 1)
    })
    //计算所有话题,并收集到Driver端
    val issues: Array[String] = issueUidAndOne.map(_._1._1)
      .distinct()
      .collect()

    val partitioner = new IssuePartitioner(issues)
    //根据指定的key和分区器进行聚合(减少一次shuffle)
    val reduced: RDD[((String, String), Int)] =
      issueUidAndOne.reduceByKey(partitioner, _+_)

    val result = reduced.mapPartitions(it => {
      //用自定义的OrderingBean封装数据
      val sorter = new mutable.TreeSet[OrderingBean]()
      //遍历出迭代器中的数据
      it.foreach(t => {
        //将数据依次迭代出来,方法TreeSet中安装定义好的规则进行排序
        sorter += new OrderingBean(t._1._1, t._1._2, t._2)
        if (sorter.size > topN) {
          val last = sorter.last
          //移除最后一个
          sorter -= last
        }
      })
      sorter.iterator
    })
    val r = result.collect()
    println(r.toBuffer)
    sc.stop()
  }
}

五、实现方式三

     使用reparationAndSortWithParititions方法,
     在shuffle是在每一个分区中实现排序

object HotIssue03 {
  def main(args: Array[String]): Unit = {
    val isLocal = args(0).toBoolean
    //创建SparkConf,然后创建SparkContext
    val conf = new SparkConf().setAppName(this.getClass.getSimpleName)
    if (isLocal) {
      conf.setMaster("local[1]")
    }
    val sc = new SparkContext(conf)
    //指定以后从哪里读取数据创建RDD
    val lines: RDD[String] = sc.textFile(args(1))
    //对数据进行切分
    val issueUidAndOne = lines.map(line => {
      val fields = line.split(",")
      val issue = fields(0)
      val uid = fields(2)
      ((issue, uid), 1)
    })
    //聚合
    val reduced = issueUidAndOne.reduceByKey(_ + _)
    //计算所有的学科,并收集到Driver端
    val issues: Array[String] = reduced.map(_._1._1).distinct().collect()

    val partitioner = new IssuePartitioner(issues)
    //对原来的数据进行整理
    val keyByRDD: RDD[((String, String, Int), Null)] = reduced
      .map(t => ((t._1._1, t._1._2, t._2), null))
    //隐式转换
    implicit val orderRules = new Ordering[(String, String, Int)] {
      override def compare(x: (String, String, Int), y: (String, String, Int)): Int = {
        -(x._3 - y._3)
      }
    }
    val topN = args(2).toInt
    //repartitionAndSortWithinPartitions按照指定的分区器进行排序并且在每个分区内进行排序
    val result: RDD[((String, String, Int), Null)] = keyByRDD
      .repartitionAndSortWithinPartitions(partitioner)
    result.foreachPartition(it => {
      var index = 1
      while (it.hasNext && index <= topN) {
        val tuple = it.next()
        println(tuple)
        index += 1
      }
    })
    sc.stop()
  }
}

     另一种方式实现,使用ShuffleRDD

val sortInPartition = new ShuffledRDD(keyByRDD, partitioner).setKeyOrdering(orderRules)
sortInPartition.foreachPartition(it => {
  var index = 1
  while (it.hasNext && index <= topN) {
    val tuple = it.next()
    println(tuple)
    index += 1
  }
})

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!