是一个 JVM 进程, 负责执行 Spark 任务的 main 方法
执行用户提交的代码,创建 SparkContext 或者 SparkSession
将用户代码转化为 Spark 任务(Jobs)创建血缘(Lineage),逻辑计划(Logical Plan)和物理计划(Physical Plan)
在 Cluster Manager 的辅助下,把 task 任务分发调度出去
跟踪任务的执行情况,收集日志
它是由 Spark driver 创建,每个 Spark 应用对应一个
程序和集群交互的入口
可以连接到 Cluster Manager
负责部署整个 Spark 集群
包括上面提到的 driver 和 executors
具有以下几种常见的部署模式: Standalone YARN Mesos Kubernetes
一个创建在 worker 节点的进程
一个 Executor 有多个 slots(线程)
一个 slot 就是一个线程,对应了一个 task
可以并发执行多个 tasks stage,stage就是可以并行的task
负责执行 Spark 任务,把结果返回给 Driver
可以将数据缓存到 worker 节点的内存
一个行为算子会触发一个job,一个job包含多个stage,一个stage是多个可以并行执行的task,task的数量跟分区数量有关,最后生成的文件也与task有关
弹性分布式数据集
使用起来类似Scala中的List集合,但是RDD中不保存数据,在大数据环境下,数据量比较大,不适合复制
五大特性
RDD是由一系列分区组成的
Task是作用在每个分区上的,每个分区至少需要一个Task
RDD之间是有一系列依赖关系的,可以按有无Shuflle分为:宽依赖、窄依赖
分区器是作用在KV格式的RDD上的
Spark为每个Task尽可能的提供最佳计算位置 ,移动计算,不移动数据
分区数:可以设置最小分区数,如果生成分区数超过最小分区数,则此时分区数为生成分区数,如果小于,则分区数为最小分区数。生成的分区数的数量与切片数量一致,一个文件至少对应一个分区。本地模式最小分区数为1,集群模式中最小分区数为2。一个分区对应一个任务
WordCount程序处理流程
该类型还可继续细分
基于Value数据类型的Transformation算子 例如:map、flatMap、filter等
基于Key-Value数据类型的Transfromation算子 例如:groupByKey、reduceByKey、join等
转换算子并不会触发提交作业,需要由Action算子触发执行 —— 懒执行
该类算子会触发 SparkContext 提交 Job 作业 一个Action算子对应一个Job
例如:foreach、count、take、collect、reduce等
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo03Map {
/**
* Spark中的基于RDD的方法可以称之为算子
* 算子可以分为两类:
* 1、转换算子:RDD与RDD直接的转换
* 2、行为算子:每一个行为算子就会触发一个Job
* 转换算子是懒执行的,如果没有行为算子触发,那么转换算子是不会被执行的
* 如何区分转换算子和行为算子?
* 观察调用算子之后返回的类型,如果是RDD则该算子是转换算子,如果是其他数据类型则该算子是行为算子
*/
def main(args: Array[String]): Unit = {
val list: List[Int] = List(1, 2, 3, 4, 5)
list.map(i => {
println("map方法被执行了")
i
}).foreach(println)
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo03Map")
val sc: SparkContext = new SparkContext(conf)
// 通过Scala中的集合构建RDD
val listRDD: RDD[Int] = sc.parallelize(list)
listRDD
/*
* map 方法需要接收一个函数f
* 函数f:Int => 自定义类型
* Int类型同RDD中的每一条数据的类型有关
* 函数f的返回值类型由自己决定
* 传入一条数据返回一条数据
*/
.map(i => {
println("RDD的map方法被执行了")
i
}).foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo04FlatMap {
def main(args: Array[String]): Unit = {
/**
* flatMap:转换算子,需要接收一个函数f
* 返回值类型有特殊要求,必须是数组或者是集合类
* 会对返回的数组或者是集合进行扁平化处理,即展开
* 传入一条数据返回多条数据
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo04FlatMap")
val sc: SparkContext = new SparkContext(conf)
val wordList: List[String] = List[String]("java,java,java", "scala,scala", "python")
val lineRDD: RDD[String] = sc.parallelize(wordList)
lineRDD.flatMap(line=>line.split(",")).foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo05Filter {
def main(args: Array[String]): Unit = {
/**
* filter:转换算子,可以实现对RDD的数据进行过滤
* 需要接受一个函数f,返回值类型必须是布尔类型
* 如果返回的是true,则保留数据
* 如果返回的是false,则过滤数据
*/
// 基于students.txt数据,过滤出理科五班的所有学生
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo05Filter")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
stuRDD.filter(stu => stu.split(",")(4) == "理科五班").foreach(println)
}
}
package com.shujia.core
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object Demo06Sample {
def main(args: Array[String]): Unit = {
/**
* sample:转换算子,用于对数据进行抽样
* 可以接收三个参数:
* withReplacement:有无放回
* fraction:抽样比例,小数,最终返回的数据条数并不固定,但是差别不大,在一个数量级
* seed:随机数种子,如果该值固定,则每次抽样的结果不变,默认等于随机的一个Long类型值
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo06Sample")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
// 对学生数据进行抽样,无放回,抽样10条数据
stuRDD.sample(withReplacement = false, fraction = 0.01, seed = 1).foreach(println)
}
}
package com.shujia.core
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object Demo07Union {
def main(args: Array[String]): Unit = {
/**
* union:转换算子,类似SQL中的union all
* 可以将两个同类型的RDD进行合并
* Spark中的union操作并不会对数据进行去重
* 如果需要去重可以使用distinct算子
*
* distinct:转换算子,可以对RDD的数据进行去重
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo07Union")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val stuRDD01: RDD[String] = stuRDD.sample(withReplacement = false, fraction = 0.01, seed = 1)
println(stuRDD01.getNumPartitions)
val stuRDD02: RDD[String] = stuRDD.sample(withReplacement = false, fraction = 0.01, seed = 1)
println(stuRDD02.getNumPartitions)
// union之后得到的RDD分区数等于两个RDD分区数之和
val unionRDD: RDD[String] = stuRDD01.union(stuRDD02)
println(unionRDD.getNumPartitions)
unionRDD.foreach(println)
val distinctUnionRDD: RDD[String] = unionRDD.distinct()
distinctUnionRDD.foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo08Cartesian {
def main(args: Array[String]): Unit = {
/**
* cartesian:转换算子
* 可以对两份数据做笛卡尔积
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo08Cartesian")
val sc: SparkContext = new SparkContext(conf)
val intList: List[Int] = List[Int](1, 2, 3, 4, 5)
val strList: List[String] = List[String]("a","b","c")
val intRDD: RDD[Int] = sc.parallelize(intList)
val strRDD: RDD[String] = sc.parallelize(strList)
val cartesianRDD: RDD[(Int, String)] = intRDD.cartesian(strRDD)
cartesianRDD.foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo09GroupBy {
def main(args: Array[String]): Unit = {
/**
* groupBy:转换算子
* 需要通过函数指定一个字段进行分组
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo09GroupBy")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[Stu] = sc
.textFile("spark/data/stu/students.txt")
.map(line => {
val splits: Array[String] = line.split(",")
Stu(splits(0), splits(1), splits(2).toInt, splits(3), splits(4))
})
// 按照班级分组
val stuGrpRDD: RDD[(String, Iterable[Stu])] = stuRDD.groupBy(_.clazz)
stuGrpRDD.foreach(println)
// 统计班级人数
stuGrpRDD
.map(kv => s"${kv._1},${kv._2.size}")
.foreach(println)
}
case class Stu(id: String, name: String, age: Int, gender: String, clazz: String)
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo10GroupByKey {
def main(args: Array[String]): Unit = {
/**
* groupByKey:转换算子
* 首先只有KV格式的RDD才能调用groupByKey算子
* 可以将KV格式的RDD按照Key进行分组
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo10GroupByKey")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[Stu] = sc
.textFile("spark/data/stu/students.txt")
.map(line => {
val splits: Array[String] = line.split(",")
Stu(splits(0), splits(1), splits(2).toInt, splits(3), splits(4))
})
// 将数据变成KV格式
// 按照班级分组
val stuKVRDD: RDD[(String, Stu)] = stuRDD.map(stu => (stu.clazz, stu))
val stuGrpRDD: RDD[(String, Iterable[Stu])] = stuKVRDD.groupByKey()
stuGrpRDD.foreach(println)
// 统计班级人数
stuGrpRDD
.map(kv => s"${kv._1},${kv._2.size}")
.foreach(println)
}
case class Stu(id: String, name: String, age: Int, gender: String, clazz: String)
}
package com.shujia.core
import com.shujia.core.Demo09GroupBy.Stu
import com.shujia.core.Demo10GroupByKey.Stu
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object Demo11ReduceByKey {
def main(args: Array[String]): Unit = {
/**
* reduceByKey:转换算子 需要接收一个聚合函数
* 首先只有KV格式的RDD才能调用reduceByKey算子
* 可以将KV格式的RDD按照Key进行分组并且同时进行聚合操作
* 聚合操作需要通过函数进行传入
* 同时也会将聚合函数用于预聚合
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo11ReduceByKey")
val sc: SparkContext = new SparkContext(conf)
// 按照班级分组并且统计班级人数
val stuKVRDD: RDD[(String, Int)] = sc
.textFile("spark/data/stu/students.txt")
// 将数据变成KV格式,以班级作为Key,1作为Value
.map(line => {
val splits: Array[String] = line.split(",")
(splits(4), 1)
})
stuKVRDD
.groupByKey()
.map(kv => {
val clazz: String = kv._1
// val cnt: Int = kv._2.size
val cnt: Int = kv._2.sum
s"$clazz,$cnt"
}).foreach(println)
// 使用reduceByKey进行简化
stuKVRDD
// reduceByKey无法直接完成avg聚合操作
// reduceByKey 可以实现预聚合操作
// .reduceByKey((i1: Int, i2: Int) => i1 + i2)
.reduceByKey(_ + _) // 基于匿名函数的省略规则进行简化
.map(kv => s"${kv._1},${kv._2}")
.foreach(println)
// 使用aggregateByKey统计班级的平均年龄
// stuKVRDD.aggregateByKey()
}
}
package com.shujia.core
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object Demo12SortBy {
def main(args: Array[String]): Unit = {
/**
* sortBy:转换算子,可以对RDD的数据进行排序
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo12SortBy")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[Stu] = sc
.textFile("spark/data/stu/students.txt")
.map(line => {
val splits: Array[String] = line.split(",")
Stu(splits(0), splits(1), splits(2).toInt, splits(3), splits(4))
})
// 按照班级降序排列
stuRDD.sortBy(_.clazz, ascending = false).foreach(println)
// 按照班级降序排列 再按照年龄升序排列
stuRDD.sortBy(stu=>stu.clazz + (1000-stu.age), ascending = false).foreach(println)
}
case class Stu(id: String, name: String, age: Int, gender: String, clazz: String)
}
package com.bigdata.core
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object DemoAggregateByKey {
def main(args: Array[String]): Unit = {
//转换算子:按照指定的计算逻辑,对key分组后进行预聚合以及聚合
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("DemoMap")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
//取出每行
val stuInfoRDD: RDD[(String, Int)] = stuRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(4), 1)
})
//取出年龄班级
val stuAgeRDD: RDD[(String, Int)] = stuRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(4), splits(2).toInt)
})
/*
aggregateByKey接收俩组参数
第一组参数zeroValue:初始化的值,类型基于值推断,可以指定任意的值
第二组参数需要接受俩个函数:seqOp,combOp
seqOp:预聚合函数,作用在MapTask内部
combOp:聚合函数,作用在ReduceTask内部
*/
// stuInfoRDD.aggregateByKey( 0)((i1,i2)=>i1+i2,(u1,u2)=>u1+u2).foreach(println)
//通过reduce求平均年龄
// 先统计班级人数
val clazzCntRDD: RDD[(String, Int)] = stuInfoRDD.reduceByKey(_ + _)
// 统计班级总年龄
val clazzAgeCnt: RDD[(String, Int)] = stuAgeRDD.reduceByKey(_ + _)
val clazzAgeNum: RDD[(String, (Int, Int))] = clazzCntRDD.join(clazzAgeCnt)
clazzAgeNum.map(kv=>{
(kv._1,kv._2._2/kv._2._1.toDouble)
}).foreach(println)
println("*"*100)
/*通过aggregateByKey实现*/
stuAgeRDD.aggregateByKey((0,0))((t2,age)=>{
val sumAge: Int = t2._1+age
val clazzCnt: Int = t2._2 + 1
(sumAge,clazzCnt)
},(sumAgeAndClazzCnt01,sumAgeAndClazzCnt02)=>{
val totalSumAge: Int = sumAgeAndClazzCnt01._1+sumAgeAndClazzCnt02._1
val totalCnt: Int = sumAgeAndClazzCnt01._2+sumAgeAndClazzCnt02._2
(totalSumAge,totalCnt)
}).map(kv => {
(kv._1, kv._2._1 / kv._2._2.toDouble)
}).foreach(println)
//使用reduceByKey优化aggregateByKey
val totalRDD: RDD[(String, (Int, Int))] = stuRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(4), (splits(2).toInt, 1))
}).reduceByKey((t1, t2) => {
val reduceAge: Int = t1._1 + t2._1
val reduceCnt: Int = t1._2 + t2._2
(reduceAge, reduceCnt)
})
totalRDD.map(kv => {
(kv._1, kv._2._1 / kv._2._2.toDouble)
}).foreach(println)
}
}
package com.bigdata.core
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object DemoJoin {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("DemoJoin")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val scoRDD: RDD[String] = sc.textFile("spark/data/stu/score.txt")
val stuNameAge: RDD[(String, (String, Int))] = stuRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(0), (splits(1), splits(2).toInt))
})
val sumScoreRDD: RDD[(String, Int)] = scoRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(0), splits(2).toInt)
}).reduceByKey(_ + _)
sumScoreRDD.join(stuNameAge).map{
case (id: String, (sumScore: Int,(name: String, age: Int))) =>
s"$id,$name,$age,$sumScore"
}.foreach(println)
//左连接
stuRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(0), (splits(1), splits(2).toInt))
}).leftOuterJoin(sumScoreRDD).map {
case (id: String, ((name: String, age: Int), mayBeSumScore: Option[Int])) =>
mayBeSumScore match {
case Some(sumScore) =>
s"$id,$name,$age,$sumScore"
case None =>
s"$id,$name,$age,0"
}
}.foreach(println)
}
}
package com.bigdata.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
object DemoMapPartitions {
def main(args: Array[String]): Unit = {
/**
* Spark的代码是需要以分布式的环境进行运行的
* 虽然代码都好像是写在main方法当中,但实际上应该可以分为两个部分:算子内部、算子外部
* 算子内部是以Task的形式发送到Executor中去执行的
* 算子外部是在Driver端执行的
* 所以在算子内部如果使用了算子外部定义的变量,则该变量必须实现序列化
* 而MySQL连接是不能被序列化的,所以就会引发问题 Task not serializable
* Caused by: java.io.NotSerializableException: com.mysql.jdbc.JDBC42PreparedStatement
*
*/
val blackList: List[String] = List[String]("1500100777", "1500100888", "1500100999")
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo15MapPartitions")
val sc: SparkContext = new SparkContext(conf)
val blackRDD: RDD[String] = sc.parallelize(blackList)
/** *
* 如果把MySQL相关的操作都放入Task,虽然可以解决Task没有被序列化的问题,但是又会导致性能问题
* 因为来一条数据,就需要建立一次连接,频繁地创建销毁连接代价太大了
* 就需要借助mapPartitions来优化性能
*/
blackRDD.mapPartitions(iter=> {
//与数据库建立连接
val conn: Connection = DriverManager.getConnection("jdbc:mysql://rm-bp1h7v927zia3t8iwho.mysql.rds.aliyuncs.com:3307/stu023", "shujia23", "123456")
val pst: PreparedStatement = conn.prepareStatement("select id,name,age,gender,clazz from student where id = ?")
iter.map(id=>{
var returnStr = ""
pst.setString(1, id)
val rs: ResultSet = pst.executeQuery()
while (rs.next()){
val id: String = rs.getString("id")
val name: String = rs.getString("name")
val age: String = rs.getString("age")
val gender: String = rs.getString("gender")
val clazz: String = rs.getString("clazz")
returnStr = s"$id,$name,$age,$gender,$clazz"
}
returnStr
})
}).foreach(println)
/**
* 对每个RDD的分区进行处理
* 一个分区一般对应一个切片,一个切片一般会有多条数据
* 多条数据在Spark中一般会通过Iterator类型进行接收
*
* 由于Task是作用在每一个分区上的,mapPartitions算子内部的代码会被执行的次数,由分区数决定
* 大大减少连接建立的次数,提高性能
*
* 适用于需要跟外部数据源建立连接,并且从外部数据源获取数据,然后有后续处理时就可以使用mapPartitions,减少连接建立的次数
*
* 类似的:foreachPartition
* 适用于需要跟外部数据源建立连接,将数据写入外部数据源或者是并且从外部数据源获取数据,但没有后续处理时就可以使用foreachPartition,减少连接建立的次数
*/
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo16MapValues {
def main(args: Array[String]): Unit = {
/**
* mapValues:转换算子,需要作用在KV格式的RDD上,可以对Value进行处理
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo16MapValue")
val sc: SparkContext = new SparkContext(conf)
val kvRDD: RDD[(String, Int)] = sc.parallelize(List(("k1", 1), ("k2", 2), ("k3", 3)))
// 将value做次方返回新的value
kvRDD.mapValues(v => v * v * v).foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo17CoGroup {
def main(args: Array[String]): Unit = {
/**
* cogroup:转换算子,需要作用在KV格式的RDD上
* 会按照相同的Key对两个RDD进行关联操作
* 如果未关联上,则置为空,所有的数据都会保留
* 实际上相当于full join操作
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo17CoGroup")
val sc: SparkContext = new SparkContext(conf)
val kvRDD01: RDD[(String, Int)] = sc.parallelize(List(("k1", 1), ("k2", 2), ("k3", 3)))
val kvRDD02: RDD[(String, Int)] = sc.parallelize(List(("k2", 22), ("k3", 33), ("k4", 44)))
kvRDD01.cogroup(kvRDD02).foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo18Action {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo18Action")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
// foreach:类似map操作,但没有返回值
stuRDD.foreach(println)
// take:取n条数据 转换成Scala的数据Array
// 这里调用的foreach操作实际上是由Array数组提供的
stuRDD.take(10).foreach(println)
// collect:将RDD所有的数据转换成Scala的Array
stuRDD.collect().foreach(println)
stuRDD.map(line => (line.split(",")(4), 1)).collect().toMap.foreach(println)
// reduce:把数据整体看成一组,进行聚合操作
val sumAge: Int = stuRDD.map(line => line.split(",")(2).toInt).reduce(_ + _)
val cnt: Int = stuRDD.map(line => 1).reduce(_ + _)
println(sumAge / cnt.toDouble)
// count:统计RDD数据量
println(stuRDD.count())
// reduceByKeyLocally:相当于reduceByKey+collect+toMap
val clazzCntMap: collection.Map[String, Int] = stuRDD.map(line => (line.split(",")(4), 1)).reduceByKeyLocally(_ + _)
clazzCntMap.foreach(println)
val clazzCntMap02: Map[String, Int] = stuRDD.map(line => (line.split(",")(4), 1)).reduceByKey(_ + _).collect().toMap
clazzCntMap02.foreach(println)
// lookup:作用在KV格式的RDD上,需要指定一个Key,可以将Key对应的所有的Value取出来,并且构建成Scala中的本地集合
println(stuRDD.map(line=>(line.split(",")(4), 1)).lookup("文科一班"))
// 保存文件相关的:saveAsTextFile、saveAsObjectFile
}
}
package com.bigdata.core
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
object DemoMapJoin {
/**
* 通过mapjoin完成map任务实现join操作
* 适用于大表关联小表
* 原理:将小表进行广播,广播给每一个map任务
* @param args
*/
def main(args: Array[String]): Unit = {
//需求:统计学生总分 输出为id,name,clazz,sumScore
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo19MapJoin")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc
.textFile("spark/data/stu/students.txt")
val scoreRDD: RDD[String] = sc
.textFile("spark/data/stu/score.txt")
//求出每个学生的总分
val sumScoreRDD: RDD[(String, Int)] = scoreRDD.map(line => {
val splits: Array[String] = line.split(",")
(splits(0), splits(2).toInt)
}).reduceByKey(_ + _)
//不可以在RDD中嵌套RDD,故需要先将RDD转换成map
val sumScoreMap: Map[String, Int] = sumScoreRDD.collect().toMap
//在此处通过map的getOrElse来获取上部中被转化的map中的学生总分
stuRDD.map(line=>{
val splits: Array[String] = line.split(",")
val id: String = splits(0)
val sumScore: Int = sumScoreMap.getOrElse(id, 0)
s"$id,${splits(1)},${splits(4)},$sumScore"
}).foreach(println)
}
}
package com.shujia.core
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import java.sql.{Connection, DriverManager, PreparedStatement}
object Demo20ForeachPartition {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo20ForeachPartition")
val sc: SparkContext = new SparkContext(conf)
// 统计班级人数,将最后的结果保存到MySQL中
val clazzCntRDD: RDD[(String, Int)] = sc.textFile("spark/data/stu/students.txt")
.map(line => (line.split(",")(4), 1))
.reduceByKey(_ + _)
clazzCntRDD
// 使用foreach操作同样会出现 连接建立过多的问题 性能比较低
.foreach(t2 => {
val clazz: String = t2._1
val cnt: Int = t2._2
// 建立连接
val conn: Connection = DriverManager.getConnection("jdbc:mysql://rm-bp1h7v927zia3t8iwho.mysql.rds.aliyuncs.com:3307/stu023", "shujia23", "123456")
// 创建Statement
val pSt: PreparedStatement = conn.prepareStatement("insert into clazz_cnt(clazz,cnt) values (?,?)")
// 设置参数
pSt.setString(1, clazz)
pSt.setInt(2, cnt)
// 如果有大量数据插入时,可以使用批量插入
pSt.addBatch() // 将每一次插入放入Batch中
pSt.executeBatch() // 执行批量插入
})
// 使用foreachPartition大大减少连接建立的次数
clazzCntRDD
.foreachPartition(iter => {
// 建立连接
val conn: Connection = DriverManager.getConnection("jdbc:mysql://rm-bp1h7v927zia3t8iwho.mysql.rds.aliyuncs.com:3307/stu023", "shujia23", "123456")
// 创建Statement
val pSt: PreparedStatement = conn.prepareStatement("insert into clazz_cnt(clazz,cnt) values (?,?)")
iter.foreach(t2 => {
val clazz: String = t2._1
val cnt: Int = t2._2
// 设置参数
pSt.setString(1, clazz)
pSt.setInt(2, cnt)
// 如果有大量数据插入时,可以使用批量插入
pSt.addBatch() // 将每一次插入放入Batch中
})
pSt.executeBatch() // 执行批量插入
})
}
}