RDD(Resilient Distributed Dataset) ,弹性分布式数据集。
textFile
方法底层封装的是MR
读取文件的方式,读取文件之前先 split
,默认 split
大小是一个 block
大小。
RDD
实际上不存储数据,这里方便理解,暂时理解为存储数据。
什么是 K、V 格式的 RDD
?
如果 RDD
里面存储的数据都是二元组对象,那么这个 RDD 我们就叫做 K,V 格式的 RDD。
哪里体现 RDD
的弹性?
RDD
是由 一系列的partition
组成。其大小和数量都是可以改变的,默认情况下,partition的个数和block块个数相同,体现了 RDD
的弹性。
哪里体现 RDD
的容错?
RDD
之间存在依赖关系,子RDD可以找出对应的父RDD然后通过一系列计算得到相应结果,这就是容错的体现。
哪里体现 RDD
的分布式?
RDD
是由 Partition
组成,partition
是分布在不同节点上的。RDD
提供计算最佳位置,体现了数据本地化。体现了大数据中“计算移动数据不移动”的理念。 以standalone模式为例,Standalone模式是Spark自带的一种集群模式,Standalone模式是真实地在多个机器之间搭建Spark集群的环境,完全可以利用该模式搭建多机器集群,用于实际的大数据处理。
以上图中有四个机器节点,Driver 和 Worker 是启动在节点上的进程,运行在 JVM 中的进程。其中:
这里只是做了简单介绍,更多可查看Spark四种运行模式介绍
SparkConf
对象
Application name
。SparkContext
对象SparkContext
即上下文环境对象创建一个 RDD
,对 RDD
进行处理。Action
类算子来触发 Transformation
类算子执行。Spark
上下文对象。Transformations 类算子是一类算子(函数)叫做转换算子,如map,flatMap,reduceByKey 等。Transformations 算子是延迟执行,也叫懒加载执行。
filter
:过滤符合条件的记录数,true 保留,false 过滤掉。
Operator_filter.scala
:
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_filter {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("filter")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val result = lines.filter { _.indexOf("Spark") >= 0 }
result.foreach { println}
sc.stop()
}
}
hello Spark
map
:将一个 RDD 中的每个数据项,通过 map 中的函数映射变为一个新的元素。
特点:输入一条,输出一条数据。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_map {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("map")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val result = lines.map { _.split(" ") }
result.foreach(println)
sc.stop()
}
}
[Ljava.lang.String;@1c200b99
[Ljava.lang.String;@1ae41188
[Ljava.lang.String;@72a7be25
[Ljava.lang.String;@26de52e0
flatMap
:先 map
后 flat
。与 map 类似,每个输入项可以映射为 0 到多个输出项。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_flatMap {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("flatMap")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val result = lines.flatMap { _.split(" ")}
result.foreach(println)
sc.stop()
}
}
hello
tiantian
hello
shsxt
hello
gzsxt
hello
Spark
sample
:随机抽样算子,根据传进去的小数按比例进行有放回或者无放回的抽样。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_sample {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sample")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
lines.sample(true, 0.5,10).foreach(println)
}
}
hello shsxt
reduceByKey
:将相同的 Key 根据相应的逻辑进行处理。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_reduceByKey {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("reduceByKey")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val flatMap = lines.flatMap { _.split(" ")}
val map = flatMap.map {(_,1)}
map.reduceByKey(_+_).foreach(println)
sc.stop()
}
}
(Spark,1)
(shsxt,1)
(tiantian,1)
(hello,4)
(gzsxt,1)
sortByKey/sortBy
:作用在 K、V 格式的 RDD 上,对 key 进行升序或者降序排序。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_sortByKey {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sortByKey")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val reduceResult = lines.flatMap { _.split(" ")}.map { (_,1)}.reduceByKey(_+_)
reduceResult.map(f => {(f._2,f._1)}).sortByKey(false).map(f => {(f._2,f._1)}).foreach(println)
sc.stop()
}
}
(hello,4)
(Spark,1)
(shsxt,1)
(tiantian,1)
(gzsxt,1)
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_sortBy {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sortBy")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val reduceResult = lines.flatMap { _.split(" ")}.map{ (_,1)}.reduceByKey(_+_)
val result = reduceResult.sortBy(_._2,false)
result.foreach{println}
sc.stop()
}
}
(hello,4)
(Spark,1)
(shsxt,1)
(tiantian,1)
(gzsxt,1)
join
算子:作用在 K,V
格式的 RDD 上。根据 K
进行连接,对(K,V)join(K,W)
返回(K,(V,W))
leftOuterJoin
算子rightOuterJoin
算子fullOuterJoin
算子注意:join 后的分区数与父 RDD 分区数多的那一个相同。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Operator_Join {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("flatMap")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(
Array(("a",1),("b",2),("c",3)),
3
)
val rdd2 = sc.parallelize(
Array(("a",1),("d",2),("e",3)),
2
)
val result1: RDD[(String, (Int, Int))] = rdd1.join(rdd2)
//注意:join 后的分区数与父 RDD 分区数多的那一个相同
println("join后的分区数=",result1.partitions.length)
result1.foreach(println)
val result2: RDD[(String, (Int, Option[Int]))] = rdd1.leftOuterJoin(rdd2)
result2.foreach(println)
val result3: RDD[(String, (Option[Int], Int))] = rdd1.rightOuterJoin(rdd2)
result3.foreach(println)
val result4: RDD[(String, (Option[Int], Option[Int]))] = rdd1.fullOuterJoin(rdd2)
result4.foreach(println)
}
}
(join后的分区数=,3)
//join
(a,(1,1))
//leftOuterJoin
(a,(1,Some(1)))
(b,(2,None))
(c,(3,None))
//rightOuterJoin
(d,(None,2))
(e,(None,3))
(a,(Some(1),1))
//fullOuterJoin
(d,(None,Some(2)))
(e,(None,Some(3)))
(a,(Some(1),Some(1)))
(b,(Some(2),None))
(c,(Some(3),None))
union
算子合并两个数据集。两个数据集的类型要一致。
注意:返回新的 RDD 的分区数是合并 RDD 分区数的总和。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Operator_union {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sortBy")
val sc = new SparkContext(conf)
//union算子
val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3), 3)
val rdd2: RDD[Int] = sc.parallelize(List(4, 5, 6), 2)
val rdd3: RDD[Int] = rdd1.union(rdd2)
println(rdd3.getNumPartitions) //并行度
rdd3.foreach(println)
sc.stop()
}
}
5
1
2
3
4
5
6
intersection
算子:取两个数据集的交集。
注意:intersection 后的分区数与父 RDD 分区数多的那一个相同。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Operator_intersection {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sortBy")
val sc = new SparkContext(conf)
//union算子
val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3), 3)
val rdd2: RDD[Int] = sc.parallelize(List(2, 3, 4), 2)
val rdd3: RDD[Int] = rdd1.intersection(rdd2)
println(rdd3.getNumPartitions)
rdd3.foreach(println)
sc.stop()
}
}
3
3
2
subtract
算子是取两个数据集的差集。
注意:subtract后的分区数与父 RDD 分区数多的那一个相同。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Operator_subtract {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sortBy")
val sc = new SparkContext(conf)
//union算子
val rdd1: RDD[Int] = sc.parallelize(List(1, 2, 3), 3)
val rdd2: RDD[Int] = sc.parallelize(List(2, 3, 4), 2)
val rdd3: RDD[Int] = rdd1.subtract(rdd2)
println(rdd3.getNumPartitions)
rdd3.foreach(println)
sc.stop()
}
}
3
1
与 map 类似,mapPartition
算子遍历的单位是每个 partition 上的数据。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
object Operator_mapPartition {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("map")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt",minPartitions = 2)
//map算子
lines.map(x=>{
println("创建数据库连接:")
println("插入数据"+x)
println("关闭数据库连接")
x
}).foreach(println)
//mapPartition算子
val result: RDD[String] = lines.mapPartitions(x => {
println("创建数据库连接:")
val list: ListBuffer[String] = ListBuffer("")
while (x.hasNext) {
val next: String = x.next()
println("查询数据库数据" + next)
list.append(next)
}
println("关闭数据库连接")
list.iterator
})
result.foreach(println)
sc.stop()
}
}
//map算子
创建数据库连接:
插入数据hello tiantian
关闭数据库连接
hello tiantian
创建数据库连接:
插入数据hello shsxt
关闭数据库连接
hello shsxt
创建数据库连接:
插入数据hello gzsxt
关闭数据库连接
hello gzsxt
创建数据库连接:
插入数据hello Spark
关闭数据库连接
hello Spark
//mapPartition算子
创建数据库连接:
查询数据库数据hello tiantian
查询数据库数据hello shsxt
关闭数据库连接
hello tiantian
hello shsxt
创建数据库连接:
查询数据库数据hello gzsxt
查询数据库数据hello Spark
关闭数据库连接
hello gzsxt
hello Spark
distinct
算子用于去重。其底层逻辑是
/**
* Return a new RDD containing the distinct elements in this RDD.
*/
def distinct(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] = withScope {
map(x => (x, null)).reduceByKey((x, y) => x, numPartitions).map(_._1)
}
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_distinct {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("filter")
val sc = new SparkContext(conf)
val lines = sc.parallelize(Array(1,2,2,3,4,4))
lines.distinct().foreach(println)
//手动给定分组条件,再利用map去掉不要的东西
lines.map(x=>{(x,1)}).reduceByKey(_+_).map(x=>{x._1}).foreach(println)
sc.stop()
}
}
//distinct算子
4
1
3
2
//map+reduceByKey+map算子
4
1
3
2
当调用类型(K,V)
和(K,W)
的数据上时,返回一个数据集
(K,(Iterable
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Operator_cogroup {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("sortByKey")
val sc = new SparkContext(conf)
val nameRDD: RDD[(String, String)] = sc.parallelize(List(
("1", "zhangsan"),
("2", "lisi"),
("3", "wangwu"),
("4", "maliu")
))
val scoreRDD: RDD[(String, Int)] = sc.parallelize(List(
("1", 100),
("2", 99),
("3", 89),
("4", 60),
("1",1000)
))
val value: RDD[(String, (Iterable[String], Iterable[Int]))] = nameRDD.cogroup(scoreRDD)
value.foreach(println)
sc.stop()
}
}
(4,(CompactBuffer(maliu),CompactBuffer(60)))
(2,(CompactBuffer(lisi),CompactBuffer(99)))
(3,(CompactBuffer(wangwu),CompactBuffer(89)))
(1,(CompactBuffer(zhangsan),CompactBuffer(100, 1000)))
类似于 mapPartitions,除此之外还会携带分区的索引值,能获取到当前处理数据的分区号。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
object Operator_mapPartitionsWithIndex {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("mapPartitionsWithIndex")
val sc = new SparkContext(conf)
val rdd = sc.parallelize(List("a","b","c"),3)
rdd.mapPartitionsWithIndex((index,iter)=>{
val list = ListBuffer[String]()
while(iter.hasNext){
val v = iter.next()
list.append(v)
println("index = "+index+" , value = "+v)
}
list.iterator
}, false).foreach(println)
sc.stop();
}
}
index = 0 , value = a
a
index = 1 , value = b
b
index = 2 , value = c
c
增加或减少分区都会产生 shuffle。当考虑减少分区时,一般使用coalesce算子,可以避免Shuffle。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
object Operator_repartition {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("repartition")
val sc = new SparkContext(conf)
val rdd1 = sc.makeRDD(List(1,2,3,4,5,6,7),3)
val rdd2 = rdd1.mapPartitionsWithIndex((partitionIndex,iter)=>{
val list = new ListBuffer[String]()
while(iter.hasNext){
list += "rdd1partitionIndex : "+partitionIndex+",value :"+iter.next()
}
list.iterator
})
rdd2.foreach{ println }
val rdd3 = rdd2.repartition(4)
val result = rdd3.mapPartitionsWithIndex((partitionIndex,iter)=>{
val list = ListBuffer[String]()
while(iter.hasNext){
list +=("repartitionIndex : "+partitionIndex+",value :"+iter.next())
}
list.iterator
})
result.foreach{ println}
sc.stop()
}
}
rdd1partitionIndex : 0,value :1
rdd1partitionIndex : 0,value :2
rdd1partitionIndex : 1,value :3
rdd1partitionIndex : 1,value :4
rdd1partitionIndex : 2,value :5
rdd1partitionIndex : 2,value :6
rdd1partitionIndex : 2,value :7
repartitionIndex : 0,value :rdd1partitionIndex : 0,value :2
repartitionIndex : 0,value :rdd1partitionIndex : 1,value :4
repartitionIndex : 0,value :rdd1partitionIndex : 2,value :6
repartitionIndex : 1,value :rdd1partitionIndex : 2,value :7
repartitionIndex : 3,value :rdd1partitionIndex : 0,value :1
repartitionIndex : 3,value :rdd1partitionIndex : 1,value :3
repartitionIndex : 3,value :rdd1partitionIndex : 2,value :5
coalesce 常用来减少分区,第二个参数是减少分区的过程中是否产生shuffle。true 为产生 shuffle,false 不产生 shuffle。默认是 false。如果 coalesce 设置的分区数比原来的 RDD 的分区数还多的话,第二个参数设置为 false 不会起作用,相当于分区原封不动。如果设置成 true,效果和 repartition 一样。即 repartition(numPartitions) = coalesce(numPartitions,true)
。意思就是只有当设置为True时,coalesce 增加分区才会生效,此时与repartition一致。两个算子底层均是coalesce算子。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable.ListBuffer
object Operator_coalesce {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("coalesce")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Array(1, 2, 3, 4, 5, 6), 4)
//可变长度的集合 不可变长度的集合
//List
val rdd2 = rdd1.mapPartitionsWithIndex((partitionIndex, iter) => {
val list = new ListBuffer[String]()
while (iter.hasNext) {
list += "rdd1 PartitonIndex : " + partitionIndex + ",value :" + iter.next()
}
list.iterator
})
rdd2.foreach {
println
}
val rdd3 = rdd2.coalesce(5, false)
println("rdd3 Partitions=" + rdd3.getNumPartitions)
val rdd4 = rdd3.mapPartitionsWithIndex((partitionIndex, iter) => {
val list = new ListBuffer[String]()
while (iter.hasNext) {
list += "coalesce PartitionIndex :" + partitionIndex + ",value:" + iter.next()
}
list.iterator
})
rdd4.foreach {
println
}
sc.stop()
}
}
rdd1 PartitonIndex : 0,value :1
rdd1 PartitonIndex : 1,value :2
rdd1 PartitonIndex : 1,value :3
rdd1 PartitonIndex : 2,value :4
rdd1 PartitonIndex : 3,value :5
rdd1 PartitonIndex : 3,value :6
rdd3 Partitions=4
coalesce PartitionIndex :0,value:rdd1 PartitonIndex : 0,value :1
coalesce PartitionIndex :1,value:rdd1 PartitonIndex : 1,value :2
coalesce PartitionIndex :1,value:rdd1 PartitonIndex : 1,value :3
coalesce PartitionIndex :2,value:rdd1 PartitonIndex : 2,value :4
coalesce PartitionIndex :3,value:rdd1 PartitonIndex : 3,value :5
coalesce PartitionIndex :3,value:rdd1 PartitonIndex : 3,value :6
作用在 K,V 格式的 RDD 上。根据 Key 进行分组。作用在(K,V),返 回(K,Iterable )。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_groupByKey {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("groupByKey")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Array(
(1,"a"),
(1,"b"),
(2,"c"),
(3,"d")
))
val result = rdd1.groupByKey()
result.foreach(println)
sc.stop()
}
}
(1,CompactBuffer(a, b))
(3,CompactBuffer(d))
(2,CompactBuffer(c))
将两个 RDD 中的元素(KV 格式/非 KV 格式)变成一个 KV 格式的 RDD,
注意:两个 RDD 的个数必须相同。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
/**
* 将两个RDD中的元素(KV格式/非KV格式)变成一个KV格式的RDD,两个RDD的个数必须相同。
*/
object Operator_zip {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setAppName("zip").setMaster("local")
val sc = new SparkContext(conf)
val nameRDD = sc.parallelize(Array("zhangsan","lisi","wangwu"))
val scoreRDD = sc.parallelize(Array(1,2,3))
val result = nameRDD.zip(scoreRDD)
result.foreach(println)
sc.stop()
}
}
(zhangsan,1)
(lisi,2)
(wangwu,3)
该函数将 RDD 中的元素和这个元素在 RDD 中的索引号(从 0 开始)组合成(K,V)对。
package com.shsxt.scalaTest.core.transform_operator
import org.apache.spark.{SparkConf, SparkContext}
/**
* 该函数将RDD中的元素和这个元素在RDD中的索引号(从0开始)组合成(K,V)对
*/
object Operator_zipWithIndex {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("zipWithIndex")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Array((1,"a"),(2,"b"),(3,"c")))
val result = rdd1.zipWithIndex()
result.foreach(println)
sc.stop()
}
}
((1,a),0)
((2,b),1)
((3,c),2)
Action 类 算 子 也 是 一 类 算 子 ( 函 数 ) 叫 做 行 动 算 子 , 如foreach,collect,count 等。Transformations 类算子是延迟执行,Action 类算子是触发执行。一个 application 应用程序中有几个 Action 类算子执行,就有几个 job 运行。
count
算子:返回数据集中的元素数。会在结果计算完成后回收到 Driver 端。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_count {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("count")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
val result = lines.count()
println(result)
sc.stop()
}
}
4
take(n)
算子:返回一个包含数据集前 n 个元素的集合。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_take {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("reduce")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Array(1, 2, 3, 4, 5))
val result = rdd1.take(2)
result.foreach(println)
sc.stop()
}
}
1
2
first
算子:first=take(1)
,返回数据集中的第一个元素。
foreach
算子:循环遍历数据集中的每个元素,运行相应的逻辑。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_foreach {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("collect")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
lines.foreach(println)
}
}
hello tiantian
hello shsxt
hello gzsxt
hello Spark
collect
算子:将计算结果回收到 Driver 端。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_collect {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("collect")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
lines.collect().foreach {
println
}
sc.stop()
}
}
hello tiantian
hello shsxt
hello gzsxt
hello Spark
foreachPartition
算子:遍历的数据是每个 partition 的数据。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.{SparkConf, SparkContext}
object Operator_foreachPartition {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("collect")
val sc = new SparkContext(conf)
val lines = sc.textFile("data/word.txt")
lines.foreachPartition(x=>{
System.out.println("连接数据库....")
while(x.hasNext){
println(x.next())
}
System.out.println("关闭数据库....")
})
}
}
连接数据库....
hello tiantian
hello shsxt
hello gzsxt
hello Spark
关闭数据库....
作用到 K,V 格式的 RDD 上,根据 Key 计数相同 Key 的数据集元素。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
/**
* countByKey
*
* 作用到K,V格式的RDD上,根据Key计数相同Key的数据集元素。返回一个Map
*/
object Operator_countByKey {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("countByKey")
val sc = new SparkContext(conf)
val rdd1: RDD[(String, Int)] = sc.parallelize(List(
("a", 100),
("b", 200),
("a", 300),
("c", 400)
))
val result: collection.Map[String, Long] = rdd1.countByKey()
result.foreach(println)
sc.stop()
}
}
(a,2)
(b,1)
(c,1)
根据数据集每个元素相同的内容来计数。返回相同内容的元素对应的条数。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* countByValue
* 根据数据集每个元素相同的内容来计数。返回相同内容的元素对应的条数。
*/
object Operator_countByValue {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("countByValue")
val sc: SparkContext = new SparkContext(conf)
val rdd1: RDD[(String, Int)] = sc.parallelize(List(
("a", 100),
("a", 100),
("b", 300),
("b", 300),
("c", 400)
))
val rdd2: collection.Map[(String, Int), Long] = rdd1.countByValue()
rdd2.foreach(println)
sc.stop()
}
}
((b,300),2)
((c,400),1)
((a,100),2)
根据聚合逻辑聚合数据集中的每个元素。
package com.shsxt.scalaTest.core.action_operator
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
/**
* reduce
*
* 根据聚合逻辑聚合数据集中的每个元素。
*/
object Operator_reduce {
def main(args: Array[String]): Unit = {
val conf = new SparkConf()
conf.setMaster("local").setAppName("reduce")
val sc = new SparkContext(conf)
val rdd1: RDD[Int] = sc.parallelize(Array(1, 2, 3, 4, 5))
val result: Int = rdd1.reduce(_ + _)
println(result)
sc.stop()
}
}
15
控制算子有三种:cache
,persist
,checkpoint
,以上算子都可以将RDD 持久化,持久化的单位是 partition。
cache
和 persist
算子都是懒执行的。必须有一个 action
类算子触发执行。checkpoint
算子不仅能将 RDD 持久化到磁盘,还能切断 RDD 之间的依赖关系。 默认将 RDD 的数据持久化到内存中。cache
是懒执行。针对重用RDD,可以将其持久化到内存中。
注意:
cache () = persist()=persist(StorageLevel.Memory_Only)
未加入cache算子之前:
package com.shsxt.scala
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Test_Persistent {
def main(args: Array[String]): Unit = {
//新建SparkContext执行环境入口对象
val conf = new SparkConf().setAppName("TransformationOperator").setMaster("local")
val sc = new SparkContext(conf)
//读取数据
var line: RDD[String] = sc.textFile("data/NASA_access_log_Aug95")
//cache算子:将 RDD 的数据持久化到内存中
// line = line.cache()
//统计行数并计算时间
val start: Long = System.currentTimeMillis()
val count: Long = line.count()
val end: Long = System.currentTimeMillis()
println("一共"+count+"条数据,初始化和缓存时间及计算时间总共为="+(end-start))
//统计行数并计算时间
val start2: Long = System.currentTimeMillis()
val count2: Long = line.count()
val end2: Long = System.currentTimeMillis()
println("一共"+count2+"条数据,初始化和缓存时间及计算时间总共为="+(end2-start2))
sc.stop()
}
}
一共1569898条数据,初始化和缓存时间及计算时间总共为=772
一共1569898条数据,初始化和缓存时间及计算时间总共为=478
加入cache算子之后:
package com.shsxt.scala
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Test_Persistent {
def main(args: Array[String]): Unit = {
//新建SparkContext执行环境入口对象
val conf = new SparkConf().setAppName("TransformationOperator").setMaster("local")
val sc = new SparkContext(conf)
//读取数据
var line: RDD[String] = sc.textFile("data/NASA_access_log_Aug95")
//cache算子:将 RDD 的数据持久化到内存中
line = line.cache()
//统计行数并计算时间
val start: Long = System.currentTimeMillis()
val count: Long = line.count()
val end: Long = System.currentTimeMillis()
println("一共"+count+"条数据,初始化和缓存时间及计算时间总共为="+(end-start))
//统计行数并计算时间
val start2: Long = System.currentTimeMillis()
val count2: Long = line.count()
val end2: Long = System.currentTimeMillis()
println("一共"+count2+"条数据,初始化和缓存时间及计算时间总共为="+(end2-start2))
sc.stop()
}
}
一共1569898条数据,初始化和缓存时间及计算时间总共为=1470
一共1569898条数据,初始化和缓存时间及计算时间总共为=55
发现:虽然第一次速度变慢,但是第二次速度明显加快!
可以指定持久化的级别。最常用的是MEMORY_ ONLY
和MEMORY_ AND_ DISK
。
持久化级别如下:
package com.shsxt.scala
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object Test_Persistent {
def main(args: Array[String]): Unit = {
//新建SparkContext执行环境入口对象
val conf = new SparkConf().setAppName("TransformationOperator").setMaster("local")
val sc = new SparkContext(conf)
//读取数据
var line: RDD[String] = sc.textFile("data/NASA_access_log_Aug95")
//持久化算子:中间结果的持久化,提升整体的效率
//默认持久化级别是MEMORY_ ONLY,与cache算子一致
line = line.persist()
// line = line.cache()
//统计行数并计算时间
val start: Long = System.currentTimeMillis()
val count: Long = line.count()
val end: Long = System.currentTimeMillis()
println("一共"+count+"条数据,初始化和缓存时间及计算时间总共为="+(end-start))
//统计行数并计算时间
val start2: Long = System.currentTimeMillis()
val count2: Long = line.count()
val end2: Long = System.currentTimeMillis()
println("一共"+count2+"条数据,初始化和缓存时间及计算时间总共为="+(end2-start2))
sc.stop()
}
}
一共1569898条数据,初始化和缓存时间及计算时间总共为=1292
一共1569898条数据,初始化和缓存时间及计算时间总共为=62
错误:
rdd.cache().count()
返回的不是持久化的 RDD,而是一个数值了。
checkpoint 将 RDD 持久化到磁盘,还可以切断 RDD 之间的依赖关系。
checkpoint 的执行原理:
使用checkpoint 时常用优化手段:对 RDD 执行 checkpoint 之前,最好对这个 RDD 先执行cache,这样新启动的 job 只需要将内存中的数据拷贝到 HDFS上就可以,省去了重新计算这一步。
使用:
package com.shsxt.scala
import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.rdd.RDD
import org.apache.spark.storage.StorageLevel
object Test_Checkpoint {
def main(args: Array[String]): Unit = {
//新建SparkContext执行环境入口对象
val conf = new SparkConf().setAppName("TransformationOperator").setMaster("local")
val sc = new SparkContext(conf)
//读取数据
sc.setCheckpointDir("./chkpoint")
var line: RDD[String] = sc.textFile("data/NASA_access_log_Aug95")
//RDD保存到内存中是对checkpoint过程的优化,因为它是新的任务(new job)
line = line.persist(StorageLevel.MEMORY_ONLY)
line.checkpoint()
//统计行数并计算时间
val start: Long = System.currentTimeMillis()
val count: Long = line.count()
val end: Long = System.currentTimeMillis()
println("一共" + count + "条数据,初始化和缓存时间及计算时间总共为=" + (end - start))
//统计行数并计算时间
val start2: Long = System.currentTimeMillis()
val count2: Long = line.count()
val end2: Long = System.currentTimeMillis()
println("一共" + count2 + "条数据,初始化和缓存时间及计算时间总共为=" + (end2 - start2))
sc.stop()
}
}
一共1569898条数据,初始化和缓存时间及计算时间总共为=2189
一共1569898条数据,初始化和缓存时间及计算时间总共为=40