学习笔记
- Key-Value RDD操作(重点突破)
常用Pair RDD转换操作:
scala
// 准备数据
val rdd = sc.parallelize(Seq(
("apple", 3), ("banana", 2), ("apple", 5),
("orange", 1), ("banana", 4)
))
// 1. reduceByKey - 相同key聚合(宽依赖)
val sumByKey = rdd.reduceByKey((x, y) => x + y)
// 结果: (apple,8), (banana,6), (orange,1)
// 2. groupByKey - 分组为迭代器(性能较差)
val grouped = rdd.groupByKey()
grouped.mapValues(_.sum) // 等同于reduceByKey但效率低
// 3. mapValues/flatMapValues - 只操作value
val doubleValues = rdd.mapValues(_ * 2)
// 结果: (apple,6), (banana,4), (apple,10), (orange,2), (banana,8)
// 4. keys/values - 提取键或值
rdd.keys.collect() // 所有key
rdd.values.collect() // 所有value
// 5. sortByKey - 按键排序
val sorted = rdd.sortByKey() // 升序
val descSorted = rdd.sortByKey(false) // 降序
// 6. join操作 - 表连接
val rdd1 = sc.parallelize(Seq(("a", 1), ("b", 2), ("c", 3)))
val rdd2 = sc.parallelize(Seq(("a", "x"), ("b", "y")))
val joined = rdd1.join(rdd2) // 内连接
// 结果: (a,(1,x)), (b,(2,y))
关键概念:宽依赖 vs 窄依赖
scala
// 窄依赖 - 每个父分区最多被子分区使用一次
val mapRDD = rdd.map(x => (x._1, x.2 * 2)) // 一对一
val filterRDD = rdd.filter(._2 > 2) // 一对一
// 宽依赖 - 需要shuffle,父分区被多个子分区使用
val reduceRDD = rdd.reduceByKey(_ + ) // 需要shuffle
val groupRDD = rdd.groupByKey() // 需要shuffle
2. RDD持久化(Persistence)
scala
// 1. 基本持久化操作
val rdd = sc.textFile("data/largefile.txt")
.flatMap(.split(" "))
.map((_, 1))
// 第一次使用会计算并缓存
rdd.persist(StorageLevel.MEMORY_ONLY) // 内存缓存
// 或
rdd.cache() // 等同于persist(MEMORY_ONLY)
// 多次使用同一RDD
val count1 = rdd.count() // 第一次触发计算,缓存
val count2 = rdd.groupByKey().count() // 复用缓存数据
// 2. 不同存储级别对比
val level1 = StorageLevel.MEMORY_ONLY // 仅内存(默认)
val level2 = StorageLevel.MEMORY_AND_DISK // 内存溢出则磁盘
val level3 = StorageLevel.MEMORY_ONLY_SER // 内存序列化(节省空间)
val level4 = StorageLevel.DISK_ONLY // 仅磁盘
// 3. 手动清理缓存
rdd.unpersist() // 移除缓存
// 4. 检查点(Checkpoint)- 切断血缘
sc.setCheckpointDir("hdfs://checkpoint-dir")
rdd.checkpoint() // 保存到可靠存储,切断依赖链
持久化策略选择指南:
MEMORY_ONLY:小数据集,内存充足
MEMORY_AND_DISK:大数据集,避免重复计算
MEMORY_ONLY_SER:内存紧张,但需要CPU序列化
DISK_ONLY:数据太大,但计算代价高
- Spark UI监控与分析
访问Spark UI
bash
Local模式:默认 http://localhost:4040
多个应用顺序端口:4040, 4041, 4042...
监控关键指标:
scala
// 实战:编写一个稍复杂的作业来观察UI
object SparkMonitorDemo {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder()
.appName("Spark Monitoring Demo")
.master("local[*]")
.getOrCreate()
val sc = spark.sparkContext
// 1. 读取数据
val rdd = sc.textFile("data/sales.csv")
.mapPartitionsWithIndex((idx, iter) => {
println(s"Partition $idx processing")
iter.map(_.split(","))
})
// 2. 多次使用的RDD - 缓存优化
val parsedRDD = rdd.map(arr => (arr(0), arr(1).toDouble))
parsedRDD.cache()
// 3. 计算统计信息
val totalSales = parsedRDD.map(_._2).sum()
val avgSales = totalSales / parsedRDD.count()
// 4. 分组聚合(产生shuffle)
val categorySales = parsedRDD.reduceByKey(_ + _)
val topCategories = categorySales.sortBy(_._2, false).take(10)
// 5. 等待用户查看UI
println("Check UI at http://localhost:4040")
Thread.sleep(60000) // 暂停1分钟供查看UI
spark.stop()
}
}

浙公网安备 33010602011771号