读取数据,得到第一个RDD
处理数据,RDD之间的转化
保存数据,将RDD保存到存储系统。
Transformation算子(转换算子):由一个RDD转化成另一个RDD,转换算子(懒执行)并不会自己执行,需要行为算子进行触发执行。
Action算子(行为算子):可以出发Spark的Job,一个Action算子对应一个Job
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo01Map {
def main(args: Array[String]): Unit = {
//获取spark的配置文件
val conf: SparkConf = new SparkConf()
//配置Spark任务的名称
conf.setAppName("Demo01Map")
//配置Spark的运行模式
conf.setMaster("local")
//搭建Spark的运行环境
val sc: SparkContext = new SparkContext(conf)
//读取文件数据
val lineRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
// lineRDD.foreach(println)
/**
* Map算子操作:与List是一致的,输入一条数据返回一条数据
*需要接受一个函数:函数的参数类型:与RDD中每条数据数据类型保持一致=> 函数体:类型自定义
*/
//需求:取出姓名以及班级
val nameAndClazz: RDD[(String, String)] = lineRDD.map(kv => {
val splits: Array[String] = kv.split(",")
val name: String = splits(2)
val calzz: String = splits(4)
(name, calzz)
})
/**
*foreach算子:行为算子,可以触发Spark的Job
*与Map算子类似,区别是在与一个有返回值,另一个没有返回值
*/
nameAndClazz.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo02flatMap {
def main(args: Array[String]): Unit = {
//获取Spark的配置文件
val conf: SparkConf = new SparkConf()
//设置Spark的任务名称
conf.setAppName("Demo02flatMap")
//设置Spark的运行模式
conf.setMaster("local")
//获取Spark的运行环境
val sc: SparkContext = new SparkContext(conf)
//读取数据文件
val stuRDD: RDD[String] = sc.textFile("spark/data/words.txt")
val words: RDD[String] = stuRDD.flatMap(kv => {
kv.split(",")
})
words.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo03MapValues {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo03MapValues")
val sc: SparkContext = new SparkContext(conf)
/**
* mapValues:转换算子,首先需要构建一个kv格式的RDD,然后通过该算子对kv的v继续操作
*
*/
//将学生的姓名和年龄取出来,然后将年龄减一
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val nameAndAgeRDD: RDD[(String, Int)] = stuRDD.map(kv => {
val splits: Array[String] = kv.split(",")
val name: String = splits(1)
val age: String = splits(2)
val age1: Int = age.toInt
(name,age1)
})
val nameAndRDD: RDD[(String, Int)] = nameAndAgeRDD.mapValues(kv => {
kv - 1
})
nameAndRDD.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo04Filter {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("Demo04Filter")
conf.setMaster("local")
val sc: SparkContext = new SparkContext(conf)
/**
* filter:转换算子
* 需要接收一个函数,函数的的参数类型与RDD中的每一条数据的类型一致=> 返回值类型是布尔类型的、
* 如果返回是true,就保留数据,反之则过滤数据
*/
//需求:过滤出年龄大于23的学生
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val ageRDD: RDD[String] = stuRDD.filter(kv => {
kv.split(",")(2).toInt > 23
})
ageRDD.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo05SortBy {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo05SortBy")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
/**
* sortBy:转换算子,用于排序
* 需要接收一个函数f用于指定按照什么进行排序
* 默认是按照升序进行排序,可以通过ascending参数来控制升降序,false表示的是降序
*/
val sortByRDD: RDD[String] = stuRDD.sortBy(c => {
c.split(",")(2).toInt
}, false)
sortByRDD.foreach(println)
//需求:先按照age升序,然后再按照id降序进行排序
val ageAndIdRDD: RDD[String] = stuRDD.sortBy(k => {
val age: String = k.split(",")(2)
val id: String = k.split(",")(0)
age + (999999999L - id.toInt).toString
})
ageAndIdRDD.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo06GroupBy {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo06GroupBy")
val sc: SparkContext = new SparkContext(conf)
/**
* groupBy:转换算子
* groupBy:可以作用在任意给格式的的RDD上
*groupBy:需要指定分组的条件,最后分完组之后,每一组数据是分组前的完整的每条数据,是将分组前的每一条数据完整的拉到对应的组中
*/
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
//需求:统计班级的人数
val clazzRDD: RDD[String] = stuRDD.map(kv => {
val clazz: String = kv.split(",")(4)
clazz
})
val groupRDD: RDD[(String, Iterable[String])] = clazzRDD.groupBy(k => k)
val lineRDD: RDD[(String, Int)] = groupRDD.map(kv => {
val clazzName: String = kv._1
val groupSize: Int = kv._2.size
(clazzName, groupSize)
})
lineRDD.foreach(println)
//使用groupBy求出每个班级的平均年龄
stuRDD.map(kv=> {
val clazz: String = kv.split(",")(4)
val age: Int = kv.split(",")(2).toInt
(clazz, age)
})
.groupBy(k=>{k._1})
.map(kv=>s"${kv._1},${kv._2.map(kv1=>kv1._2).sum.toDouble /kv._2.size}")
.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo07GroupByKey {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo07GroupByKey")
val sc: SparkContext = new SparkContext(conf)
/**
* groupByKey:转换算子
* groupByKey:不需要指定分组条件,会自动的默认按照key进行分组,得到的数据只由value组成
* groupByKey:只能作用在kv形式的RDD
* groupBYKey后续还可以继续进行聚合
*/
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val mapRDD: RDD[(String, Int)] = stuRDD.map(kv => {
val clazz: String = kv.split(",")(4)
(clazz, 1)
})
val keyRDD: RDD[(String, Iterable[Int])] = mapRDD.groupByKey()
val countRDD: RDD[(String, Int)] = keyRDD.map(kv => {
val clazz: String = kv._1
val count: Int = kv._2.size
(clazz, count)
})
countRDD.foreach(println)
//统计班级的平均年龄
stuRDD.map(kv=>{
val age: Int = kv.split(",")(2).toInt
val clazz: String = kv.split(",")(4)
(clazz,age)
})
.groupByKey()
.map(kv=>{
val clazz: String = kv._1
val avg: Double = kv._2.sum.toDouble / kv._2.size
s"${clazz},${avg}"
})
.foreach(println)
}
}
groupBy与groupKeyBy的区别:
1、groupBy可以作用在任意形式的RDD,然而对于groupKeyBy来说,只能作用在kv形式的RDD
2、groupBy在进行分组的时候需要指定分组的条件,但是groupKeyBy在分组的时候不需要指定分组条件,会自动根据key继续分组
3、groupBy分组结束以后,组中的数据都是都是分组前的每一条完整的数据,对于groupKeyBy来说,分组结束后得到的数据是由kv形式的RDD中的value组成。
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo08ReduceByKey {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo08ReduceByKey")
val sc: SparkContext = new SparkContext(conf)
/**
* reduceByKey:转换算子
* 既可以按照key进行分组,又可以对value进行聚合,还可以在reduce之前也是就是在map阶段进行预聚合
* 就是根据key进行分组聚合,然后再进行累加
*/
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
//统计班级的人数
stuRDD.map(kv=>{
val clazz: String = kv.split(",")(4)
(clazz,1)
})
.reduceByKey((i1,i2)=>{
i1+i2
})
.foreach(println)
//统计班级的平均年龄
/**
* 对于预聚合是用于sum,count,max,min,avg(对于平均的这里不能使用)这五种
*/
//解决方法:就是先求出人数和年龄总和,应为reduceByKey只使用在kv形式的RDD,所以将value设置成一个二元组用来存放年龄和人数
stuRDD
.map(kv=>{
val clazz: String = kv.split(",")(4)
val age: Int = kv.split(",")(2).toInt
(clazz,(age,1))
})
.reduceByKey((i1,i2)=>{
val ageSum: Int = i1._1+i2._1
val count: Int = i1._2 + i2._2
(ageSum,count)
})
.map(kv=>{
val clazz: String = kv._1
val avg: Double = kv._2._1.toDouble/kv._2._2
(clazz,avg)
})
.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo09AggregateByKey {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo08ReduceByKey")
val sc: SparkContext = new SparkContext(conf)
/**
* zeroValue:U 初始化的值,类型自定义
* seqOp:(U,KV格式的RDD的V的类型)=>U 预聚合的操作
* combOp:(U,U)=>U 聚合的操作
*
*(zeroValue:U )(seqOp:((U,KV格式的RDD的V的类型)=>U,combOp:(U,U)=>U))
*
* 同reduceByKey的区别在于:
* 两者都能做预聚合,只不过在aggregateByKey中将聚合以及预聚合操作分开来了,可以单独定义
* reduceByKey相当于聚合和预聚合操作共用相同的计算逻辑
* 也只是用于kv形式的RDD
* 当预聚合操作和聚合操作不一致时,可以使用aggregateByKey
*/
//统计班级人数
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
stuRDD
.map(kv => {
val clazz: String = kv.split(",")(4)
(clazz, 1)
})
.aggregateByKey(0)(seqOp = (z, v) => {
z + v
}, combOp = (i1, i2) => {
i1 + i2
})
.foreach(println)
//统计班级的平均年龄
stuRDD.map(kv=>(kv.split(",")(4),kv.split(",")(2).toDouble))
//因为求平均年龄是没有办法进行预聚合的,所以可以通过求出总数和个数然后间接的求出平均,
// 所以我们需要求出两个参数,sum和count,应为需要接受两个参数,所以需要一个二元组来接受sum和count这两个变量
/**
* zeroValue:U 设置初始值,设置sum的初始值时0.0,double类型的,设置count的初始值是0类型是int类型的
*/
.aggregateByKey((0.0,0))(seqOp = (u,age)=>{
//预编译阶段,相当于是在map阶段的操作,取出第一个参数加上age,求出预编译阶段的sum(age),取出第二个参数,统计预编译阶段的人数
(u._1+age,u._2+1)
},combOp = (i1,i2)=>{
//聚合阶段:此时的数据是某一个完成预编译map阶段的数据,年龄和人数
(i1._1+i2._1,i2._2+i1._2)
})
.map(kv=>{
s"${kv._1},${kv._2._1/kv._2._2}"
})
.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo10Join {
def main(args: Array[String]): Unit = {
/**
* join :转换算子,只能作用在kv形式的RDD中,并且表的关联使用的是key来做关联
*/
//加载学生表以及学生的分数表合并
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo10Join")
val sc: SparkContext = new SparkContext(conf)
//读取学生的信息,将学生的学号设置成key,姓名设置成value
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val stu1RDD: RDD[(String, String)] = stuRDD.map(kv => (kv.split(",")(0), kv))
//读取学生的成绩表,将学号设置成key,分数设置成value
val scoreRDD: RDD[String] = sc.textFile("spark/data/stu/score.txt")
val score1RDD: RDD[(String, String)] = scoreRDD.map(kv => (kv.split(",")(0), kv))
val joinRDD: RDD[(String, (String, String))] = stu1RDD.join(score1RDD)
//(1500100488,(1500100488,丁鸿骞,22,男,理科四班,1500100488,1000008,99))
// 需求:对于上面的数据,只保留id,姓名,age,clazz,科目,分数
//此时要注意的是:key依旧是学生id,这个返回的value是一个二元组
joinRDD.map(kv=>{
val stuId: String = kv._1
val stuName: String = kv._2._1.split(",")(1)
val stuAge: String = kv._2._1.split(",")(2)
val stuGender: String = kv._2._1.split(",")(3)
val stuClazz: String = kv._2._1.split(",")(4)
val subject: String = kv._2._2.split(",")(1)
val score: String = kv._2._2.split(",")(2)
(stuId,stuName,stuAge,stuClazz,stuGender,subject,score)
})
//使用左关联
//(1500100843,(1500100843,管鸿达,21,男,文科五班,Some(1500100843,1000003,79)))
//返回值类型:是一个二元组,后面的value返回的返回值也是一个二元组,保持左边的表完整性,当匹配的值没有的时候就返回None。
val leftRDD: RDD[(String, (String, Option[String]))] = stu1RDD.leftOuterJoin(score1RDD)
//对数据进行整理:取出姓名,和成绩,因为最后返回的类型是some,可以做模式匹配进行
leftRDD.map(kv=>{
val id: String = kv._1
val name: String = kv._2._1.split(",")(1)
val value: Any = kv._2._2 match {
case Some(v) =>
val score: String = v.split(",")(2)
s"${score}"
case None =>
"None"
}
//val list: List[String] = kv._2._2.toList
// val score1: List[String] = list.map(kv => {
// val score: String = kv.split(",")(2)
// score
// })
s"${id},${name},${value}"
})
//使用右关联:(1500100430,(Some(1500100430,屈佑运,22,男,文科五班),1500100430,1000006,32))
//保持右边的表的完整性,如果没有匹配到返回的是None
val rightRDD: RDD[(String, (Option[String], String))] = stu1RDD.rightOuterJoin(score1RDD)
rightRDD.map(kv => {
val id: String = kv._1
val scoSplits: Array[String] = kv._2._2.split(",")
val stuStr: String = kv._2._1 match {
case Some(v) =>
val stuSplits: Array[String] = v.split(",")
s"${stuSplits(1)},${stuSplits(4)}"
case None =>
"None"
}
s"$id,$stuStr,${scoSplits(1)},${scoSplits(2)}"
})
//全外连接:
val fullRDD: RDD[(String, (Option[String], Option[String]))] = stu1RDD.fullOuterJoin(score1RDD)
fullRDD.map(kv => {
val id: String = kv._1
val stuStr: String = kv._2._1 match {
case Some(v) =>
val stuSplits: Array[String] = v.split(",")
s"${stuSplits(1)},${stuSplits(4)}"
case None =>
"None"
}
val scoStr: String = kv._2._2 match {
case Some(v) =>
val scoSplits: Array[String] = v.split(",")
s"${scoSplits(1)},${scoSplits(2)}"
case None =>
"None"
}
s"$id,$stuStr,$scoStr"
})
.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo11Sample {
def main(args: Array[String]): Unit = {
/**
* 随机放回:Sample,转换算子,需要传入三个参数
* withReplacement:是否放回,不放回的目的是保持数据有重复
* fraction:取样比例 ,由于是随机放回,所以是只能每次只能大致的接近这个比例
* seed:种子,随机数种子,如果需要每次的输入的数目是一致的,那么对于种子seed来说,需要给定一个固定值
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo11Sample")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
stuRDD.sample(withReplacement = false,fraction = 0.01,100).foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo12Union {
/**
* union:转换算子,
* 只有两个结构类型一致的数据才能进行union操作
* 对RDD中数据,合并之后是不会进行去重的,如果需要去重,就需要进行distinct
* 对于union过后的RDD分区的数目是两个RDD分区数目的总和
*/
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo12Union ")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val line1: RDD[String] = stuRDD.sample(false, 0.01, 111)
val line2: RDD[String] = stuRDD.sample(false, 0.01, 100)
val unionRDD: RDD[String] = line1.union(line2)
unionRDD.foreach(println)
//对合并之后的union进行合并
unionRDD.distinct().foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo13Cartesian {
/**
*Cartesian:转换算子,做笛卡尔积:指的是不指定条件或者是条件恒成立的关联
*/
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setAppName("Demo13Cartesian")
//这里指定的并行度
conf.setMaster("local")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val rdd01: RDD[String] = stuRDD.sample(false, 0.01, 100)
val rdd02: RDD[(String, String)] = rdd01.cartesian(rdd01)
rdd02.foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import java.sql.{Connection, DriverManager, PreparedStatement, ResultSet}
object Demo14MapPartitions {
def main(args: Array[String]): Unit = {
// 假设有一个黑名单:List[String]
val blacklist: List[String] = List[String]("1500100001", "1500100011", "1500100111")
/**
* 基于黑名单结合SparkCore,从MySQL中获取学生的基本信息
*/
// 除了使用textFile加载数据构建RDD,还可以通过Scala本地集合来构建RDD
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo14MapPartitions")
val sc: SparkContext = new SparkContext(conf)
val blacklistRDD: RDD[String] = sc.parallelize(blacklist)
// blacklistRDD.foreach(println)
// conn、pSt的构建不能放在map算子外面,不然就会导致Task未被序列化的问题
// Task为什么需要被序列化?因为Task最终是需要由Driver端进行发送到Executor中执行,
// Driver端和Executor之间交互是通过网络进行的,所以Task必须序列化后才能进行网络传输
/**
* 虽然Spark的代码都在一个main方法里,但实际上运行的时候会分为两个部分:
* 算子内部的代码:最终会变成Task到Executor中执行
* 算子外部的代码:在Driver端执行
*/
// 使用mapPartitions算子进行优化:可以单独对每个分区的数据进行操作
//当不使用mapPartitions、foreachPartition的时候,在每一次与外界交互的时候,每读取一次数据,就需要与外界进行一次来连接,效率不高,
// 对于mapPartitions、foreachPartition的作用:因为每一个Task是作用在每一个分区上,然后每一个分区中会有很多的数据,
// 此时mapPartitions、foreachPartition就可以作用在每一个分区,在执行task任务的时候,每一个分区都只需要与外界建立一次连接,就不要每读取一条数据就与外界建立一次连接。
/**
* 如果需要在Spark代码中同外部的某个数据源建立连接,是作用在Spark上的每一个分区上
* 连接建立之后如果是需要从外部获取数据一般就是使用 mapPartitions 转换算子
* 如果是需要将结果数据写入外部系统,则一般使用 foreachPartition 行为算子
*/
blacklistRDD
// 对每一条数据进行操作
.map(id => {
/**
* 首先conn、pSt都是不可以放在算子外部的,因为它们不能够被序列化
* 但是放算子里又会出现连接被重复创建的问题,相当于一条数据就需要建立一次连接
*/
// 建立JDBC连接
val conn: Connection = DriverManager.getConnection("jdbc:mysql://master:3306/student", "root", "123456")
// 创建prepareStatement
val pSt: PreparedStatement = conn.prepareStatement("select name,age,gender,clazz from students where id = ?")
pSt.setString(1, id)
val rs: ResultSet = pSt.executeQuery()
var returnStr: String = ""
while (rs.next()) {
val name: String = rs.getString("name")
val age: Int = rs.getInt("age")
val gender: String = rs.getString("gender")
val clazz: String = rs.getString("clazz")
returnStr = s"$id,$name,$age,$gender,$clazz"
}
returnStr
})
// .foreach(println)
// 统计班级里存在黑名单学生的人数
blacklistRDD
.mapPartitions(iter => {
// 建立JDBC连接
val conn: Connection = DriverManager.getConnection("jdbc:mysql://master:3306/student", "root", "123456")
// 创建prepareStatement
val pSt: PreparedStatement = conn.prepareStatement("select name,age,gender,clazz from students where id = ?")
// iter表示RDD中一个分区的数据
iter
// 相当于对分区里的每一条数据进行操作,
// 这里的map只是迭代器提供的一个普通方法而已,并不会有内部外部之分
.map(id => {
pSt.setString(1, id)
val rs: ResultSet = pSt.executeQuery()
var returnStr: String = ""
while (rs.next()) {
val name: String = rs.getString("name")
val age: Int = rs.getInt("age")
val gender: String = rs.getString("gender")
val clazz: String = rs.getString("clazz")
returnStr = s"$id,$name,$age,$gender,$clazz"
}
returnStr
})
})
.foreach(println)
blacklistRDD
.repartition(2) // 对RDD进行重新分区,指定RDD的分区数
.mapPartitionsWithIndex((index, iter) => {
println(s"正在处理的分区index为:$index")
iter
}).foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import java.sql.{Connection, DriverManager, PreparedStatement}
object Demo15ForeachPartition {
def main(args: Array[String]): Unit = {
// 统计班级人数,将最终的结果写入MySQL
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo15ForeachPartition")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
val clazzCntRDD: RDD[(String, Int)] = stuRDD.map(line => (line.split(",")(4), 1))
.reduceByKey(_ + _)
// clazzCntRDD
// .foreach(kv => {
// /**
// * 同map算子类似,无法将连接建立在算子外部,否则会出现Task不能够被序列化的问题
// * 同样的放到算子内部则性能太低了,每一条数据会建立一次连接
// */
// // 建立JDBC连接
// val conn: Connection = DriverManager.getConnection("jdbc:mysql://master:3306/student?characterEncoding=utf8", "root", "123456")
// // 创建prepareStatement
// val pSt: PreparedStatement = conn.prepareStatement("insert into clazz_cnt (clazz,cnt) values (?,?)")
//
//
// val clazz: String = kv._1
// val cnt: Int = kv._2
//
// // 设置参数
// pSt.setString(1, clazz)
// pSt.setInt(2, cnt)
//
// // 执行插入语句
// pSt.execute()
//
// })
clazzCntRDD
// 同mapPartitions类似 适用于将结果写到外部系统
.foreachPartition(iter => {
// 建立JDBC连接
val conn: Connection = DriverManager.getConnection("jdbc:mysql://master:3306/student?characterEncoding=utf8", "root", "123456")
// 创建prepareStatement
val pSt: PreparedStatement = conn.prepareStatement("insert into clazz_cnt (clazz,cnt) values (?,?)")
var c: Int = 0
val batchSize: Int = 1000
// 处理每个分区内部的每一条数据
iter
.foreach(kv => {
c += 1
val clazz: String = kv._1
val cnt: Int = kv._2
// 设置参数
pSt.setString(1, clazz)
pSt.setInt(2, cnt)
// 执行插入语句
// pSt.execute()
pSt.addBatch() // 将构建好的insert语句添加到Batch中,等待批量执行
if (c >= batchSize) {
// 执行批量插入
//优化:数据进行批量写入
pSt.executeBatch()
c = 0
}
})
if (c > 0) {
// 执行批量插入
pSt.executeBatch()
}
})
}
}
object Demo16CoGroup {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo16CoGroup")
val sc: SparkContext = new SparkContext(conf)
/**
* CoGroup:转换算子,功能类似于fullOutputJoin,也是作用在kv的格式上面
* parallelize:作用就是将list转化成RDD
*/
val kvRDD01: RDD[(String, Int)] = sc.parallelize(List[(String, Int)]("kv" -> 1, "kv1" -> 2, "kv2" -> 3))
val kvRDD02: RDD[(String, Int)] = sc.parallelize(List[(String, Int)]("kv" -> 1, "kv1" -> 2, "kv2" -> 3))
kvRDD01.cogroup(kvRDD02).foreach(println)
}
}
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Demo17Action {
def main(args: Array[String]): Unit = {
/**
* 行为算子(Action算子):指的是没有返回值或者是返回值的类型不是RDD
* 主要包括:foreach,foreachPartition,collect,collectAsMap,reduce,reduceByKeyLocally,count,top,lookup
*/
val conf: SparkConf = new SparkConf()
conf.setMaster("local")
conf.setAppName("Demo17Action")
val sc: SparkContext = new SparkContext(conf)
val stuRDD: RDD[String] = sc.textFile("spark/data/stu/students.txt")
//1、collect:作用是将RDD中的所有的数据转化成本地集合Array数组,数据中的泛型与RDD中的里类型保持一致
val strings: Array[String] = stuRDD.collect()
//此处的println并不是spark中的算子,所以触发job任务的并不是println,是collect触发的job任务
strings.foreach(println)
//take:作用取出前n条数据,转换成scala中的本地集合
val strings1: Array[String] = stuRDD.take(2)
strings1.foreach(println)
//reduce:作用是对全局的数据进行聚合操作
//需求:求出所有学生年龄的总和
val ageSum: Int = stuRDD.map { kv => {
val age: Int = kv.split(",")(2).toInt
age
}
}
.reduce((i1, i2) => {
i1 + i2
})
println(ageSum)
//reduceByKeyLocally:是只能作用在kv形式的RDD中,作用在作用对数据进行聚合,并且将最终的结果转化成本地的map集合
//需求:统计每个班级的人数
val clazzRDD: RDD[(String, Int)] = stuRDD.map(kv => (kv.split(",")(4), 1))
clazzRDD.reduceByKeyLocally((i1,i2)=>{
i1+i2
})
.foreach(println)//这里的foreach是本地的map集合所提供的
//count:返回的是数量,返回值的类型是long
val l: Long = stuRDD.count()
println(l)
//top:取出前n条数据,中间是有排序的问题。
val topRDD: Array[String] = stuRDD.top(10)
topRDD.foreach(println)
//lookUp:只能作用在kv形式的RDD上面,作用是根据key来找出对应的value的值,返回值的类型是sep,有序的
val lookUpRDD: RDD[(String, String)] = stuRDD.map(kv => (kv.split(",")(0), kv))
val lookUp01: Seq[String] = lookUpRDD.lookup("1500100007")
lookUp01.foreach(println)
}
}