1. 基础环境搭建
- 日志级别设置:
Logger.getLogger("org").setLevel(Level.OFF)关闭 Spark 冗余日志,便于查看业务输出; - Spark 上下文创建:Spark 2.0+ 优先通过
SparkSession构建,再通过spark.sparkContext获取Sc(SparkContext),本地模式配置master("local"),指定应用名appName("xxx")。
2. RDD 核心分类
- 普通 RDD:元素为单一类型(Int、String 等),支持基础的过滤、映射、集合运算;
- 键值对 RDD(Pair RDD):元素为
(key, value)二元组,支持 ByKey 系列专属操作,是分布式聚合、分组的核心载体。
3. 数据处理通用流程
从实战代码中提炼出通用 RDD 数据处理流程:RDD创建 → 数据清洗 → 数据转换/合并/运算 → 数据筛选 → 结果输出(如违章记录处理:读取 HDFS 文件 → 去表头清洗 → union 合并 → map 切分 + filter 筛选 → 格式化打印)
二、 核心 RDD 函数分类总结(按功能归类,含详细说明)
按 RDD 操作类型和功能场景,将所有涉及的函数分为RDD 创建函数 、行动操作函数 、普通转换操作函数 、键值对 RDD 专属函数四大类,清晰呈现每个函数的用途、特性和示例。
1. RDD 创建函数
用于生成 RDD,是所有 RDD 操作的基础,支持内存集合、外部文件、RDD 转换三种来源。
| 函数名 | 功能描述 | 适用场景 | 代码示例 |
|---|---|---|---|
parallelize |
将本地集合(List/Array)并行化,生成 RDD;元素可单一类型或二元组(键值对) | 本地小数据测试、快速构建 RDD | sc.parallelize(List(1,2,3))(普通 RDD);sc.parallelize(List(("Tom",82)))(键值对 RDD) |
textFile |
读取外部文本文件(本地路径 / HDFS 路径),每行文本作为 RDD 一个元素,返回RDD[String] |
读取日志、数据文件等文本数据 | sc.textFile("hdfs://master:8020/spark/data/record.txt")(读取 HDFS 文件) |
zip |
拉链函数:将两个同分区、同元素数量的 RDD 合并为键值对 RDD(第一个 RDD 为 key,第二个为 value) | 两个关联 RDD 绑定为键值对 | rdd4.zip(rdd5)(将 "五岳名称" 与 "对应山脉" 合并为键值对 RDD) |
2. 行动操作函数
立即触发 Spark 作业计算(非懒加载),返回本地数据(非 RDD),用于取值、打印、结果输出等场景。
| 函数名 | 功能描述 | 返回类型 | 特性 / 注意事项 | 代码示例 |
|---|---|---|---|---|
take(n) |
获取 RDD 中前 n 个元素 | 本地 Array 数组 | 可遍历打印,适用于预览少量数据 | intRdd.take(3)(获取前 3 个元素,返回Array[Int]) |
first() |
获取 RDD 的第一个元素,等价于take(1)(0) |
单个元素(与 RDD 元素类型一致) | 简化获取首个元素的操作 | recordA.first()(获取文件表头行);intRdd.first()(获取首个数值元素) |
foreach |
分布式遍历 RDD 中所有元素,执行自定义逻辑(如打印、写入存储) | 无返回值(Unit) | 行动操作,触发计算;区别于本地数组的foreach |
rdd2.foreach(println)(打印 RDD 所有元素);result.foreach(x=>{格式化打印}) |
lookup(key) |
键值对 RDD 专属:根据指定 key 查找所有对应的 value | Seq(序列) | 无需全量遍历,查询效率较高 | rdd6.lookup("东岳")(查找 "东岳" 对应的 value "泰山") |
3. 普通转换操作函数
懒加载(仅记录逻辑,不立即计算),返回新的 RDD,适用于所有类型 RDD,核心用于数据清洗、映射、集合运算等。
| 函数名 | 功能描述 | 输入输出关系 | 关键特性 | 代码示例 |
|---|---|---|---|---|
filter |
接收布尔类型匿名函数,保留返回true的元素,过滤掉false的元素 |
多对少(元素数量≤原 RDD) | 不改变元素类型,仅筛选元素 | rdd1.filter(x=>x%2==0)(筛选偶数);rdd3.filter(x=>x._2>=60)(筛选及格成绩) |
map |
一对一转换:对每个元素执行自定义逻辑,输入一个元素,输出一个元素 | 一对一(元素数量 = 原 RDD) | 可改变元素类型,保留嵌套结构 | intRdd.map(x=>x+1)(数值 + 1);strRdd.map(x=>x.toUpperCase())(字符串转大写) |
flatMap |
一对多转换:先执行 map 逻辑,再对结果进行扁平化展开(拆解嵌套结构) | 一对多(元素数量≥原 RDD) | 拆解数组 / 集合等嵌套结构,核心区别于 map | strRdd2.flatMap(x=>x.split(" "))(切分字符串并拆解数组,获取单个单词) |
union |
合并两个同类型 RDD,求并集 | 元素数量 = 两个 RDD 元素之和 | 不自动去重,是唯一不去重的集合运算 | cleanA.union(cleanB)(合并两个清洗后的违章记录 RDD) |
intersection |
求两个同类型 RDD 的交集,仅保留同时存在于两个 RDD 中的元素 | 元素数量≤两个 RDD 的最小值 | 自动去重,会触发 Shuffle | rdd5.intersection(rdd6)(获取 rdd5 和 rdd6 的共同元素) |
subtract |
求差集:A.subtract (B) 保留属于 A 但不属于 B 的元素 | 元素数量≤原 RDD(A)的元素数量 | 自动去重,会触发 Shuffle | rdd5.subtract(rdd6)(获取 rdd5 有但 rdd6 没有的元素) |
distinct |
对单个 RDD 进行去重,移除重复元素 | 元素数量≤原 RDD | 会触发 Shuffle,适用于去重需求 | rdd6.distinct()(去掉 rdd6 中的重复元素 2) |
4. 键值对 RDD 专属函数
仅适用于元素为(key, value)二元组的 Pair RDD,是分布式分组、聚合、排序的核心函数,按功能再细分三类。
(1) 基础取值函数
| 函数名 | 功能描述 | 返回类型 | 代码示例 |
|---|---|---|---|
keys |
提取键值对 RDD 中所有的 key,生成仅包含 key 的普通 RDD | RDD(key 类型) | rdd6.keys(提取五岳名称 key) |
values |
提取键值对 RDD 中所有的 value,生成仅包含 value 的普通 RDD | RDD(value 类型) | rdd6.values(提取山脉名称 value) |
(2) ByKey 核心函数(分组 / 聚合 / 排序)
| 函数名 | 功能描述 | 关键特性 | 代码示例 |
|---|---|---|---|
groupByKey |
按 key 进行分组,同一 key 的所有 value 封装到CompactBuffer(Spark 可变数组)中 |
仅分组,不做聚合;性能低于reduceByKey |
rdd10.groupByKey()(按 "水果 / 蔬菜" 分组,聚合对应商品) |
reduceByKey |
先按 key 分组,再对每组的 value 执行归约计算(求和、最大值等) | 分组 + 聚合;有预聚合,性能更优 | rdd14.reduceByKey((a,b)=>a+b)(按 key 对 value 求和) |
sortByKey |
按 key 进行排序,默认升序(true),传入false为降序 |
按 key 有序排列,支持数值 / 字符串排序 | rdd12.sortByKey(false)(按 key 降序排序) |
(3) 仅处理 Value 的函数
| 函数名 | 功能描述 | 关键特性 | 代码示例 |
|---|---|---|---|
mapValues |
仅对键值对 RDD 的 value 进行自定义转换,key 保持不变 | 无需手动处理 key,比普通 map 更简洁 | rdd16.mapValues(x=>x*0.6)(value 乘以 0.6,key 自动保留) |
三、 核心函数关键区别与避坑指南
mapvsflatMap:核心区别是是否保留嵌套结构 ,map保留数组 / 集合嵌套,flatMap拆解嵌套;groupByKeyvsreduceByKey:reduceByKey有预聚合(Map 端聚合),性能优于groupByKey(仅 Shuffle 后聚合),优先使用reduceByKey做分组聚合;- 集合运算函数:
union不自动去重,intersection/subtract自动去重,且后两者会触发 Shuffle,大数据量使用需谨慎; - 字符串比较:筛选数据时,优先用
equals()(如x.equals(firstA)),而非==/!=,避免分布式环境下对象引用判断错误; - 索引区别:数组索引从 0 开始,元组索引从 1 开始(
x._1/x._2),避免取错元素。
例子:
java
package bigdata2403
import org.apache.log4j.{Level, Logger}
import org.apache.spark.sql.SparkSession
object Weizhang {
def main(args: Array[String]): Unit = {
//1.设置日志级别
Logger.getLogger("org").setLevel(Level.OFF)
//2.创建SparkSession对象
val spark = SparkSession
.builder()
.appName("rdd5")
.master("local")
.getOrCreate()
//3.SparkContext对象的获取
val sc = spark.sparkContext
/*
查找违章3次以上的车辆
分析:
1.读取数据文件,创建RDD数据集---textFile
2.去掉表头
(1)获取第一个元素---first
(2)筛选出不是第一个元素的数据---filter
3.对每个rdd元素做切分操作---map(split)
4.转换为键值对RDD---map
(车牌号,1)---(x(2),1)
5.每个车辆的违章次数---reduceByKey
(Mu0066,3)
6.筛选出次数大于3的记录---filter(x=>x._2>3)
*/
//1.读取数据文件,创建RDD数据集---textFile
val record = sc.textFile("D:\\spark\\data\\record.txt")
//2.去掉表头
val recordFirst = record.first()
val recordWithoutHead = record.filter(x=> !x.equals(recordFirst))
//3.对每个rdd元素做切分操作---map(split)
val recordSplit = recordWithoutHead.map(x=>x.split("\t"))
//4.转换为键值对RDD---map
val recordMerge = recordSplit.map(x=>(x(2),1))
//5.每个车辆的违章次数---reduceByKey
val recordCount = recordMerge.reduceByKey((a,b)=>a+b)
//6.筛选出次数大于3的记录---filter(x=>x._2>3)
val result = recordCount.filter(x=>x._2>3)
//7.打印输出结果:车牌号:【CZ8462】,违章次数:【6】次
result.foreach(x=>println("车牌号:【"+x._1+"】,违章次数:【"+x._2+"】次"))
}
}