Spark 核心之二次排序、分组取 TopN 优化分析
摘要为什么你的分组 TopN 任务跑了 30 分钟还 OOM答案通常是四个字——groupByKey。本文将二次排序和分组 TopN 作为两条优化主线从反模式剖析、三种方案对比groupByKey / reduceByKeymapPartitions / SQL 窗口函数、groupByKey vs reduceByKey 本质区别、完整代码实战、Spark SQL 窗口函数最佳实践五个维度配合 1 张原创深色架构图助你彻底攻克 TopN 优化的每一个细节。关键词Spark 二次排序, TopN, groupByKey, reduceByKey, 窗口函数, row_number, mapPartitions, 分组优化一、开篇groupByKey 是 TopN 的超级陷阱// ❌ 这是 95% 的新手会写的代码——也是 OOM 的根源rdd.groupByKey()// 全量数据按 Key 分组.mapValues(_.toList.sortBy(-_._2).take(3))// ⚠ 所有 Value 加载到内存// 问题: 如果某个 Key 有 1000 万条数据 → Executor OOMgroupByKey 的问题它不做 Map 端预聚合将全部 KV 对原封不动地 Shuffle 到 Reducer然后全部加载到内存——数据倾斜时直接炸掉。二、优化方案全景图三、方案 1二次排序Secondary Sort3.1 自定义排序键// 按 classId 升序score 降序caseclassSecondarySortKey(classId:String,score:Int)extendsOrdered[SecondarySortKey]{overridedefcompare(that:SecondarySortKey):Int{valcmp1this.classId.compareTo(that.classId)if(cmp1!0)cmp1else-this.score.compareTo(that.score)// score 降序}}// 使用rdd.map{case(classId,name,score)(SecondarySortKey(classId,score),(name,score))}.sortByKey()// 按自定义键全局排序3.2 逐组处理避免 groupByKey// 利用 sortByKey 后的有序性mapPartitions 内逐组处理rdd.mapPartitions{itervarcurrentClassvarbufferListBuffer[(String,Int)]()iter.flatMap{case(key,(name,score))if(key.classId!currentClass){valresultbuffer.toList currentClasskey.classId bufferListBuffer((name,score))if(result.nonEmpty)result.iteratorelseIterator.empty}else{buffer((name,score))Iterator.empty}}}四、方案 2分组 TopN4.1 ❌ 反模式groupByKeyrdd.groupByKey().mapValues(_.toList.sortBy(-_._2).take(3))// 每个 Key 的 Value 全部加载到单节点内存 → OOM4.2 ⚠ 中等方案reduceByKey 局部 TopNrdd.map{case(key,v)(key,List(v))}.reduceByKey((a,b)(ab).sortBy(-_._2).take(3))// Map 端先局部合并Combiner → Shuffle 数据量大幅减少// 但 RDD API 写起来较繁琐4.3 ✅ 推荐方案Spark SQL 窗口函数-- 每个班级取成绩 Top 3SELECTclass,name,scoreFROM(SELECT*,row_number()OVER(PARTITIONBYclassORDERBYscoreDESC)ASrnFROMstudents)tWHERErn3// DataFrame APIimportorg.apache.spark.sql.expressions.WindowvalwindowSpecWindow.partitionBy(class).orderBy($score.desc)df.withColumn(rn,row_number().over(windowSpec)).where($rn3).drop(rn)五、groupByKey vs reduceByKey 本质区别维度groupByKeyreduceByKeymapSideCombine❌ false✅ trueShuffle 数据量全部 KV 对Map 端预聚合后少量数据内存压力高全量加载低逐批合并适用场景需要对 Value 做非结合性操作可结合、可交换的聚合等价窗口函数collect_list()sum/count/max/min 自定义 UDAF// reduceByKey 的内部 Combiner 机制rdd.reduceByKey(__)// 源码等价于:// combineByKey(createCombiner, mergeValue, mergeCombiners)// Map 端先 mergeValue → mergeCombiners 分批合并六、实战每个班级成绩 Top 3// 方案 A: RDD reduceByKey较复杂rdd.map{case(cls,name,score)(cls,List((name,score)))}.reduceByKey((a,b)(ab).sortBy(-_._2).take(3)).flatMapValues(identity)// 方案 B: Spark SQL 窗口函数推荐valwWindow.partitionBy(class).orderBy($score.desc)df.withColumn(rn,row_number().over(w)).where($rn3)// 方案 C: rank/dense_rank处理并列// rank(): 1,1,3,4 (并列占位)// dense_rank(): 1,1,2,3 (并列不占位)// row_number(): 1,2,3,4 (纯粹行号)七、性能总结方案Shuffle 量内存安全推荐度groupByKey100%❌ 高危⛔ 禁止reduceByKey10-30%⚠ 可控可用窗口函数10-30%✅ 最优⭐ 首选金句groupByKey 是 Spark 新手的第一大坑——它把分组当成目的却忘了分组只是手段。真正的目的是在减少 Shuffle 的前提下拿到想要的结果。记住能用 reduceByKey 绝不用 groupByKey能用窗口函数就用窗口函数。作者starzy | AI Data Engineer / 大数据技术实践者博客blog.starzy.cn | GitHubstarzy1990.github.io专注 AI Agent · LangGraph · RAG · 大数据架构 · 数据工程实践

相关新闻

车开两三万就顿挫、缺火?别再盲目换火花塞了!

车开两三万就顿挫、缺火?别再盲目换火花塞了!

很多车主都有过这样的困惑:车子才开2-4年,里程也就两三万公里,明明不算老车,却突然出现怠速抖动、加速顿挫、动力发闷的情况。去保养检查,技师拆开火花塞,指着发黑积碳的电极说:“火花塞老化了&…

2026/10/12 15:33:34 阅读更多 →
dnSpy 使用教程:保姆级带你从零完成 .NET 程序集的反编译、调试与修改

dnSpy 使用教程:保姆级带你从零完成 .NET 程序集的反编译、调试与修改

dnSpy 使用教程:保姆级带你从零完成 .NET 程序集的反编译、调试与修改 【免费下载链接】dnSpy Unofficial revival of the well known .NET debugger and assembly editor, dnSpy 项目地址: https://gitcode.com/gh_mirrors/dns/dnSpy 接手一个没有源码的 .N…

2026/10/4 9:36:21 阅读更多 →
2026.8.13 基础组合数学训练题解

2026.8.13 基础组合数学训练题解

交错代表队 一、题目题目名称:交错代表队题目背景: 某场 ICPC 训练营需要从两个方向的成员中选出一支代表队,并按照出场顺序排成一列。题目内容: 训练营中有 A 名算法组成员和 B 名工程组成员,每个人都互不相同。现在要…

2026/10/12 3:58:57 阅读更多 →

最新新闻

ponytail插件怎么用?从安装配置到实战踩坑的完整指南

ponytail插件怎么用?从安装配置到实战踩坑的完整指南

1. 从“ponytail”这个热词说起:它到底指什么第一次看到“ponytail”被当成一个技术热词来搜,我其实愣了一下。这个词在英文里的本义是“马尾辫”,一个再日常不过的发型词。但结合“ponytail skill”“ponytail 插件”“插件 ponytail 如何使…

2026/10/14 21:17:56 阅读更多 →
【测试门禁怎么设计?不是所有 Bug,都应该用同一种方式阻断发布】

【测试门禁怎么设计?不是所有 Bug,都应该用同一种方式阻断发布】

在上一篇文章中,我们聊了如何把接口自动化接入 CI/CD。 当自动化测试能够自动执行、生成报告并反馈结果后,下一个问题就出现了: 测试失败了,是否应该阻断发布? 很多团队在刚开始建设自动化测试时,容易采用两…

2026/10/14 21:17:56 阅读更多 →
单调栈用法

单调栈用法

一、什么时候适合用单调栈题目要求"为每个元素找左边/右边第一个比它大(或小)的元素",且暴力 O(N) 会超时 → 用单调栈。这是最核心的信号。看到"第一个更大 / 第一个更小 / 下一个更大 / 下一个更小"这类字眼&#xff0…

2026/10/14 21:17:56 阅读更多 →
智能 PDU 固件升级这件事:版本核对、灰度批次和回滚预案

智能 PDU 固件升级这件事:版本核对、灰度批次和回滚预案

智能 PDU 固件升级这件事:版本核对、灰度批次和回滚预案 固件升级在机房运维里属于「做了没人夸,做砸了整个部门都知道」的活儿。我参与过的项目里,升级本身出事的极少,出事的大多是准备工作没做足——型号搞混、批次一次上太多、…

2026/10/14 21:17:56 阅读更多 →
力扣 hot 100 python

力扣 hot 100 python

3. 无重复字符的最长子串 - 力扣(LeetCode) 题目 给定一个字符串 s ,请你找出其中不含有重复字符的最长子串的长度。 思路 找最长子串,范围为i,j-1 第j个字符不在i,j-1里,就加入,在ij里,i向…

2026/10/14 21:17:56 阅读更多 →
C++类和对象(下)初始化列表、类型转换、static、友元、内部类、匿名对象

C++类和对象(下)初始化列表、类型转换、static、友元、内部类、匿名对象

一.再探构造函数之初始化列表• 之前实现构造函数时,初始化成员变量都是在函数体内赋值,构造函数初始化还有一种方式:初始化列表。初始化列表的使用方式是:在函数定义处的参数列表后(在声明和定义分离时就在真正定义的…

2026/10/14 21:16:56 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →