目录
[1. SparkContext 的作用](#1. SparkContext 的作用)
[2. 参数解析](#2. 参数解析)
[(1) 第一个参数:"local"](#(1) 第一个参数:"local")
[(2) 第二个参数:"example"](#(2) 第二个参数:"example")
[3. 完整代码示例](#3. 完整代码示例)
[1. map(func)](#1. map(func))
[3. sample(withReplacement, fraction, seed)](#3. sample(withReplacement, fraction, seed))
[4. groupByKey()](#4. groupByKey())
[5. reduceByKey(func)](#5. reduceByKey(func))
[6. sortByKey(ascending=True)](#6. sortByKey(ascending=True))
[7. flatMap(func)](#7. flatMap(func))
[8. union(otherRDD)](#8. union(otherRDD))
[9. join(otherRDD)](#9. join(otherRDD))
[10. cogroup(otherRDD)](#10. cogroup(otherRDD))
[1. collect()](#1. collect())
[2. reduce(func)](#2. reduce(func))
[3. count()](#3. count())
[4. saveAsTextFile(path)](#4. saveAsTextFile(path))
[5. lookup(key)](#5. lookup(key))
sc = SparkContext("local", "example")
是 Apache Spark 中用于初始化 Spark 应用程序的代码。它的作用是创建一个 SparkContext 对象(通常简写为 sc
),这是 Spark 应用程序的入口点,负责连接集群、管理任务和资源。以下是逐部分解析:
sc.parallelize
是 Apache Spark 中的一个方法,用于将 本地集合(Local Collection) 转换为 分布式数据集(RDD,Resilient Distributed Dataset)。它是 Spark 中最基础的创建 RDD 的方式之一,常用于测试和小规模数据处理。
1. SparkContext
的作用
- Spark 应用程序的核心 :所有 Spark 功能(如创建 RDD、调度任务)都通过
SparkContext
实现。 - 资源管理:与集群管理器(如 YARN、Kubernetes)通信,分配 CPU、内存等资源。
- 应用入口 :类似于打开数据库需要
Connection
,使用 Spark 必须先创建SparkContext
。
2. 参数解析
(1) 第一个参数:"local"
- 含义 :指定 Spark 运行的模式为 本地模式(Local Mode)。
- 本地模式:Spark 不会连接到集群,而是在当前机器上启动一个单机进程,所有任务在本地线程中运行。
- 适用场景:开发、测试、调试(无需集群环境,适合小数据量)。
- 扩展 :
"local[*]"
:使用本地所有 CPU 核心。"local[4]"
:指定使用 4 个 CPU 核心。
(2) 第二个参数:"example"
- 含义:设置 Spark 应用程序的名称(Application Name)。
- 用途:在集群管理界面(如 YARN 的 Web UI)或日志中标识应用。
- 示例 :
如果应用名为"example"
,在 YARN 的 Web UI 中会显示为"example"
,方便监控和调试。
3. 完整代码示例
python
from pyspark import SparkContext
# 初始化 SparkContext:本地模式,应用名称为 "example"
sc = SparkContext("local", "example")
# 使用 SparkContext 创建 RDD
data = [1, 2, 3, 4, 5]
rdd = sc.parallelize(data)
# 执行操作:计算元素总和
total = rdd.sum()
print("总和:", total) # 输出:总和: 15
# 关闭 SparkContext(释放资源)
sc.stop()
以下是 Spark Operations 中常见 Transformations(转换操作) 和 Actions(动作操作) 的详细解释及示例:
一、Transformations(转换操作)
定义 :
转换操作通过现有 RDD 生成新的 RDD,延迟执行(Lazy Execution),仅在触发 Action 时实际计算。
1. map(func)
- 说明 :对 RDD 中的每个元素应用函数
func
,生成新 RDD。 - 示例:将每个数值加 1
python
rdd = sc.parallelize([1, 2, 3])
mapped_rdd = rdd.map(lambda x: x + 1) # 结果:[2, 3, 4]
2. filter(func)
- 说明 :筛选满足
func
条件的元素。 - 示例:保留偶数。
python
rdd = sc.parallelize([1, 2, 3, 4])
filtered_rdd = rdd.filter(lambda x: x % 2 == 0) # 结果:[2, 4]
3. sample(withReplacement, fraction, seed)
- 说明 :随机抽样数据。
withReplacement
: 是否放回(True/False)。fraction
: 抽样比例。
- 示例:不放回抽样 50%。
python
rdd = sc.parallelize(range(100))
sampled_rdd = rdd.sample(False, 0.5, seed=42)
sampleByKey
非常适合以下需求:
-
对每类数据分开采样,比如每个用户、每种标签、每种类型......
-
保证某些 key 的数据比例不同(不均匀采样)
python
sampled_by_key = rdd.sampleByKey(
withReplacement=False,
fractions={"a": 0.5, "b": 1.0, "c": 0.5, "d": 0.0},
seed=42
)
一个字典,指定每个 key 的采样比例:
• "a": 0.5
表示 key 为 "a"
的值抽 50%
• "b": 1.0
表示 key 为 "b"
的值全部保留
• "c": 0.5
表示 key 为 "c"
的值抽 50%
• "d": 0.0
表示 key 为 "d"
的值全部舍弃
4. groupByKey()
- 说明 :按键分组,生成
(K, Iterable<V>),groupByKey
是 Spark 中针对 键值对 RDD(Pair RDD) 的转换操作,其作用是将相同键(Key)的所有值(Value)聚合为一个可迭代对象 (在 Python 中为Iterable
) - 示例:按单词首字母分组。
在示例代码中,mapValues(list)
的作用是将 groupByKey
生成的 迭代器(Iterable
) 转换为 列表(list
) ,可视化结果 :将迭代器转为列表后,可以通过 collect()
直观查看分组后的所有值。
python
# 创建 Pair RDD(示例代码存在输入错误,应为正确键值对)
rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3), ("b", 4)])
# 按 Key 分组(输出为 RDD[("a", Iterable), ("b", Iterable)])
grouped_rdd = rdd.groupByKey()
# 将 Iterable 转为 List(输出为 RDD[("a", [1, 3]), ("b", [2, 4])])
result = grouped_rdd.mapValues(list).collect()
print(result) # 输出: [('a', [1, 3]), ('b', [2, 4])]
5. reduceByKey(func)
-
说明 :按键聚合,先本地合并(Combiner)再全局聚合,与
groupByKey
不同,reduceByKey
在 Shuffle 前通过 预聚合(Combine) 减少数据量,具体优势如下:预聚合减少 Shuffle 数据量Map 端聚合:
数据压缩效果 :例如,若每个 Key 有 1000 条 Value,预聚合后仅需传输 1 条聚合值,Shuffle 数据量减少 99%
-
示例:对相同键求和。
python
kv_rdd = sc.parallelize([("a", 1), ("b", 2), ("a", 3)])
reduced = kv_rdd.reduceByKey(lambda x, y: x + y) # 结果:("a", 4), ("b", 2)
6. sortByKey(ascending=True)
- 说明:按键排序。
- 示例:按字母升序排序。
python
kv_rdd = sc.parallelize([("z", 1), ("a", 2)])
sorted_rdd = kv_rdd.sortByKey() # 结果:[("a", 2), ("z", 1)]
7. flatMap(func)
- 说明 :类似
map
,但结果会被"扁平化"(如将字符串拆分为单词)。 - 示例:拆分句子为单词。
python
rdd = sc.parallelize(["hello world", "spark is cool"])
flat_rdd = rdd.flatMap(lambda x: x.split()) # 结果:["hello", "world", "spark", ...]
8. union(otherRDD)
- 说明:合并两个 RDD。
- 示例:合并两个列表
python
rdd1 = sc.parallelize([1, 2])
rdd2 = sc.parallelize([3, 4])
union_rdd = rdd1.union(rdd2) # 结果:[1, 2, 3, 4]
9. join(otherRDD)
- 说明:对两个键值对 RDD 进行内连接。
- 示例:连接两个数据集。
python
rdd1 = sc.parallelize([("a", 1), ("b", 2)])
rdd2 = sc.parallelize([("a", 3), ("c", 4)])
joined = rdd1.join(rdd2) # 结果:("a", (1, 3))
10. cogroup(otherRDD)
- 说明 :对多个 RDD 中的相同键分组,生成
(K, (Iterable<V1>, Iterable<V2>))
。 - 示例
python
rdd1 = sc.parallelize([("a", 1), ("b", 2)])
rdd2 = sc.parallelize([("a", 3), ("c", 4)])
cogrouped = rdd1.cogroup(rdd2)
# 结果:("a", ([1], [3])), ("b", ([2], [])), ("c", ([], [4]))
11. cross(otherRDD)
- 说明:生成两个 RDD 的笛卡尔积(所有元素对)。
- 示例
python
rdd1 = sc.parallelize([1, 2])
rdd2 = sc.parallelize(["a", "b"])
cross_rdd = rdd1.cartesian(rdd2)
# 结果:[(1, "a"), (1, "b"), (2, "a"), (2, "b")]
12. mapValues(func)
- 说明 :仅对键值对的
Value
应用函数,保留键不变。 - 示例:将值加倍。
python
kv_rdd = sc.parallelize([("a", 1), ("b", 2)])
mapped_values = kv_rdd.mapValues(lambda x: x * 2) # 结果:("a", 2), ("b", 4)
二、Actions(动作操作)
定义 :
动作操作触发实际计算,返回结果到 Driver 程序或保存数据到外部存储。
1. collect()
- 说明:将 RDD 的所有元素以列表形式返回到 Driver。
- 示例:
python
rdd = sc.parallelize([1, 2, 3])
result = rdd.collect() # 结果:[1, 2, 3]
2. reduce(func)
- 说明 :通过函数
func
聚合所有元素(需满足结合律和交换律)。 - 示例:求和
python
rdd = sc.parallelize([1, 2, 3])
total = rdd.reduce(lambda x, y: x + y) # 结果:6
3. count()
- 说明:返回 RDD 的元素总数。
- 示例:
python
rdd = sc.parallelize([1, 2, 3])
num = rdd.count() # 结果:3
4. saveAsTextFile(path)
- 说明:将 RDD 保存为文本文件到指定路径(如 HDFS)。
- 示例
python
rdd = sc.parallelize(["hello", "world"])
rdd.saveAsTextFile("hdfs://output_dir")
5. lookup(key)
- 说明:返回键值对 RDD 中指定键的所有值。
- 示例:
python
kv_rdd = sc.parallelize([("a", 1), ("a", 2), ("b", 3)])
values = kv_rdd.lookup("a") # 结果:[1, 2]
注意事项:
- 转换操作是惰性的:需通过动作操作触发实际计算。
- 避免
collect
大数据集:可能导致 Driver 内存溢出。 - 优先使用
reduceByKey
而非groupByKey
:前者在 Shuffle 前合并数据,效率更高。