一、Dependency:依赖:RDD分区之间的依存关系
---------------------------------------------------------
1.NarrowDependency: 子RDD的每个分区依赖于父RDD的少量分区。
|
/ \
---
|---- OneToOneDependency //父子RDD之间的分区存在一对一关系。
|---- RangeDependency //父RDD的一个分区范围和子RDD存在一对一关系。
|---- PruneDependency //删减依赖--在PartitionPruningRDD和其父RDD之间的依赖,子RDD包含了父RDD的分区子集。。
2.ShuffleDependency //混洗依赖,在shuffle阶段输出时的一种依赖。
二、SparkContext创建调度器的过程
------------------------------------------------------------------
[SparkContext.scala:501行]
val (sched, ts) = SparkContext.createTaskScheduler(this, master, deployMode)
_schedulerBackend = sched
_taskScheduler = ts
_dagScheduler = new DAGScheduler(this)
三、Spark启动模式
--------------------------------------------------
1.[本地模式,通过线程模拟]
本地后台调度器
spark local
spark local[3] //3线程,模拟cluster集群
spark local[*] //匹配cpu个数,
spark local[3,2] //3:3个线程,2表示每个分区重试1次[0和1等价,不重试]
conf.seetMaster[3]
conf.seetMaster[*]
conf.seetMaster[3,2]
2.[相当于伪分布式]
StandaloneSchedulerBackend
spark local-cluster[N, cores, memory] //模拟spark集群。
3.[完全分布式]
StandaloneSchedulerBackend
spark spark://s201:7077 //连接到spark集群上.
4.代码演示
import org.apache.spark.{SparkConf, SparkContext}
object Test8 {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf()
sparkConf.setAppName("Test-1")
sparkConf.setMaster("local[2]")
sparkConf.setMaster("local[*]")
sparkConf.setMaster("local[2,3]")
sparkConf.setMaster("local[*]")
val sc: SparkContext = new SparkContext(sparkConf)
val data = List(1,2,3,4,5,6,7,8,9,10);
val rdd1 = sc.parallelize(data)
val rdd2 = rdd1.map(
e => {
val s = Thread.currentThread().getName();
println(s);
e
}
)
val rdd3 = rdd1.repartition(4);
val rdd4 = rdd3.map(
e => {
val s = Thread.currentThread().getName();
println(s);
e
}
)
rdd4.collect()
}
}
四、shuffle操作
--------------------------------------------------
1.跨分区再分发数据的一种机制
2.触发shuffle的操作:groupByKey reduceByKey... reparatition coalesce... join cogroup
3.shuffle对性能的影响
a.shuffle涉及到磁盘或者网络io,所以成本很高
b.shuffle为了数据重用,在磁盘上生成大量的中间文件
c.垃圾回收如果不及时,长时间的shuffle会占用大量的磁盘空间
五、RDD持久化
--------------------------------------------------
1.数据在内存中持久化,可以跨操作
2.持久化RDD时,节点上的每个分区的数据都会保存在内存中,以备在其他操作中进行重用
3.这种缓存技术是迭代式计算和交互式查询的重要手段。
4.可以使用persist()和cache(),来进行rdd的持久化,cache()是persist()一种,使用内存方式进行缓存
a.首次Action计算时,会发生persist(),会将计算的数据保存在节点内存中
b.spark的这种缓存机制,是容错的,如果rdd的任何一个分区丢失了,都可以通过最初创建rdd的进行重新计算出直到Action的RDD结果
c.每个RDD都可以使用不同的存储级别进行持久化,可以通过storelevel进行设定
MEMORY_ONLY //只在内存
MEMORY_AND_DISK
MEMORY_ONLY_SER //内存存储(串行化)
MEMORY_AND_DISK_SER
DISK_ONLY //硬盘
MEMORY_ONLY_2 //带有副本
MEMORY_AND_DISK_2 //快速容错。
OFF_HEAP
d.级别选取建议
如果想快速容错,就带副本:MEMORY_ONLY_2
如果内存足够,选取MEMORY_ONLY
如果内存不是很足够,选取MEMORY_ONLY_SER
尽量不要缓存到磁盘,除非内存非常不够
5.删除持久化数据
rdd2.unpersist(isBlocking = true)
6.代码演示
import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}
object TestPersist {
def main(args: Array[String]): Unit = {
val sparkConf: SparkConf = new SparkConf()
sparkConf.setAppName("Test-1")
sparkConf.setMaster("local")
val sc: SparkContext = new SparkContext(sparkConf)
val rdd1 = sc.parallelize(1 to 20)
val rdd2 = rdd1.map(
e => {
println(e)
e * 2
}
)
//更改缓存地
rdd2.persist(StorageLevel.DISK_ONLY)
//rdd2.persist(StorageLevel.MEMORY_ONLY)
//将rdd2的结果缓存到内存中
//rdd2.cache()
println(rdd2.reduce(_ + _))
//移除缓存
rdd2.unpersist(true)
println(rdd2.reduce(_ + _))
}
}
六、变量传递过程中的串行化
----------------------------------------------------------
因为是集群计算,所以map过程的代码和过程中的变量,会将变量的值拷贝,然后通过网络,分发到各个节点,[区别于共享变量:每次计算都要分发的]过程中需要进行串行和反串行,所以就要求过程中的变量要能串行化
scala> class Dog
scala> val d = new Dog
scala> sc.parallelize(1 to 20).map( e=> {println(d);e}).reduce(_ + _)
//会报错因为dog不能串行化
//需要定义Dog的时候继承Serializable接口
scala> case class Dog (name:String,age:Int)
scala> val d = Dog("tom",1)
scala> sc.parallelize(1 to 20).map( e=> {println(d);e}).reduce(_ + _)
七、共享变量
---------------------------------------------------
1.在所有的节点间共享一个变量。
2.Spark通过广播变量和累加器实现变量的共享
3.广播变量
a.一个只读的变量,在创建的时候广播一次[也必须继承了串行化接口],然后在每个节点进行缓存。而不是跟随任务进行网络间传递
b.使用方式
scala> val broadcastVar = sc.broadcast(Array(1,2,3))
broadcastVar: org.apache.spark.broadcast.Broadcast[Array[Int]] = Broadcast(3)
scala> broadcastVar.value
res7: Array[Int] = Array(1, 2, 3)
4.累加器
a.一个只能增加的变量,高效并行。通常用于MR的Counter和Sum。UI可见
b.使用方式
scala> val ac1 = sc.longAccumulator("ac1")
ac1: org.apache.spark.util.LongAccumulator = LongAccumulator(id: 288, name: Some(ac1), value: 0)
scala> ac1.value
res8: Long = 0
scala> sc.parallelize(1 to 20).map(_ * 2).map(e=>{ac1.add(1) ; e}).reduce(_+_)
res9: Int = 420
scala> ac1.value
res10: Long = 20
c.自定义累加器
class MyAccumulatorV2 extends AccumulatorV2[V,T]{
private val v:V = ...
def reset():
def add():
def sum():
def count():
def value():
...
}
八、Spark分布式计算PI的值
----------------------------------------------------------
scala> sc.parallelize(1 to 999999999).map(e=>{
val a = 1f / (2 * e - 1) ;
val b = if (e % 2 == 0) -1 else 1 ;
a * b * 4
}
).reduce(_+_)
res19: Float = 3.1415954