聊一聊Spark实现TopN的几种方式

简介: Spark实现TopN

前言

在实际开发过程中,我们会经常碰到求TopN这样常见的需求,那在Spark中,是如何实现求TopN呢?带着这个问题,就来看一下TopN的实现方式都有哪些!

方式1:采用groupByKey

思路:

  1. 按照key对数据进行聚合(groupByKey)
  2. 对同组的key的所有value先转换为List,然后进行排序,最后取TopN

代码实现:

// 构造上下文
val conf = new SparkConf().setMaster("local").setAppName("topn")
val sc = SparkContext.getOrCreate(conf)
// 创建rdd
val path = "datas/groupsort.txt"
val rdd = sc.textFile(path)
// rdd操作得出结果
val rdd2 = rdd.map(_.split(" "))
val result = rdd2.map(arr => (arr(0).trim, arr(1).trim.toInt))
    .groupByKey()
    .map {
        case (key, values) => {
            // 对values中的数据进行排序,然后获取最大的前三个数据
            val sortedValues = values.toList.sorted
            val top3Values = sortedValues.takeRight(3).reverse
            (key, top3Values)
        }
    }
// 打印输出
result.collect().foreach(println)
sc.stop()

方式2:采用两阶段聚合优化

思路:

  1. 第一阶段给每个key加上一个随机值前缀,然后进行局部的聚合操作
  2. 第二阶段去除每个key的前缀,然后进行全局的聚合操作

代码实现:

// 构造上下文
val conf = new SparkConf().setMaster("local").setAppName("topn")
val sc = SparkContext.getOrCreate(conf)
// 创建rdd
val path = "datas/groupsort.txt"
val rdd = sc.textFile(path)
// rdd操作得出结果
val rdd2 = rdd.map(_.split(" "))
val result = rdd2.mapPartitions(iter => {
      val random = Random
      iter.map(arr => {
        val key = arr(0).trim
        val value = arr(1).trim.toInt
        ((random.nextInt(5),key),value)
      })
    }).groupByKey()
      .flatMap{
          case ((_,key),values) => {
            val sortedValues = values.toList.sorted
            val top3Values = sortedValues.takeRight(3).reverse
            top3Values.map(count => (key,count))
          }
        }
        .groupByKey()
        .flatMap{
          case (key, values) => {
            val sortedValues = values.toList.sorted
            val top3Values = sortedValues.takeRight(3).reverse
            top3Values.map((key,_))
          }
        }
// 打印输出
result.collect().foreach(println)
sc.stop()

方式3:先获取每个分区的TopN,后获取全局TopN

思路:

  1. 对于每一个key获取每个分区中的TopN
  2. 做全局的数据聚合操作,获取TopN

代码实现:

// 构造上下文
val conf = new SparkConf().setMaster("local").setAppName("topn")
val sc = SparkContext.getOrCreate(conf)
// 创建rdd
val path = "datas/groupsort.txt"
val rdd = sc.textFile(path)
// rdd操作得出结果
val rdd2 = rdd.map(_.split(" "))
val result3 = rdd2.map(arr => {
    val key = arr(0).trim
    val count = arr(1).trim.toInt
    (key, count)
})
.mapPartitions(iter => {
    import scala.collection.mutable
    val temp = iter.foldLeft(mutable.Map[String, ArrayBuffer[Int]]())((a, b) => {
        val key = b._1
        val count = b._2
        val buf = a.getOrElseUpdate(key, new mutable.ArrayBuffer[Int]())
        buf += count
        if (buf.size > 3) {
            val max3Vals = buf.sorted.takeRight(3)
            a(key) = max3Vals
        }
        a
    })
    val top3IterPrePartition = temp.toList.flatMap {
        case (key, countIters) => countIters.map(count => (key, count))
    }
    top3IterPrePartition.toIterator
})
.groupByKey()
.flatMap {
    case (key, values) => {
        val sorted = values.toList.sorted
        val top3 = sorted.takeRight(3).reverse
        top3.map((key, _))
    }
}
result3.foreachPartition(iter => iter.foreach(println))
sc.stop()

方式4:采用aggregateByKey

思路:

  1. 初始值为mutable.ArrayBufferInt
  2. 对每组key中的每个value和之前的聚合值进行聚合操作,就是在分区中,来一个value和上次取出的TopN进行一次排序,取出新的TopN
  3. 对每个分区操作后的局部聚合结果进行合并聚合操作,就是在分区间,来一个分区和上次取出的TopN进行一次合并排序,取出新的TopN

代码实现:

// 构造上下文
val conf = new SparkConf().setMaster("local").setAppName("topn")
val sc = SparkContext.getOrCreate(conf)
// 创建rdd
val path = "datas/groupsort.txt"
val rdd = sc.textFile(path)
// rdd操作得出结果
val rdd2 = rdd.map(_.split(" "))
import scala.collection.mutable
val result4 = rdd2.map(arr => {
    val key = arr(0).trim
    val count = arr(1).trim.toInt
    (key, count)
}).aggregateByKey(mutable.ArrayBuffer[Int]())(
    (u, v) => {
        u += v
        u.sorted.takeRight(3).reverse
    },
    (u1, u2) => {
        u1 ++= u2
        u1.sorted.takeRight(3).reverse
    }
).flatMap {
    case (key, values) => {
        values.toList.map((key, _))
    }
}
result4.foreachPartition(iter => iter.foreach(println))
sc.stop()

优缺点

方式1的缺点:

  1. groupByKey会将相同key的所有value全部加载到内存进行处理,当value特别多的时候可能出现OOM异常
  2. groupByKey会将所有的value数据均发送给下一个RDD,性能比较低,因为在实际聚合操作中只需要部分数据

方式2的优缺点:

  1. 对于聚合类Shuffle操作(groupByKey,reduceByKey等)产生的问题能够很好的解决
  2. 对于非聚合类(join等)产生的问题很难使用该方法解决

方式3、方式4:

  1. 解决了方式1实现方式的两个缺点
  2. 都采用了先分区内预聚合,然后进行全局聚合的思想

结语

好了,今天就为大家分享到这里了。咱们下期见!

如果本文对你有帮助的话,欢迎点赞&收藏&分享,这对我继续分享&创作优质文章非常重要。感谢🙏🏻

相关文章
|
分布式计算 并行计算 Ubuntu
|
分布式计算 Spark
【Spark 应用】实现分组取topN
【Spark 应用】实现分组取topN
204 0
|
21天前
|
分布式计算 大数据 Apache
ClickHouse与大数据生态集成:Spark & Flink 实战
【10月更文挑战第26天】在当今这个数据爆炸的时代,能够高效地处理和分析海量数据成为了企业和组织提升竞争力的关键。作为一款高性能的列式数据库系统,ClickHouse 在大数据分析领域展现出了卓越的能力。然而,为了充分利用ClickHouse的优势,将其与现有的大数据处理框架(如Apache Spark和Apache Flink)进行集成变得尤为重要。本文将从我个人的角度出发,探讨如何通过这些技术的结合,实现对大规模数据的实时处理和分析。
56 2
ClickHouse与大数据生态集成:Spark & Flink 实战
|
1月前
|
存储 分布式计算 算法
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
大数据-106 Spark Graph X 计算学习 案例:1图的基本计算、2连通图算法、3寻找相同的用户
61 0
|
1月前
|
消息中间件 分布式计算 NoSQL
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
40 0
|
1月前
|
消息中间件 存储 分布式计算
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
87 0
|
22天前
|
SQL 机器学习/深度学习 分布式计算
Spark快速上手:揭秘大数据处理的高效秘密,让你轻松应对海量数据
【10月更文挑战第25天】本文全面介绍了大数据处理框架 Spark,涵盖其基本概念、安装配置、编程模型及实际应用。Spark 是一个高效的分布式计算平台,支持批处理、实时流处理、SQL 查询和机器学习等任务。通过详细的技术综述和示例代码,帮助读者快速掌握 Spark 的核心技能。
48 6
|
20天前
|
存储 分布式计算 Hadoop
数据湖技术:Hadoop与Spark在大数据处理中的协同作用
【10月更文挑战第27天】在大数据时代,数据湖技术凭借其灵活性和成本效益成为企业存储和分析大规模异构数据的首选。Hadoop和Spark作为数据湖技术的核心组件,通过HDFS存储数据和Spark进行高效计算,实现了数据处理的优化。本文探讨了Hadoop与Spark的最佳实践,包括数据存储、处理、安全和可视化等方面,展示了它们在实际应用中的协同效应。
71 2
|
21天前
|
存储 分布式计算 Hadoop
数据湖技术:Hadoop与Spark在大数据处理中的协同作用
【10月更文挑战第26天】本文详细探讨了Hadoop与Spark在大数据处理中的协同作用,通过具体案例展示了两者的最佳实践。Hadoop的HDFS和MapReduce负责数据存储和预处理,确保高可靠性和容错性;Spark则凭借其高性能和丰富的API,进行深度分析和机器学习,实现高效的批处理和实时处理。
59 1
|
22天前
|
分布式计算 大数据 OLAP
AnalyticDB与大数据生态集成:Spark & Flink
【10月更文挑战第25天】在大数据时代,实时数据处理和分析变得越来越重要。AnalyticDB(ADB)是阿里云推出的一款完全托管的实时数据仓库服务,支持PB级数据的实时分析。为了充分发挥AnalyticDB的潜力,将其与大数据处理工具如Apache Spark和Apache Flink集成是非常必要的。本文将从我个人的角度出发,分享如何将AnalyticDB与Spark和Flink集成,构建端到端的大数据处理流水线,实现数据的实时分析和处理。
50 1
下一篇
无影云桌面