在 Spark 中,RDD(弹性分布式数据集)和 DataFrame 的操作被分为 ** 转换算子(Transformations)和行动算子(Actions)** 两类,它们是构建 Spark 应用的核心概念。以下是对这两种算子的详细解释和常见示例:
一、转换算子(Transformations)
转换算子用于从现有 RDD/DF 创建新的 RDD/DF,这类操作是惰性的(Lazy),即调用转换算子时不会立即执行计算,而是记录计算逻辑(DAG),直到遇到行动算子才会触发实际计算。
常见转换算子
-
map(func)
- 对 RDD/DF 中的每个元素应用函数
func
,返回新的 RDD/DF。
python
运行
# 示例:将每个元素平方 rdd = sc.parallelize([1, 2, 3]) squared_rdd = rdd.map(lambda x: x**2) # [1, 4, 9]
- 对 RDD/DF 中的每个元素应用函数
-
filter(func)
- 过滤出满足函数
func
条件的元素,返回新的 RDD/DF。
python
运行
# 示例:过滤出偶数 even_rdd = rdd.filter(lambda x: x % 2 == 0) # [2]
- 过滤出满足函数
-
flatMap(func)
- 先对元素应用
func
,再将结果展平(常用于处理嵌套结构)。
python
运行
# 示例:将每行文本拆分为单词 lines = sc.parallelize(["Hello world", "Spark is fast"]) words = lines.flatMap(lambda line: line.split(" ")) # ["Hello", "world", "Spark", "is", "fast"]
- 先对元素应用
-
union(other)
- 合并两个 RDD/DF,返回一个新的 RDD/DF。
python
运行
rdd1 = sc.parallelize([1, 2]) rdd2 = sc.parallelize([3, 4]) union_rdd = rdd1.union(rdd2) # [1, 2, 3, 4]
-
reduceByKey(func)
- 对键值对 RDD 中相同键的值应用
func
进行聚合。
python
运行
# 示例:单词计数 pairs = sc.parallelize([("apple", 1), ("banana", 2), ("apple", 3)]) counts = pairs.reduceByKey(lambda x, y: x + y) # [("apple", 4), ("banana", 2)]
- 对键值对 RDD 中相同键的值应用
-
join(other)
- 对两个键值对 RDD 进行内连接(基于键匹配)。
python
运行
# 示例:用户ID与订单ID关联 users = sc.parallelize([(1, "Alice"), (2, "Bob")]) orders = sc.parallelize([(1, "ORD1"), (2, "ORD2"), (2, "ORD3")]) joined = users.join(orders) # [(1, ("Alice", "ORD1")), (2, ("Bob", "ORD2")), (2, ("Bob", "ORD3"))]
-
groupByKey()
- 对键值对 RDD 按键分组,返回
(key, Iterable[value])
。
python
运行
# 示例:按单词分组 grouped = pairs.groupByKey() # [("apple", [1, 3]), ("banana", [2])]
- 对键值对 RDD 按键分组,返回
-
distinct()
- 去重,返回唯一元素的 RDD。
python
运行
rdd = sc.parallelize([1, 2, 2, 3]) distinct_rdd = rdd.distinct() # [1, 2, 3]
二、行动算子(Actions)
行动算子用于触发实际计算并返回结果(如返回值、写入文件、显示数据等)。调用行动算子时,Spark 会执行之前记录的所有转换操作。
常见行动算子
-
collect()
- 将 RDD/DF 的所有元素收集到驱动程序(Driver)中,返回列表。
python
运行
# 示例:收集所有元素 result = rdd.collect() # 返回Python列表
-
count()
- 返回 RDD/DF 的元素个数。
python
运行
# 示例:统计元素数量 rdd.count() # 3
-
first() / take(n)
first()
:返回第一个元素。take(n)
:返回前n
个元素。
python
运行
rdd.first() # 1 rdd.take(2) # [1, 2]
-
reduce(func)
- 对 RDD/DF 的所有元素应用
func
进行聚合。
python
运行
# 示例:求和 total = rdd.reduce(lambda x, y: x + y) # 6
- 对 RDD/DF 的所有元素应用
-
saveAsTextFile(path)
- 将 RDD 保存为文本文件(每个元素转换为字符串)。
python
运行
rdd.saveAsTextFile("hdfs://path/to/output")
-
foreach(func)
- 对 RDD/DF 的每个元素应用
func
(常用于副作用操作,如写入外部存储)。
python
运行
# 示例:将每个元素打印到控制台(生产环境慎用,可能导致性能问题) rdd.foreach(print)
- 对 RDD/DF 的每个元素应用
-
countByKey()
- 对键值对 RDD 按键统计元素个数,返回字典。
python
运行
# 示例:统计每个键的出现次数 pairs.countByKey() # {"apple": 2, "banana": 1}
三、转换算子 vs 行动算子
特性 | 转换算子 | 行动算子 |
---|---|---|
执行时机 | 惰性执行(不触发计算) | 立即执行(触发计算) |
返回值 | 新的 RDD/DF | 具体值(如列表、整数、文件等) |
常见用途 | 构建计算逻辑链(DAG) | 获取最终结果或触发副作用 |
典型算子 | map , filter , reduceByKey |
collect , count , saveAsTextFile |
四、示例:WordCount 流程
python
运行
# 转换算子(惰性执行)
lines = sc.textFile("hdfs://input.txt") # 读取文件(转换)
words = lines.flatMap(lambda line: line.split(" ")) # 分词(转换)
pairs = words.map(lambda word: (word, 1)) # 映射为键值对(转换)
counts = pairs.reduceByKey(lambda a, b: a + b) # 按单词聚合(转换)
# 行动算子(触发计算)
counts.saveAsTextFile("hdfs://output") # 保存结果(行动)
五、注意事项
- 惰性求值:转换算子的惰性特性允许 Spark 优化执行计划(如合并操作、减少数据传输)。
- 内存管理 :
collect()
等行动算子会将数据收集到驱动程序,可能导致内存溢出,建议使用take()
或sample()
进行调试。 - 性能优化 :优先使用
reduceByKey()
而非groupByKey()
,避免数据倾斜;使用cache()
或persist()
缓存频繁使用的 RDD。