案例:计算学科最受欢迎老师TopN
1.需求:根据网站的行为日志,统计每个学科最受欢迎老师的TopN,即按照学科分组,在每一个组内进行排序
2. 样例数据:
http://bigdata.51doit.cn/laozhang
http://bigdata.51doit.cn/laozhang
http://bigdata.51doit.cn/laozhao
http://bigdata.51doit.cn/laozhao
http://bigdata.51doit.cn/laozhao
http://bigdata.51doit.cn/laozhao
http://bigdata.51doit.cn/laozhao
http://bigdata.51doit.cn/laoduan
http://bigdata.51doit.cn/laoduan
http://javaee.51doit.cn/xiaozhang
http://javaee.51doit.cn/xiaozhang
http://javaee.51doit.cn/laowang
http://javaee.51doit.cn/laowang
http://javaee.51doit.cn/laowang
数据格式:http://学科.51doit.cn/老师名称
3.六种不同的实现方式:
3.1 第一种方法:调用groupBy按照学科进行分组,然后将value对应的迭代器toList,将数据全部加载到内存中,然后在调用List的sortBy方法进行排序,然后再调用take取TopN
package com.zxx.spark.day06
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* 第一种方法是将迭代器中的数据都收集到内存中然后在内存中进行排序,
* 这种方法只针对于少量数据适合都加载到内存中,
* 如果是海量数据则有可能出现内存溢出的问题
*/
object FavoriteTeacherDemo1 {
def main(args: Array[String]): Unit = {
//求每一学科最受欢迎的老师TopN
//先创建SparkContext和集群建立链接
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName)
//添加一个判断,判断是否设置为集群或是,还是本地模式
val flag: Boolean = args(0).toBoolean
if (flag) {
conf.setMaster("local[*]")
}
val sc: SparkContext = new SparkContext(conf)
//创建RDD
val rdd: RDD[String] = sc.textFile(args(1))
// http: / / bigdata.51doit.cn/laozhang
//将读取到的数据进行切割和处理
val subjectAndTeacherOne: RDD[((String, String), Int)] = rdd.map(e => {
val sp: Array[String] = e.split("/")
val url: Array[String] = sp(2).split("\\.")
((url(0), sp(3)), 1)
})
//subjectAndTeacherOne:((bigdata,laozhao),1), ((bigdata,laozhao),1), ((bigdata,laozhao),1),
//将数据按照学科和老师进行联合作为key进行聚合
val reduced: RDD[((String, String), Int)] = subjectAndTeacherOne.reduceByKey(_ + _)
//然后在按照学科进行分组
val grouped: RDD[(String, Iterable[((String, String), Int)])] = reduced.groupBy(_._1._1)
//第一种方法是将迭代器中的数据都收集到内存中然后在内存中进行排序,这种方法只针对于少量数据适合都加载到内存中,如果是海量数据则有可能出现内存溢出的问题
val res: RDD[(String, List[((String, String), Int)])] = grouped.mapValues(it => {
val sorted: List[((String, String), Int)] = it.toList.sortBy(-_._2).take(3)
sorted
})
println(res.collect().toBuffer)
}
}
3.2 第二种方法:第二种方法是将每个学科过滤出来,然后单独对每一个学科进行排序,求topN,(不分组),这样就是将所有学科放在一个数组中,然后只对一个学科进行排序,这样就可以避免分组,减少一次shuffle,这样做虽然减少了一次shuffle,但是提交了3次job
package com.zxx.spark.day06
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
/**
* 第二种方式,是将每个学科过滤出来,然后单独对每一个学科进行求topN(底层调用了有界优先队列,不需要排序),利用for循环,遍历所有学科
* 需要将要过滤的所有学科放在一个数组中
*/
object FavoriteTeacherDemo2 {
def main(args: Array[String]): Unit = {
//求每一学科最受欢迎的老师TopN
//先创建SparkContext和集群建立链接
val conf: SparkConf = new SparkConf().setAppName(this.getClass.getName)
//添加一个判断,判断是否设置为集群或是,还是本地模式
val flag: Boolean = args(0).