所有的这些算子都是在rdd,rdd上的分区partition上面执行的,不是在driver本地执行。
是spark作业执行的动因
foreach
打印rdd
arr.foreach(println)
foreachPartition
object Demo3 {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
.setAppName("Demo2")
.setMaster("local[*]")
val sc = new SparkContext(conf)
val array = sc.parallelize(Array(
"hello you",
"green eye",
"demon you",
"heaven you world",
"hell me",
"green hand"
), 1)
val pairs = array.flatMap(_.split("\\s+")).map((_, 1))
val rdd:RDD[(String, Int)] = pairs.reduceByKey(_+_)
saveInfoMySQLByConnectionPool(rdd)
sc.stop()
}
/* 1.引入foreachPartition,对一次分区循环,减少全局循环次数,对分区在写入数据库时的动作局部循环
2.数据库连接池,减少每次注册驱动次数
3.引入批量操作,减少与数据库写的次数
4.但也要对批量操作进行优化,防止批量数据过大
就是说:如果该分区由10w记录,每个批次不再是全量写入,二十每个批次写u5000条记录,分20此写完
每4条记录写一次
*/
def saveInfoMySQLByConnectionPool(rdd: RDD[(String, Int)]): Unit = {
rdd.foreachPartition(partition => {
//这是在partition内部,属于该partition的本地
val connection = ConnectionPool.getConnection()
val sql =
"""
|insert into wordcounts(word, `count`) Values(?, ?)
|""".stripMargin
val ps = connection.prepareStatement(sql)
var times = 0
partition.foreach{case (word, count) => {
times += 1
ps.setString(1, word)
ps.setInt(2, count)
ps.addBatch() //批量操作
if(times >= 4) {//分批次分批量写入
ps.executeBatch()
times = 0
}
}}
if(times != 0) {
ps.executeBatch() //批量操作
times = 0
}
ps.close()
ConnectionPool.release(connection)
})
}
}
count
// count:返回rdd中的记录数,相当于集合.size()
println(s"${rdd.count()}")
collect
// collect
/*
collect的作用就是将分散在executor上面的rdd-partition数据,通过网络拉回到driver之上,返回值是一个数组
尽量避免对全量数据集进行collect操作,搞不好,driver要崩溃
所以,在collect之前先filter一部分数据,或者使用其他算子,比如take
*/
var array1 = ret.collect()
println("--collect---" + array1.mkString("[", ", ", "]"))
take
/*
take(N)就是从rdd中只拉去其中的N条记录,返回值是一个数组
如果rdd中的数据都是有序的,take(N) ---> topN
take(N)的一个特例就是first,获取第一条记录
*/
// * take
val tuples = rdd.take(3)
println(s"${tuples.mkString(",")}")
first
// * first
println(s"${rdd.first()}")
/*
reduce
reduce, reduce是一个action操作,返回值是单列内容,就相当于scala集合
reduceByKey是一个transformation操作,返回值是一个RDD
*/
// * reduce
val num = rdd.values.reduce(_ + _)
println(num)
countByKey
/**
* countByKey,统计key出现的次数
* 返回值是一个Map[K, Long]
*/
// * countByKey
val map = rdd.countByKey()
map.foreach{
case(k,v)=>{
println(s"${k}++${v}")
}
}
saveAsXxx
/*
写入外部的存储系统里面
saveAsTextFile():将数据集以普通的文本写入到文件系统中
saveAsObjectFile: 将数据集以hadoop支持的SequenceFile格式写入文件系统,相当于saveAsSequenceFile
saveAsHadoopFile() ----> OutputFormat ---> interface org.apache.hadoop.mapred.OutputFormat
saveAsNewAPIHadoopFile() ---->NewOutputFormat --> abstract class org.apache.hadoop.mapreduce.OutputFormat
mr中指定输出时候,需要设置一个OutputFormat
FileOutputFormat.setOutput(path)
job.setOutputKeyClass()
job.setOutputValueClass()
job.setOutputFormatClass(TextOutputFormat.classs)
*/
//输出路径不能事先存在,跟hadoop输出一样
ret.saveAsTextFile("C:/Users/70201/Desktop/test/wc")
//("hdfs://ns1/data/output/spark/wc")
// .saveAsObjectFile("file:/E:/data/output/spark/obj")
// .saveAsHadoopFile()跟下面的一样,只有导包不同
// .saveAsNewAPIHadoopFile(
// path = "C:/Users/70201/Desktop/test/wchadoop",
// keyClass = classOf[Text],
// valueClass = classOf[IntWritable],
// outputFormatClass = classOf[TextOutputFormat[Text, IntWritable]]
// )
sc.stop()
}
}