020 Spark中分组后的TopN,以及Spark的优化(重点)
一:准备
1.源数据

2.上传数据

二:TopN程序编码
1.程序
package com.ibeifeng.bigdata.spark.core
import java.util.concurrent.ThreadLocalRandom
import org.apache.spark.{SparkConf, SparkContext}
/**
* 分组TopN:按照第一个字段分组;同一组中,按照第二个字段进行排序;每一组中,获取出现最多的前K个数据。
* Created by ibf on 01/15.
*/
object GroupedTopN {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setMaster("local[*]")
.setAppName("grouped-topn")
//.set("spark.eventLog.enabled", "true")
//.set("spark.eventLog.dir", "hdfs://hadoop-senior01:8020/spark-history")
val sc = SparkContext.getOrCreate(conf)
// ==========具体代码逻辑========================
// 原始数据存储的路径, 需要自己上传
val path = "/user/beifeng/spark/groupedtopk/groupsort.txt"
val K = 3
// 构建rdd
val rdd = sc.textFile(path)
// rdd操作
val word2CountRDD = rdd
.filter((line: String) => {
// 过滤空字符串,所以非空的返回true
!line.isEmpty
})
.map(line => {
// 按照空格分隔字段
val arr = line.split(" ")
// 将数据转换为二元组
(arr(0), arr(1).toInt)
})
// 如果一个RDD被多次使用,该RDD需要进行缓存操作
word2CountRDD.cache()
// 直接使用groupByKey函数进行统计,这种方式存在OOM的情况
/*
val resultRDD = word2CountRDD
.groupByKey() // 按照第一个字段进行分组
.map(tuple => {
// 同一组的数据中获取前K个元素
// 获取对应分组
val word = tuple._1
// 获取前K个元素(最大的k个元素), list默认排序是升序, 所以采用takeRight从后往前获取K个元素(此时的K个元素就是最大的K个元素); 最后对K个元素进行反转,最终结果元素是从大到小排序的
val topk = tuple._2.toList.sorted.takeRight(K).reverse
// 返回结果
(word, topk)
})
*/
/*
* groupByKey存在OOM异常
* 解决方案:采用两阶段聚合操作
* 两阶段聚合可以解决的一些常见:
* 1. 聚合操作中存储的OOM异常
* 2. 聚合操作中存在的数据倾斜问题
* 聚合操作:分区、排序、reduceByKey.....
* */
val random = ThreadLocalRandom.current()
val resultRDD2 = word2CountRDD
.map(tuple => {
// 第一阶段第一步:在key前加一个随机数
((random.nextInt(100), tuple._1), tuple._2)
})
.groupByKey() // 第一阶段的第二步:按照修改后的key进行聚合操作
.flatMap(tuple => {
// 第一阶段的第三步:对一组value进行聚合操作
// 获取对应分组
val word = tuple._1._2
// 获取前K个
val topk = tuple._2.toList.sorted.takeRight(K).reverse
// 返回结果
topk.map(count => (word, count))
})
.groupByKey() // 第二阶段第一步:按照原本的key进行聚合操作
.map(tuple => {
// 第二阶段第二步: 获取前k个元素
val word = tuple._1
val topk = tuple._2.toList.sorted.takeRight(K).reverse
// 返回结果
(word, topk)
})
// 结果输出
resultRDD2.foreach(println)
/*
resultRDD2.foreachPartition(iter => {
// foreachPartition该函数常用于将RDD的数据输出到第三方的数据存储系统中,比如:redis、mongoDB
/*
* 1. 创建连接
* 2. 对iter进行迭代,进行数据输出
* 3. 关闭连接
* */
iter.foreach(println)
})
*/
// 如果RDD有cache,需要去除cache
word2CountRDD.unpersist()
// ==========具体代码逻辑========================
sc.stop()
}
}
2.结果

3.注意点
Spark中不支持二次排序,如果想实现二次排序,需要根据业务的执行逻辑使用两阶段聚合来进行操作
二:优化
1.两阶段聚合

020 Spark中分组后的TopN,以及Spark的优化(重点)的更多相关文章
- 如何在spark中读写cassandra数据 ---- 分布式计算框架spark学习之六
由于预处理的数据都存储在cassandra里面,所以想要用spark进行数据分析的话,需要读取cassandra数据,并把分析结果也一并存回到cassandra:因此需要研究一下spark如何读写ca ...
- Spark中的编程模型
1. Spark中的基本概念 Application:基于Spark的用户程序,包含了一个driver program和集群中多个executor. Driver Program:运行Applicat ...
- Spark中的Phoenix Dynamic Columns
代码及使用示例:https://github.com/wlu-mstr/spark-phoenix-dynamic phoenix dynamic columns HBase的数据模型是动态的,很多系 ...
- MySQL 排名、分组后组内排名、取各组的前几名 及排名后更新插入数据表中
一.排名 /*普通排名:从1开始,顺序往下排*/ AS rank ) r ORDER BY score; /*并列排名:相同的值是相同的排名*/ SELECT cs.* , CASE WHEN @p= ...
- Spark:求出分组内的TopN
制作测试数据源: c1 85 c2 77 c3 88 c1 22 c1 66 c3 95 c3 54 c2 91 c2 66 c1 54 c1 65 c2 41 c4 65 spark scala实现 ...
- sql-实现select取行号、分组后在分组内排序、每个分组中的前n条数据
表结构设计: 实现select取行号 sql局部变量的2种方式 set @name='cm3333f'; select @id:=1; 区别:set 可以用=号赋值,而select 不行,必须使用:= ...
- [MySQL]MySQL数据库中如何查询分组后每组中的最后一条记录?
原文地址:https://codedefault.com/s/how-can-i-retrieve-the-last-record-in-each-group-mysql 问题描述 比如,在MySQL ...
- SQL获取分组后取某字段最大一条记录(求每个类别中最大的值的列表)
获取分组后取某字段最大一条记录 方法一:(效率最高) select * from test as a where typeindex = (select max(b.typeindex) from t ...
- 024 关于spark中日志分析案例
1.四个需求 需求一:求contentsize的平均值.最小值.最大值 需求二:请各个不同返回值的出现的数据 ===> wordCount程序 需求三:获取访问次数超过N次的IP地址 需求四:获 ...
随机推荐
- git gui提交无法获知你的身份
解决方法: 打开git 终端 #输入下面两句,并且替换成你的名字和邮箱 git config --global user.email "your@email.com" git co ...
- C# 反射实例
1.接口 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ...
- Nginx GZIP 压缩
[ HTTP 开启gzip ] gzip on; // 开启 nginx在线实时压缩数据流: gzip_min_length 1k; // 允许压缩的页面最小字节 gzip_buffers 32k; ...
- Linux - 日志处理一
Linux 日志处理 history # 历时命令默认1000条 HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " # 让history命令显示具体时间 hi ...
- yolov3实践(二)
这次给大家带来一个有趣的项目,项目主要是Python写的,基于Keras,backend是tf. 首先,当我们回顾视觉目标检测这个任务时,我们可能纠结于如何使这个项目变得更加work,我理解的更加wo ...
- transition,过渡效果
语法: transtion:property time change-speed delay. 人话就是:属性(property )在多少秒内(time )通过什么样的速度(change-speed) ...
- ichartjs一分钟快速入门教程
1.构建项目环境 由于ichartjs是一个js库,所以只要将ichart.js加入你页面的head中就完成了ichartjs的运行环境.代码如下: <script type="tex ...
- 【API】检查进程是否存在 - CreateToolhelp32Snapshot
1 学习目标 今天静态逆向mydocument病毒时,看到病毒代码为了防止自身被调试会先检测杀毒软件和调试工具的进程是否存在.如果没有杀毒软件则释放真正的病毒文件,提前熟悉一下枚举进程的反汇编代码. ...
- Ubuntu14.04+caffe+CPU
刚刚在上篇博客记录了windows10下GPU版本caffe的安装,正准备跑跑论文里的代码,发现好多命令都是.sh命令,这是linux系统的脚本文件.不能直接在windows下运行,于是我想把.sh转 ...
- tomcat端口冲突,多个tomcat同时启动问题
一台PC机上安装了两个tomcat,需要同时启动,每个tomcat上跑一个程序,但是现在提示端口号冲突,需要手动更改. 需要修改/conf/server.xml四个地方: 1.<Server p ...