feat:ChatGPT第 5 课:Partition 分区

This commit is contained in:
2026-09-18 01:07:06 +08:00
parent b5dd87fd84
commit d1166381b1
9 changed files with 459 additions and 0 deletions
@@ -0,0 +1,62 @@
package org.exercises.exercises5.demo
/*
* Spark 第 5 课:Partition 分区*/
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo1PartitionBasic {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("Demo1PartitionBasic").setMaster("local")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8), 2)
println("=============获取分区===========")
println("=============获取分区===========")
println(rdd.getNumPartitions) // 2
println("=============查看分区的数据===========")
val res = rdd.glom()
println(res.collect().toList) // List([I@7d755813, [I@69c93ca4)
// 分区 0: 1, 2, 3, 4
// 分区 1: 5, 6, 7, 8
printPartition(res)
println("=============Transformation 一般会不会改变 Partition 数?===========")
println("=============Transformation 一般会不会改变 Partition 数?===========")
val rddMap = sc.parallelize(
List(1, 2, 3, 4, 5, 6),
2
)
val rddMap2 = rddMap.map(_ * 10)
// Partition 0
// 1 2 3
// ↓ map
// 10 20 30
//
// Partition 1
// 4 5 6
// ↓ map
// 40 50 60
// map 是在原有 Partition 中进行转换
println(s"map前:${rddMap.getNumPartitions}")
println(s"map后:${rddMap2.getNumPartitions}")
println(rddMap2.toDebugString)
// map
// filter
// flatMap
// 这类算子通常属于窄依赖,数据不需要大规模跨 Partition 重分配。
sc.stop()
}
private def printPartition(res: RDD[Array[Int]]): Unit = {
res.collect().zipWithIndex.foreach {
case (data, index) =>
println(s"分区 $index: ${data.mkString(", ")}")
}
}
}
@@ -0,0 +1,79 @@
package org.exercises.exercises5.demo
/*
* 什么操作会导致 Shuffle?*/
import org.apache.spark.{SparkConf, SparkContext}
object Demo2Shuffle {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("ShuffleDemo")
.setMaster("local[2]")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(
List(
("A", 1),
("B", 2),
("A", 3),
("B", 4),
("A", 5),
("B", 6)
),
2
)
// 查看原始 RDD 每个 Partition 里面的数据
println("========== 原始 RDD ==========")
rdd.glom().collect().zipWithIndex.foreach {
case (data, index) =>
println(s"Partition $index: ${data.toList}")
}
// ==========================
// reduceByKey
// ==========================
println("\n========== reduceByKey ==========")
val reduceRdd = rdd.reduceByKey(_ + _, 2)
println("reduceByKey 后的 Partition:")
reduceRdd.glom().collect().zipWithIndex.foreach {
case (data, index) =>
println(s"Partition $index: ${data.toList}")
}
println("\nreduceByKey 的依赖关系:")
println(reduceRdd.toDebugString)
// ==========================
// groupByKey
// ==========================
println("\n========== groupByKey ==========")
val groupRdd = rdd.groupByKey(2)
println("groupByKey 后的 Partition:")
groupRdd.glom().collect().zipWithIndex.foreach {
case (data, index) =>
println(s"Partition $index: ${data.toList}")
}
println("\ngroupByKey 的依赖关系:")
println(groupRdd.toDebugString)
sc.stop()
}
}
@@ -0,0 +1,26 @@
package org.exercises.exercises5.demo
/*
* 现在我们开始主动控制 PairRDD 的分区方式。*/
import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}
object Demo3PartitionBy {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("Demo3PartitionBy")
.setMaster("local")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(
List(
("A", 1),
("B", 2),
("C", 3),
("D", 4)
), 5
)
println("partitionByq前:" + rdd.getNumPartitions)
val result = rdd.partitionBy(new HashPartitioner(2))
println("partitionByq后:" + result.getNumPartitions)
}
}
@@ -0,0 +1,39 @@
package org.exercises.exercises5.demo
/*
* 奇数偶数分区*/
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
object Demo4MyPartitioner {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("MyApp")
.setMaster("local")
val sc = new SparkContext(conf)
val pairRDD = sc.parallelize(
List(
(1, "A"),
(2, "B"),
(3, "C"),
(4, "D"),
(5, "E"),
(6, "F")
), 5
)
val result = pairRDD.partitionBy(new MyPartitioner)
println("partitionBy前:" + pairRDD.getNumPartitions)
println("partitionBy后:" + result.getNumPartitions)
result.glom().collect().foreach(x => println(x.mkString(",")))
}
}
class MyPartitioner extends Partitioner {
override def numPartitions: Int = 2
override def getPartition(key: Any): Int = {
key.asInstanceOf[Int] % 2
}
}
@@ -0,0 +1,34 @@
package org.exercises.exercises5.exercises
/*
* Exercises1:观察 Partition
创建:
List(1, 2, 3, 4, 5, 6, 7, 8)
并且指定:
4 个 Partition
要求打印:
RDD 的 Partition 数量
每个 Partition 中的数据
提示使用:
getNumPartitions()
glom()
collect()*/
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
object Exercises1 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("Exercises1")
.setMaster("local")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8), 4)
println(rdd.getNumPartitions)
rdd.glom().collect().zipWithIndex.foreach {
case (data, index) =>
println(s"Partition $index: ${data.toList}")
}
sc.stop()
}
}
@@ -0,0 +1,28 @@
package org.exercises.exercises5.exercises
/*
* Exercises2:观察 map 是否改变 Partition
创建:
List(1,2,3,4,5,6,7,8)
4 个 Partition。
然后:
val result = rdd.map(_ * 10)
打印:
原 RDD Partition 数
result Partition 数
要求验证:
map 前后 Partition 数是否相同。*/
import org.apache.spark.{SparkConf, SparkContext}
object Exercises2 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("Exercises2")
.setMaster("local")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List(1, 2, 3, 4, 5, 6, 7, 8), 4)
val result = rdd.map(_ * 10)
println(rdd.getNumPartitions)
println(result.getNumPartitions)
sc.stop()
}
}
@@ -0,0 +1,46 @@
package org.exercises.exercises5.exercises
import org.apache.spark.{HashPartitioner, SparkConf, SparkContext}
/*
* Exercises3:使用 partitionBy
创建:
List(
("A", 1),
("B", 2),
("C", 3),
("D", 4),
("E", 5),
("F", 6)
)
转换为 PairRDD。
使用:
new HashPartitioner(2)
进行:
partitionBy
然后使用:
glom()
观察:
数据最终进入了哪个 Partition。*/
object Exercises3 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("Exercises3")
.setMaster("local")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List(
("A", 1),
("B", 2),
("C", 3),
("D", 4),
("E", 5),
("F", 6)
))
val res = rdd.partitionBy(new HashPartitioner(2))
res.glom().collect().zipWithIndex.foreach {
case (value, index) =>
println(s"Partition $index: ${value.mkString("Array(", ", ", ")")}")
}
sc.stop()
}
}
@@ -0,0 +1,68 @@
package org.exercises.exercises5.exercises
/*
* Exercises4:自定义奇偶分区
定义:
class MyPartitioner extends Partitioner
要求:
偶数 → Partition 0
奇数 → Partition 1
数据:
List(
(1, "one"),
(2, "two"),
(3, "three"),
(4, "four"),
(5, "five"),
(6, "six")
)
使用:
partitionBy(new MyPartitioner)
然后通过:
glom()
观察结果。
预期:
Partition 0:
(2,two)
(4,four)
(6,six)
Partition 1:
(1,one)
(3,three)
(5,five)*/
import org.apache.spark.{Partitioner, SparkConf, SparkContext}
object Exercises4 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setMaster("local")
.setAppName("Exercises4")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List(
(1, "one"),
(2, "two"),
(3, "three"),
(4, "four"),
(5, "five"),
(6, "six")
))
val res = rdd.partitionBy(new MyPartitioner())
res.glom().collect().zipWithIndex.foreach {
case (arr, index) => println(s"分区 $index: ${arr.mkString(", ")}")
}
sc.stop()
}
}
// 一共有几个分区?
// 数据怎么分?
class MyPartitioner extends Partitioner {
// 我的 RDD 一共有 2 个分区
override def numPartitions: Int = 2
override def getPartition(key: Any): Int = {
key.asInstanceOf[Int] % 2
}
}
@@ -0,0 +1,77 @@
package org.exercises.exercises5.exercises
/*
* Exercises25:综合题——观察 Shuffle
创建:
List(
("A", 1),
("B", 2),
("A", 3),
("B", 4),
("A", 5),
("C", 6)
)
要求完成两个版本。
版本一
groupByKey
然后观察:
glom()
版本二
reduceByKey(_ + _)
然后观察:
glom()
最后打印:
Partition 数量
Partition 中的数据
最终结果
你不需要通过代码证明 Shuffle 的内部每一个细节,重点观察:
groupByKey
→ 分组后的数据
reduceByKey
→ 聚合后的数据
并思考:
为什么两个算子的最终结果类似,但 Shuffle 阶段处理的数据量可能不同?*/
import org.apache.spark.{SparkConf, SparkContext}
object Exercises5 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("Exercises5")
.setMaster("local")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List(
("A", 1),
("B", 2),
("A", 3),
("B", 4),
("A", 5),
("C", 6)
), 4)
println("=============原始数据==============")
println(rdd.getNumPartitions)
rdd.glom().collect().zipWithIndex.foreach {
case (value, index) => println(s"Partition $index: ${value.toList}")
}
println("=============reduceByKey==============")
val resRBK = rdd.reduceByKey(_ + _)
println(resRBK.getNumPartitions)
resRBK.glom().collect().zipWithIndex.foreach {
case (value, index) => println(s"Partition $index: ${value.toList}")
}
println("=============groupByKey==============")
val resGBK = rdd.groupByKey()
println(resGBK.getNumPartitions)
resGBK.glom().collect().zipWithIndex.foreach {
case (value, index) => println(s"Partition $index: ${value.toList}")
}
sc.stop()
}
}