文章目录
1.RDD五大特性对应的源码
上图中是对应的五个方法,输入以及输出。第一个方法和第三个方法在dirver中运行,第二个方法在exector中运行
2.stage
stage是对job的划分,遇到shuffle就划分,一个stage有多个tasks,同一个job间的stage具有依赖依赖关系,前者必须结束才能进行后者的计算。
2.1WC演示
如下的wc测试代码
scala> val rdd = sc.parallelize(List(1,1,2,3,1,3,45,6,4,3))
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:24
scala> rdd.collect
collect collectAsync
scala> rdd.collect()
res0: Array[Int] = Array(1, 1, 2, 3, 1, 3, 45, 6, 4, 3)
scala> rdd.map((_,1)).collect()
res1: Array[(Int, Int)] = Array((1,1), (1,1), (2,1), (3,1), (1,1), (3,1), (45,1), (6,1), (4,1), (3,1))
scala> rdd.map((_,1)).reduceByKeycollect()
reduceByKey reduceByKeyLocally
scala> rdd.map((_,1)).reduceByKey(_+_).collect()
res2: Array[(Int, Int)] = Array((4,1), (6,1), (2,1), (45,1), (1,3), (3,3))
****
查询web界面,如下图,可知三个collect的action产生了3三个作业
点击第三个作业,查看DAG图,由图知道是有两个stage的,因为reducebykey是会产生shuffle操作,遇到shuffle就会切割stage,repartition也会产生shuffle操作,task数即是每个stage的RDD分区和
3.持久数据(persisit/catch)
当一个RDD被多次调用时,可以考虑使用缓存。
3.1数据存储级别
val NONE = new StorageLevel(false, false, false, false)
val DISK_ONLY = new StorageLevel(true, false, false, false)
val DISK_ONLY_2 = new StorageLevel(true, false, false, false, 2)
val MEMORY_ONLY = new StorageLevel(false, true, false, true)
val MEMORY_ONLY_2 = new StorageLevel(false, true, false, true, 2)
val MEMORY_ONLY_SER = new StorageLevel(false, true, false, false)
val MEMORY_ONLY_SER_2 = new StorageLevel(false, true, false, false, 2)
val MEMORY_AND_DISK = new StorageLevel(true, true, false, true)
val MEMORY_AND_DISK_2 = new StorageLevel(true, true, false, true, 2)
val MEMORY_AND_DISK_SER = new StorageLevel(true, true, false, false)
val MEMORY_AND_DISK_SER_2 = new StorageLevel(true, true, false, false, 2)
val OFF_HEAP = new StorageLevel(true, true, true, false, 1)
spark core的catch()调的是存储级别为MEMORY_ONLY的persist方法
persisit()方法是lazy的,但是释放缓存数据的unpersisit()方法是eager的
注意:
- 对于spark core 生产中只选择MEMORY_ONLY和MEMORY_ONLY_SER这两种存储级别,
- cpu的计算速度远远大于从内存中读取数据的速度更大于从磁盘中读取数据的速度,有时重新计算数据比读取缓存的数据速度更快,故不需要将数据存多份或者存储磁盘,故spark core生产只选择上述两种存储类型
- 序列化使得占用的内存更少,但是序列化以及反序列化是需要耗时的,同时耗费CPU,故具体要根据资源以及业务去选择
- 使用缓存,最好养成结束释放缓存的良好规范
4.宽窄依赖(Narrow/Wide)
对于窄依赖的RDD,若某个分区丢失只需将父RDD相应的分区算出即可。
4.1宽窄依赖定义
- Narrow:父RDD的Partition只能被子RDD的Partiton使用一次,即为窄依赖
- 宽依赖:与窄依赖想反
4.2常见产生宽窄依赖的算子
如上图,map、filter、union、join with inputs co-partitioned(分区数据1:1关联关系) 产生的RDD都是窄依赖
groupByKey、reduceByKey、join with inputs not-partitioned,repartition产生的RDD都是宽依赖
注意:由上图我们可知join操作产生的RDD不一定是宽依赖
4.3宽窄依赖与stage
如上图,每遇到一个shuffle操作就会划分一个stage,故有三个stage ,每个stage中只有窄依赖,计算时窄依赖的RDD数据是不会落地的,窄依赖计算时stage从头干到尾,故判定某个RDD是否是宽依赖可以根据是否由shuffle产生来确定。stage1有3个task,stage2有4个task,stage3有7个task
5.键值对(key-value pairs)
RDD中数据结构可以是各种各样的,但是最常用的是kv结构,如groupByKey、reduceByKey等操作都是要求RDD的数据是KV结构,故scala中若元素的结构为Tuplue2,则RDD会隐式转换为PairRDDFunctions,非常方便。即groupByKey、reduceByKey等是PairRDDFunctions的方法,而不是RDD类的方法