RDD之间的关系
判断是宽依赖还是窄依赖,取决于RDD的分区是否能放在同一个流水线上执行,取决于这两个RDD是否是Shuffle关系,
如果是shuffle有kv对的就是宽依赖不能放在一个流水线上执行,不是shuffle关系的就是窄依赖可以放在一个流水线上执行
(1)窄依赖示例
import org.apache.spark.{SparkConf, SparkContext}
import org.junit.Test
class sparkDemo {
@Test
def narrowDependency: Unit ={
//1.生成RDD
val conf = new SparkConf().setMaster("local[3]").setAppName("dke")
val sc = new SparkContext(conf)
val rdd1 = sc.parallelize(Seq(1,2,3,4,5,6))
val rdd2 = sc.parallelize(Seq("a","b","c"))
//2.计算,笛卡尔积
val resultRDD = rdd1.cartesian(rdd2)
//3.获取结果
//collect()返回包含此RDD中所有元素的数组。
//forecah遍历每个元素
resultRDD.collect().foreach(println(_))
//关闭
sc.stop()
}
}
闭包
/**
* 编写一个函数,在这个函数内要有一个变量,返回一个函数值
* 通过这个变量完成一个计算
*/
@Test
def test(): Unit ={
val f: Int => Double = closure()
val result = f(5)
println(result)
}
//返回一个新的函数,执行一个计算
def closure(): Int=>Double ={ //返回一个函数传入int函数返回出double
val factor = 3.14
val function: Int => Double = (r:Int) => {
math.pow(r,2) * factor //函数嵌套函数,内函数访问外函数变量,就是闭包
}
function
}
spark全局累加器
(1)全局计数的问题
var cunt = 0
//1.生成RDD
val conf = new SparkConf().setMaster("local[3]").setAppName("dke")
val sc = new SparkContext(conf)
sc.parallelize(Seq(1,2,3,4,5)).foreach(count+=_)
上述代码是一个使用的错误案例,我们的spark是分布式计算,在不同的节点进行计算,我们的RDD算子会被闭包分发到不同的节点上去,每一个节点都会有一个count = 0的变量,不同的节点使用不同的初始值为0的count进行计算.所有他们的结果并不是预想的那样会累加
(2)spark的全局累加器
//1.生成RDD
val conf = new SparkConf().setMaster("local[3]").setAppName("dke")
val sc = new SparkContext(conf)
val counter = sc.longAccummlator("counter")
sc.parallelize(Seq(1,2,3,4,5)).foreach(counter.add(_))
(3)自定义累加器
我们可以参照官方实现的累加器longAccummlator来自定义累加器
默认情况下,Scala 使用的是不可变集合,如果你想使用可变集合,需要引用 scala.collection.mutable.Set 包。
class Accumulator{
@Test
def acc(): Unit ={
//1.生成RDD
val conf = new SparkConf().setMaster("local[3]").setAppName("dke")
val sc = new SparkContext(conf)
val numAccumulator = new NumAccumulator
//注册,告诉spark框架我自己定义了一个累加器
sc.register(numAccumulator,"num")
sc.parallelize(Seq("1","2","3")).foreach(item=>numAccumulator.add(item))
println(numAccumulator.value)
}
}
class NumAccumulator extends AccumulatorV2[String,Set[String]]{
private val nums: mutable.Set[String] = mutable.Set()
/**
* 告诉spark框架这个累加器是否为空
* @return
*/
override def isZero: Boolean = {
nums.isEmpty
}
/**
* 提供给spark框架一个拷贝的累加器
* @return
*/
override def copy(): AccumulatorV2[String, Set[String]] = {
val newAccumulator = new NumAccumulator()
nums.synchronized{
newAccumulator.nums ++= this.nums
}
newAccumulator
}
/**
* 帮助spark框架清理累加器的内容
*/
override def reset(): Unit = {
nums.clear()
}
/**
* 外部传入要累加的内容,在这个方法中进行累加
* @param v
*/
override def add(v: String): Unit = {
nums += v
}
/**
* 累加器进行累加的时候,每个分布式节点都有一个实例
* 在最后Driver进行一次合并,把所有的实例的内容合并起来
* @param other
*/
override def merge(other: AccumulatorV2[String, Set[String]]): Unit = {
nums ++= other.value
}
/**
* 提供给外部累加结果
* 外部有可能进行修改,如果是可变的集合,其外部的修改会影响内部的值
* @return:返回一个不可变的nums集合
*/
override def value: Set[String] = {
nums.toSet
}
}
广播变量
(1)广播变量的作用和原理
对于RDD算子内计算调用的变量,就产生了闭包,会一起被分发到不同的节点上的不同任务,就会产生很多冗余的变量存在,我们想通过一个广播变量在一个节点上就产生一个变量供不同的任务共同使用
class broadCast(){
@Test
def test02(): Unit ={
//1.生成RDD
val conf = new SparkConf().setMaster("local[3]").setAppName("dke")
val sc = new SparkContext(conf)
val b = sc.broadcast(1) //定义一个广播变量,值为1
//下面正常进行rdd计算
val dataSet = sc.parallelize(Seq(1,2,3,4))
val resultSet = dataSet.map(item=>b.value+item).collect().foreach(println(_))
}
}