feat:ChatGPT第 4 课:groupByKey、reduceByKey 与 Shuffle
This commit is contained in:
@@ -0,0 +1,42 @@
|
|||||||
|
package org.exercises.exercises4.demo
|
||||||
|
|
||||||
|
import org.apache.spark.{SparkConf, SparkContext}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* groupByKey、reduceByKey 与 Shuffle
|
||||||
|
*
|
||||||
|
* groupByKey
|
||||||
|
→ 相同 Key 的 Value 放到一起
|
||||||
|
|
||||||
|
reduceByKey
|
||||||
|
→ 相同 Key 的 Value 进行聚合
|
||||||
|
|
||||||
|
Shuffle
|
||||||
|
→ 为了让相同 Key 的数据重新分配到合适的分区
|
||||||
|
|
||||||
|
reduceByKey
|
||||||
|
→ 可以先进行本地聚合,再 Shuffle,通常能减少数据传输*/
|
||||||
|
object Demo1GroupByKeyAndReduceByKey {
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
val conf = new SparkConf()
|
||||||
|
.setAppName("Demo1")
|
||||||
|
.setMaster("local")
|
||||||
|
val sc = new SparkContext(conf)
|
||||||
|
val rdd = sc.parallelize(List(
|
||||||
|
("A", 1),
|
||||||
|
("B", 2),
|
||||||
|
("A", 3),
|
||||||
|
("B", 4),
|
||||||
|
("A", 5)
|
||||||
|
))
|
||||||
|
val resGroupByKey = rdd.groupByKey() // 为了让 A 的 Value 全部到一起,Spark 要 Shuffle。
|
||||||
|
// A → [1,3,5]
|
||||||
|
// B → [2,4]
|
||||||
|
println(resGroupByKey.collect().toList)
|
||||||
|
|
||||||
|
val resReduceByKey = rdd.reduceByKey(_ + _) //它可以先在本地做部分聚合,在 Shuffle 前进行本地聚合,减少需要传输的数据量。
|
||||||
|
println(resReduceByKey.collect().toList)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
package org.exercises.exercises4.demo
|
||||||
|
|
||||||
|
/*
|
||||||
|
* groupByKey:先把 Value 收集起来,再计算。
|
||||||
|
reduceByKey:能先算的地方先算,减少 Shuffle 后再计算。*/
|
||||||
|
|
||||||
|
import org.apache.spark.{SparkConf, SparkContext}
|
||||||
|
|
||||||
|
object Demo2GroupByKeyVsReduceByKey {
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
|
||||||
|
val conf = new SparkConf()
|
||||||
|
.setMaster("local[3]")
|
||||||
|
.setAppName("DemoGroupByKeyVsReduceByKey")
|
||||||
|
|
||||||
|
val sc = new SparkContext(conf)
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 1. 模拟 200 条数据
|
||||||
|
// =========================================================
|
||||||
|
//
|
||||||
|
// 100 个 A,每个 Value = 1
|
||||||
|
// 100 个 B,每个 Value = 2
|
||||||
|
//
|
||||||
|
// 最终:
|
||||||
|
// A = 1 + 1 + ... + 1 = 100
|
||||||
|
// B = 2 + 2 + ... + 2 = 200
|
||||||
|
//
|
||||||
|
val data =
|
||||||
|
(1 to 100).map(_ => ("A", 1)) ++
|
||||||
|
(1 to 100).map(_ => ("B", 2))
|
||||||
|
|
||||||
|
val rdd = sc.parallelize(data, 3)
|
||||||
|
|
||||||
|
println("========== 原始数据 ==========")
|
||||||
|
println("总数据量:" + rdd.count())
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 2. 查看三个 Partition 中的数据
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
println("\n========== 各 Partition 中的数据 ==========")
|
||||||
|
|
||||||
|
rdd.glom().collect().zipWithIndex.foreach {
|
||||||
|
case (partitionData, index) =>
|
||||||
|
println(s"Partition $index:${partitionData.toList}")
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 3. groupByKey
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
println("\n========== groupByKey ==========")
|
||||||
|
|
||||||
|
val groupByKeyResult = rdd
|
||||||
|
.groupByKey()
|
||||||
|
.mapValues(values => values.sum)
|
||||||
|
|
||||||
|
groupByKeyResult.collect().foreach {
|
||||||
|
case (key, value) =>
|
||||||
|
println(s"$key -> $value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// 4. reduceByKey
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
println("\n========== reduceByKey ==========")
|
||||||
|
|
||||||
|
val reduceByKeyResult = rdd
|
||||||
|
.reduceByKey(_ + _)
|
||||||
|
|
||||||
|
reduceByKeyResult.collect().foreach {
|
||||||
|
case (key, value) =>
|
||||||
|
println(s"$key -> $value")
|
||||||
|
}
|
||||||
|
|
||||||
|
sc.stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
package org.exercises.exercises4.exercises
|
||||||
|
/*
|
||||||
|
* Exercises1:groupByKey
|
||||||
|
创建:
|
||||||
|
List(
|
||||||
|
("A", 1),
|
||||||
|
("B", 2),
|
||||||
|
("A", 3),
|
||||||
|
("B", 4),
|
||||||
|
("A", 5)
|
||||||
|
)
|
||||||
|
使用:
|
||||||
|
groupByKey()
|
||||||
|
并打印结果。
|
||||||
|
你应该观察到类似:
|
||||||
|
(A,CompactBuffer(1,3,5))
|
||||||
|
(B,CompactBuffer(2,4))
|
||||||
|
注意顺序不一定固定。
|
||||||
|
* */
|
||||||
|
import org.apache.spark.{SparkConf, 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(
|
||||||
|
("A", 1),
|
||||||
|
("B", 2),
|
||||||
|
("A", 3),
|
||||||
|
("B", 4),
|
||||||
|
("A", 5)
|
||||||
|
))
|
||||||
|
val res = rdd.groupByKey()
|
||||||
|
println(res.collect().toList)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.exercises.exercises4.exercises
|
||||||
|
/*
|
||||||
|
* Exercises2:groupByKey + mapValues
|
||||||
|
还是上面的数据。
|
||||||
|
使用:
|
||||||
|
groupByKey()
|
||||||
|
然后计算每个 Key 的总和。
|
||||||
|
要求使用:
|
||||||
|
mapValues
|
||||||
|
最终:
|
||||||
|
(A,9)
|
||||||
|
(B,6)*/
|
||||||
|
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(
|
||||||
|
("A", 1),
|
||||||
|
("B", 2),
|
||||||
|
("A", 3),
|
||||||
|
("B", 4),
|
||||||
|
("A", 5)
|
||||||
|
))
|
||||||
|
val res = rdd.groupByKey().mapValues(_.sum)
|
||||||
|
println(res.collect().toList)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.exercises.exercises4.exercises
|
||||||
|
/*
|
||||||
|
* Exercises3:reduceByKey
|
||||||
|
还是相同数据。
|
||||||
|
直接:
|
||||||
|
reduceByKey(_ + _)
|
||||||
|
得到:
|
||||||
|
(A,9)
|
||||||
|
(B,6)*/
|
||||||
|
import org.apache.spark.{SparkConf, SparkContext}
|
||||||
|
|
||||||
|
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),
|
||||||
|
("A", 3),
|
||||||
|
("B", 4),
|
||||||
|
("A", 5)
|
||||||
|
))
|
||||||
|
val res = rdd.reduceByKey(_ + _)
|
||||||
|
println(res.collect().toList)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package org.exercises.exercises4.exercises
|
||||||
|
/*
|
||||||
|
* Exercises4:比较两种写法
|
||||||
|
分别计算下面数据:
|
||||||
|
List(
|
||||||
|
("A", 10),
|
||||||
|
("B", 20),
|
||||||
|
("A", 30),
|
||||||
|
("B", 40),
|
||||||
|
("A", 50)
|
||||||
|
)
|
||||||
|
方法一
|
||||||
|
groupByKey
|
||||||
|
→ mapValues(_.sum)
|
||||||
|
方法二
|
||||||
|
reduceByKey(_ + _)
|
||||||
|
两个结果都应该:
|
||||||
|
A → 90
|
||||||
|
B → 60
|
||||||
|
这题的重点不是结果,而是你自己观察:
|
||||||
|
groupByKey
|
||||||
|
中间会出现什么结构。*/
|
||||||
|
import org.apache.spark.{SparkConf, SparkContext}
|
||||||
|
|
||||||
|
object Exercises4 {
|
||||||
|
def main(args: Array[String]): Unit = {
|
||||||
|
val conf = new SparkConf()
|
||||||
|
.setAppName("Exercises4")
|
||||||
|
.setMaster("local")
|
||||||
|
val sc = new SparkContext(conf)
|
||||||
|
val rdd = sc.parallelize(List(
|
||||||
|
("A", 10),
|
||||||
|
("B", 20),
|
||||||
|
("A", 30),
|
||||||
|
("B", 40),
|
||||||
|
("A", 50)
|
||||||
|
))
|
||||||
|
val resGBK = rdd.groupByKey().mapValues(_.sum)
|
||||||
|
val resRBK = rdd.reduceByKey(_ + _)
|
||||||
|
println("groupByKey" + resGBK.collect().toList)
|
||||||
|
println("reduceByKey" + resRBK.collect().toList)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.exercises.exercises4.exercises
|
||||||
|
/*
|
||||||
|
* Exercises:综合题——WordCount
|
||||||
|
重新写一次 WordCount:
|
||||||
|
List(
|
||||||
|
"hello spark",
|
||||||
|
"hello scala",
|
||||||
|
"spark hello",
|
||||||
|
"scala spark"
|
||||||
|
)
|
||||||
|
要求:
|
||||||
|
flatMap
|
||||||
|
↓
|
||||||
|
map((_, 1))
|
||||||
|
↓
|
||||||
|
reduceByKey(_ + _)
|
||||||
|
↓
|
||||||
|
collect
|
||||||
|
最终:
|
||||||
|
hello → 3
|
||||||
|
spark → 3
|
||||||
|
scala → 2*/
|
||||||
|
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(
|
||||||
|
"hello spark",
|
||||||
|
"hello scala",
|
||||||
|
"spark hello",
|
||||||
|
"scala spark"
|
||||||
|
))
|
||||||
|
val res = rdd.flatMap(_.split(" "))
|
||||||
|
.map((_, 1))
|
||||||
|
.reduceByKey(_ + _)
|
||||||
|
println(res.collect().toList)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user