RDD(Resilient Distributed Dataset)叫做分布式数据集,是Spark中最基本的数据抽象,它代表一个不可变、可分区、里面的元素可并行计算的集合。在 Spark 中,对数据的所有操作不外乎创建 RDD、转化已有RDD 以及调用 RDD 操作进行求值。每个 RDD 都被分为多个分区,这些分区运行在集群中的不同节点上。RDD 可以包含 Python、Java、Scala 中任意类型的对象, 甚至可以包含用户自定义的对象。RDD具有数据流模型的特点:自动容错、位置感知性调度和可伸缩性。RDD允许用户在执行多个查询时显式地将工作集缓存在内存中,后续的查询能够重用工作集,这极大地提升了查询速度。
1、RDD是整个Spark的计算基石。是分布式数据的抽象,为用户屏蔽了底层复杂的计算和映射环境
1、RDD是不可变的,如果需要在一个RDD上进行转换操作,则会生成一个新的RDD
2、RDD是分区的,RDD里面的具体数据是分布在多台机器上的Executor里面的。堆内内存和堆外内存 + 磁盘。
3、RDD是弹性的。
1、存储:Spark会根据用户的配置或者当前Spark的应用运行情况去自动将RDD的数据缓存到内存或者磁盘。他是一个对用户不可见的封装的功能。
2、容错:当你的RDD数据被删除或者丢失的时候,可以通过血统或者检查点机制恢复数据。这个用户透明的。
3、计算:计算是分层的,有应用->JOb->Stage->TaskSet-Task 每一层都有对应的计算的保障与重复机制。保障你的计算不会由于一些突发因素而终止。
4、分片:你可以根据业务需求或者一些算子来重新调整RDD中的数据分布。
2、Spark Core干了什么东西,其实就是在操作RDD
RDD的创建--》RDD的转换--》RDD的缓存--》RDD的行动--》RDD的输出。
RDD的创建
1.从集合中创建RDD,由一个已经存在的Scala集合创建,集合并行化
val rdd1 = sc.parallelize(Array(1,2,3,4,5,6,7,8)) rdd1.collect rdd1.collect.foreach(println _)
val rdd2 = sc.makeRDD(List(0 to 10)) rdd2.collect rdd2.collect.foreach(println _)
2.从外部存储创建RDD
sc.textFile("path")
3.从其他RDD创建
RDD转换与行动
//********************** 转换操作 *********************
1、def map[U: ClassTag](f: T => U): RDD[U] 将函数应用于RDD的每一元素,并返回一个新的RDD
val rdd1_1 = rdd1.map(_*2)
2、def filter(f: T => Boolean): RDD[T] 通过提供的产生boolean条件的表达式来返回符合结果为True新的RDD
val filter = sourceFilter.filter(_.contains("xiao"))
3、def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U]
将函数应用于RDD中的每一项,对于每一项都产生一个集合,并将集合中的元素压扁成一个集合。
val flatMap = sourceFlat.flatMap(1 to _)
4、def mapPartitions[U: ClassTag]( f: Iterator[T] => Iterator[U], preservesPartitioning: Boolean = false): RDD[U]
将函数应用于RDD的每一个分区,每一个分区运行一次,函数需要能够接受Iterator类型,然后返回Iterator。
scala> val rdd = sc.parallelize(List(("kpop","female"),("zorro","male"),("mobin","male"),("lucy","female")))
rdd: org.apache.spark.rdd.RDD[(String, String)] = ParallelCollectionRDD[16] at parallelize at <console>:24
scala> :paste
// Entering paste mode (ctrl-D to finish)
def partitionsFun(iter : Iterator[(String,String)]) : Iterator[String] = {
var woman = List[String]()
while (iter.hasNext){
val next = iter.next()
next match {
case (_,"female") => woman = next._1 :: woman
case _ =>
}
}
woman.iterator
}
// Exiting paste mode, now interpreting.
partitionsFun: (iter: Iterator[(String, String)])Iterator[String]
scala> val result = rdd.mapPartitions(partitionsFun)
result: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[17] at mapPartitions at <console>:28
scala> result.collect()
res13: Array[String] = Array(kpop, lucy)
5、def mapPartitionsWithIndex[U: ClassTag]( f: (Int, Iterator[T]) => Iterator[U], preservesPartitioning: Boolean = false): RDD[U]
将函数应用于RDD中的每一个分区,每一个分区运行一次,函数能够接受 一个分区的索引值 和一个代表分区内所有数据的Iterator类型,需要返回Iterator类型。
scala> val rdd = sc.parallelize(List(("kpop","female"),("zorro","male"),("mobin","male"),("lucy","female")))
rdd: org.apache.spark.rdd.RDD[(String, String)] = ParallelCollectionRDD[18] at parallelize at <console>:24
scala> :paste
// Entering paste mode (ctrl-D to finish)
def partitionsFun(index : Int, iter : Iterator[(String,String)]) : Iterator[String] = {
var woman = List[String]()
while (iter.hasNext){
val next = iter.next()
next match {
case (_,"female") => woman = "["+index+"]"+next._1 :: woman
case _ =>
}
}
woman.iterator
}
// Exiting paste mode, now interpreting.
partitionsFun: (index: Int, iter: Iterator[(String, String)])Iterator[String]
scala> val result = rdd.mapPartitionsWithIndex(partitionsFun)
result: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[19] at mapPartitionsWithIndex at <console>:28
scala> result.collect()
res14: Array[String] = Array([0]kpop, [3]lucy)
6、def sample(withReplacement: Boolean, fraction: Double, seed: Long = Utils.random.nextLong): RDD[T]
在RDD中以seed为种子,返回大致上有fraction比例个数据样本RDD,withReplacement表示是否采用放回式抽样。
以指定的随机种子随机抽样出数量为fraction的数据,withReplacement表示是抽出的数据是否放回,
true为有放回的抽样,false为无放回的抽样,seed用于指定随机数生成器种子。例子从RDD中随机且
有放回的抽出50%的数据,随机种子值为3(即可能以1 2 3的其中一个起始值)。
val sample1 = rdd1.sample(true,0.5,3)
7、def union(other: RDD[T]): RDD[T] 将两个RDD中的元素进行合并,返回一个新的RDD
val rdd3 = rdd1.union(rdd2)
8、def intersection(other: RDD[T]): RDD[T] 将两个RDD做交集,返回一个新的RDD。
val rdd3 = rdd1.intersection(rdd2)
9、def distinct(): RDD[T] 将当前RDD进行去重后,返回一个新的RDD,
默认情况下,只有8个并行任务来操作,但是可以传入一个可选的numTasks参数改变它。
val unionRDD = distinctRdd.distinct()
val unionRDD = distinctRdd.distinct(2)
10、def partitionBy(partitioner: Partitioner): RDD[(K, V)] 根据设置的分区器重新将RDD进行分区,返回新的RDD。
scala> val rdd = sc.parallelize(Array((1,"aaa"),(2,"bbb"),(3,"ccc"),(4,"ddd")),4)
rdd: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[44] at parallelize at <console>:24
scala> rdd.partitions.size
res24: Int = 4
scala> var rdd2 = rdd.partitionBy(new org.apache.spark.HashPartitioner(2))
rdd2: org.apache.spark.rdd.RDD[(Int, String)] = ShuffledRDD[45] at partitionBy at <console>:26
scala> rdd2.partitions.size
res25: Int = 2
11、def reduceByKey(func: (V, V) => V): RDD[(K, V)] 在一个(K,V)的RDD上调用,返回一个(K,V)的RDD,
使用指定的reduce函数,将相同key的值聚合到一起,reduce任务的个数可以通过第二个可选的参数来设置
scala> val rdd = sc.parallelize(List(("female",1),("male",5),("female",5),("male",2)))
rdd: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[46] at parallelize at <console>:24
scala> val reduce = rdd.reduceByKey((x,y) => x+y)
reduce: org.apache.spark.rdd.RDD[(String, Int)] = ShuffledRDD[47] at reduceByKey at <console>:26
scala> reduce.collect()
res29: Array[(String, Int)] = Array((female,6), (male,7))
12、def groupByKey(): RDD[(K, Iterable[V])] 将相同Key的值进行聚集,输出一个(K, Iterable[V])类型的RDD
scala> val words = Array("one", "two", "two", "three", "three", "three")
words: Array[String] = Array(one, two, two, three, three, three)
scala> val wordPairsRDD = sc.parallelize(words).map(word => (word, 1))
wordPairsRDD: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[4] at map at <console>:26
scala> val group = wordPairsRDD.groupByKey()
group: org.apache.spark.rdd.RDD[(String, Iterable[Int])] = ShuffledRDD[5] at groupByKey at <console>:28
scala> group.collect()
res1: Array[(String, Iterable[Int])] = Array((two,CompactBuffer(1, 1)), (one,CompactBuffer(1)), (three,CompactBuffer(1, 1, 1)))
scala> val map = group.map(t => (t._1, t._2.sum))
map: org.apache.spark.rdd.RDD[(String, Int)] = MapPartitionsRDD[7] at map at <console>:30
scala> map.collect()
res4: Array[(String, Int)] = Array((two,2), (one,1), (three,3))
13、def combineByKey[C](createCombiner: V => C, mergeValue: (C, V) => C, mergeCombiners: (C, C) => C, numPartitions: Int): RDD[(K, C)]
对相同K,把V合并成一个集合。
createCombiner: combineByKey() 会遍历分区中的所有元素,因此每个元素的键要么还没有遇到过,要么就 和之前的某个元素的键相同。
如果这是一个新的元素,combineByKey() 会使用一个叫作 createCombiner() 的函数来创建那个键对应的累加器的初始值。
mergeValue: 如果这是一个在处理当前分区之前已经遇到的键, 它会使用 mergeValue() 方法将该键的累加器对应的当前值与这个新的值进行合并。
mergeCombiners: 由于每个分区都是独立处理的, 因此对于同一个键可以有多个累加器。如果有两个或者更多的分区都有对应同一个键的累加器,
就需要使用用户提供的 mergeCombiners() 方法将各个分区的结果进行合并。
scala> val scores = Array(("Fred", 88), ("Fred", 95), ("Fred", 91), ("Wilma", 93), ("Wilma", 95), ("Wilma", 98))
scores: Array[(String, Int)] = Array((Fred,88), (Fred,95), (Fred,91), (Wilma,93), (Wilma,95), (Wilma,98))
scala> val input = sc.parallelize(scores)
input: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[52] at parallelize at <console>:26
scala> val combine = input.combineByKey(
| (v)=>(v,1),
| (acc:(Int,Int),v)=>(acc._1+v,acc._2+1),
| (acc1:(Int,Int),acc2:(Int,Int))=>(acc1._1+acc2._1,acc1._2+acc2._2))
combine: org.apache.spark.rdd.RDD[(String, (Int, Int))] = ShuffledRDD[53] at combineByKey at <console>:28
scala> val result = combine.map{
| case (key,value) => (key,value._1/value._2.toDouble)}
result: org.apache.spark.rdd.RDD[(String, Double)] = MapPartitionsRDD[54] at map at <console>:30
scala> result.collect()
res33: Array[(String, Double)] = Array((Wilma,95.33333333333333), (Fred,91.33333333333333))
14、def aggregateByKey[U: ClassTag](zeroValue: U, partitioner: Partitioner)(seqOp: (U, V) => U,
combOp: (U, U) => U): RDD[(K, U)] 通过seqOp函数将每一个分区里面的数据和初始值迭代带入函数返回最终值,comOp将每一个分区返回的最终值根据key进行合并操作。
scala> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
rdd: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[12] at parallelize at <console>:24
scala> val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_)
agg: org.apache.spark.rdd.RDD[(Int, Int)] = ShuffledRDD[13] at aggregateByKey at <console>:26
scala> agg.collect()
res7: Array[(Int, Int)] = Array((3,8), (1,7), (2,3))
scala> agg.partitions.size
res8: Int = 3
scala> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),1)
rdd: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[10] at parallelize at <console>:24
scala> val agg = rdd.aggregateByKey(0)(math.max(_,_),_+_).collect()
agg: Array[(Int, Int)] = Array((1,4), (3,8), (2,3))
15、def foldByKey( eroValue: V, partitioner: Partitioner)(func: (V, V) => V): RDD[(K, V)]
aggregateByKey的简化操作,seqop和combop相同。
val agg = rdd.foldByKey(0)(_+_)
16、def sortByKey(ascending: Boolean = true, numPartitions: Int = self.partitions.length)
: RDD[(K, V)] 在一个(K,V)的RDD上调用,K必须实现Ordered接口,返回一个按照key进行排序的(K,V)的RDD
rdd.sortByKey(true).collect()
17、def sortBy[K](f: (T) => K, ascending: Boolean = true, numPartitions: Int = this.partitions.length)
(implicit ord: Ordering[K], ctag: ClassTag[K]): RDD[T]
底层实现还是使用sortByKey,只不过使用fun生成的新key进行排序。
rdd.sortBy(x => x%3).collect()
18、def join[W](other: RDD[(K, W)], partitioner: Partitioner): RDD[(K, (V, W))]
在类型为(K,V)和(K,W)的RDD上调用,返回一个相同key对应的所有元素对在一起的(K,(V,W))的RDD,
但是需要注意的是,他只会返回key在两个RDD中都存在的情况。
scala> rdd.join(rdd1).collect()
res13: Array[(Int, (String, Int))] = Array((1,(a,4)), (2,(b,5)), (3,(c,6)))
19、def cogroup[W](other: RDD[(K, W)], partitioner: Partitioner): RDD[(K, (Iterable[V], Iterable[W]))]
在类型为(K,V)和(K,W)的RDD上调用,返回一个(K,(Iterable<V>,Iterable<W>))类型的RDD,
注意,如果V和W的类型相同,也不放在一块,还是单独存放。
rdd.cogroup(rdd1).collect()
20、def cartesian[U: ClassTag](other: RDD[U]): RDD[(T, U)] 做两个RDD的笛卡尔积,返回对偶的RDD
rdd1.cartesian(rdd2).collect()
21、def pipe(command: String): RDD[String] 对于每个分区,都执行一个perl或者shell脚本,返回输出的RDD,
注意,如果你是本地文件系统中,需要将脚本放置到每个节点上。
scala> val rdd = sc.parallelize(List("hi","Hello","how","are","you"),1)
rdd: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[50] at parallelize at <console>:24
scala> rdd.pipe("/home/bigdata/pipe.sh").collect()
res18: Array[String] = Array(AA, >>>hi, >>>Hello, >>>how, >>>are, >>>you)
scala> val rdd = sc.parallelize(List("hi","Hello","how","are","you"),2)
rdd: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[52] at parallelize at <console>:24
scala> rdd.pipe("/home/bigdata/pipe.sh").collect()
res19: Array[String] = Array(AA, >>>hi, >>>Hello, AA, >>>how, >>>are, >>>you)
pipe.sh:
#!/bin/sh
echo "AA"
while read LINE; do
echo ">>>"${LINE}
done
22、def coalesce(numPartitions: Int, shuffle: Boolean = false,
partitionCoalescer: Option[PartitionCoalescer] = Option.empty)
(implicit ord: Ordering[T] = null) : RDD[T] 缩减分区数,用于大数据集过滤后,提高小数据集的执行效率。
val coalesceRDD = rdd.coalesce(3)
23、def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T]
根据你传入的分区数重新通过网络分区所有数据,重型操作。
val rerdd = rdd.repartition(2)
24、def repartitionAndSortWithinPartitions(partitioner: Partitioner): RDD[(K, V)]
性能要比repartition要高。在给定的partitioner内部进行排序
val rerdd = rdd.repartitionAndSortWithinPartitions(2)
25、def glom(): RDD[Array[T]] 将每一个分区形成一个数组,形成新的RDD类型时RDD[Array[T]]
rdd.glom().collect()
26、def mapValues[U](f: V => U): RDD[(K, U)] 将函数应用于(k,v)结果中的v,返回新的RDD
scala> rdd3.mapValues(_+"|||").collect()
res26: Array[(Int, String)] = Array((1,a|||), (1,d|||), (2,b|||), (3,c|||))
27、def subtract(other: RDD[T]): RDD[T] 计算差的一种函数去除两个RDD中相同的元素,不同的RDD将保留下来。
rdd.subtract(rdd1).collect()
Action(行动操作)
******************* 行动操作 *****************
1、def takeSample( withReplacement: Boolean, num: Int, seed: Long = Utils.random.nextLong): Array[T] 抽样但是返回一个scala集合。
2、def reduce(f: (T, T) => T): T 通过func函数聚集RDD中的所有元素
scala> val rdd1 = sc.makeRDD(1 to 10,2)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[85] at makeRDD at <console>:24
scala> rdd1.reduce(_+_)
res50: Int = 55
scala> val rdd2 = sc.makeRDD(Array(("a",1),("a",3),("c",3),("d",5)))
rdd2: org.apache.spark.rdd.RDD[(String, Int)] = ParallelCollectionRDD[86] at makeRDD at <console>:24
scala> rdd2.reduce((x,y)=>(x._1 + y._1,x._2 + y._2))
res51: (String, Int) = (adca,12)
3、def collect(): Array[T] 在驱动程序中,以数组的形式返回数据集的所有元素
4、def count(): Long 返回RDD中的元素个数
5、def first(): T 返回RDD中的第一个元素
6、def take(num: Int): Array[T] 返回RDD中的前n个元素
7、def takeOrdered(num: Int)(implicit ord: Ordering[T]) 返回前几个的排序
8、def aggregate[U: ClassTag](zeroValue: U)(seqOp: (U, T) => U, combOp: (U, U) => U): U
aggregate函数将每个分区里面的元素通过seqOp和初始值进行聚合,然后用combine函数将每个分区的结果和
初始值(zeroValue)进行combine操作。这个函数最终返回的类型不需要和RDD中元素类型一致。
scala> var rdd1 = sc.makeRDD(1 to 10,2)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[88] at makeRDD at <console>:24
scala> rdd1.aggregate(1)(
| {(x : Int,y : Int) => x + y},
| {(a : Int,b : Int) => a + b}
| )
res56: Int = 58
scala> rdd1.aggregate(1)(
| {(x : Int,y : Int) => x * y},
| {(a : Int,b : Int) => a + b}
| )
res57: Int = 30361
9、def fold(zeroValue: T)(op: (T, T) => T): T 折叠操作,aggregate的简化操作,seqop和combop一样。
scala> var rdd1 = sc.makeRDD(1 to 4,2)
rdd1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[90] at makeRDD at <console>:24
scala> rdd1.aggregate(1)(
| {(x : Int,y : Int) => x + y},
| {(a : Int,b : Int) => a + b}
| )
res59: Int = 13
scala> rdd1.fold(1)(_+_)
res60: Int = 13
10、def saveAsTextFile(path: String): Unit 将RDD以文本文件的方式保存到本地或者HDFS中
11、def saveAsObjectFile(path: String): Unit 将RDD中的元素以序列化后对象形式保存到本地或者HDFS中。
12、def countByKey(): Map[K, Long] 针对(K,V)类型的RDD,返回一个(K,Int)的map,表示每一个key对应的元素个数。
scala> val rdd = sc.parallelize(List((1,3),(1,2),(1,4),(2,3),(3,6),(3,8)),3)
rdd: org.apache.spark.rdd.RDD[(Int, Int)] = ParallelCollectionRDD[95] at parallelize at <console>:24
scala> rdd.countByKey()
res63: scala.collection.Map[Int,Long] = Map(3 -> 2, 1 -> 3, 2 -> 1)
13、def foreach(f: T => Unit): Unit 在数据集的每一个元素上,运行函数func进行更新。
scala> var rdd = sc.makeRDD(1 to 10,2)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[107] at makeRDD at <console>:24
scala> var sum = sc.accumulator(0)
warning: there were two deprecation warnings; re-run with -deprecation for details
sum: org.apache.spark.Accumulator[Int] = 0
scala> rdd.foreach(sum+=_)
scala> sum.value
res68: Int = 55
scala> rdd.collect().foreach(println)
1
2
3
4
5
6
7
8
9
10
注意:当你在RDD中使用到了class的方法或者属性的时候,该class需要继承java.io.Serializable接口,
或者可以将属性赋值为本地变量来防止整个对象的传输。
数值RDD的统计操作
Spark 对包含数值数据的 RDD 提供了一些描述性的统计操作。 Spark 的数值操作是通过流式算法实现的,允许以每次一个元素的方式构建出模型。这些 统计数据都会在调用 stats() 时通过一次遍历数据计算出来,并以 StatsCounter 对象返回。
RDD的依赖关系
1、RDD的依赖关系分为窄依赖和宽依赖。
2、窄依赖是父RDD的每一个分区最多被一个子RDD的分区应用,也就是出度为1。
3、宽依赖是父RDD的每一个分区被多个子RDD的分区来应用,也就是出度大于等于2。
4、应用在整个过程中,RDD之间形成的产生关系,就叫做血统关系,RDD在没有持久化的时候默认是不保存的,如果需要那么就要根据血统关系来重新计算。
5、应用在执行过程中,是分为多个Stage来进行的,划分Stage的关键就是判断是不是存在宽依赖。从Action往前去推整个Stage的划分。
DAG的生成
DAG(Directed Acyclic Graph)叫做有向无环图,原始的RDD通过一系列的转换就就形成了DAG,根据RDD之间的依赖关系的不同将DAG划分成不同的Stage,对于窄依赖,partition的转换处理在Stage中完成计算。对于宽依赖,由于有Shuffle的存在,只能在parent RDD处理完成后,才能开始接下来的计算,因此宽依赖是划分Stage的依据。
RDD的持久化
1、RDD的持久化主要通过persist和cache操作来实现。cache操作就相当于StoageLevel为MEMORY_ONLY的persist操作。
2、持久化它的存储等级可以分为:存储的位置(磁盘、内存、非堆内存)、是否序列化、存储的份数(1,2)。
3、取消RDDL的持久化、unpersist
scala> val rdd = sc.makeRDD(1 to 10)
rdd: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[19] at makeRDD at <console>:25
scala> val nocache = rdd.map(_.toString+"["+System.currentTimeMillis+"]")
nocache: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[20] at map at <console>:27
scala> val cache = rdd.map(_.toString+"["+System.currentTimeMillis+"]")
cache: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[21] at map at <console>:27
scala> cache.cache
res24: cache.type = MapPartitionsRDD[21] at map at <console>:27
scala> nocache.collect
res25: Array[String] = Array(1[1505479375155], 2[1505479374674], 3[1505479374674], 4[1505479375153], 5[1505479375153], 6[1505479374675], 7[1505479375154], 8[1505479375154], 9[1505479374676], 10[1505479374676])
scala> nocache.collect
res26: Array[String] = Array(1[1505479375679], 2[1505479376157], 3[1505479376157], 4[1505479375680], 5[1505479375680], 6[1505479376159], 7[1505479375680], 8[1505479375680], 9[1505479376158], 10[1505479376158])
scala> nocache.collect
res27: Array[String] = Array(1[1505479376743], 2[1505479377218], 3[1505479377218], 4[1505479376745], 5[1505479376745], 6[1505479377219], 7[1505479376747], 8[1505479376747], 9[1505479377218], 10[1505479377218])
scala> cache.collect
res28: Array[String] = Array(1[1505479382745], 2[1505479382253], 3[1505479382253], 4[1505479382748], 5[1505479382748], 6[1505479382257], 7[1505479382747], 8[1505479382747], 9[1505479382253], 10[1505479382253])
scala> cache.collect
res29: Array[String] = Array(1[1505479382745], 2[1505479382253], 3[1505479382253], 4[1505479382748], 5[1505479382748], 6[1505479382257], 7[1505479382747], 8[1505479382747], 9[1505479382253], 10[1505479382253])
scala> cache.collect
res30: Array[String] = Array(1[1505479382745], 2[1505479382253], 3[1505479382253], 4[1505479382748], 5[1505479382748], 6[1505479382257], 7[1505479382747], 8[1505479382747], 9[1505479382253], 10[1505479382253])
cache.persist(org.apache.spark.storage.StorageLevel.MEMORY_ONLY)
RDD Checkpoint检查点
1、checkpoint的使用
1、我需要先创建一个RDD
2、需要设置sparkContext他的checkpoint目录,如果你要用spark-shell,那么就是sc.setCheckpointDir("....")
3、在RDD上调用checkpoint方法
4、触发RDD的行动操作,让RDD的数据真实写入checkpoint目录。
注意:整个checkpoint的读取时用户透明
scala> val data = sc.parallelize(1 to 100 , 5)
data: org.apache.spark.rdd.RDD[Int] =
ParallelCollectionRDD[12] at parallelize at <console>:12
scala> sc.setCheckpointDir("hdfs://master01:9000/checkpoint")
scala> data.checkpoint
scala> data.count
scala> val ch1 = sc.parallelize(1 to 2)
ch1: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[33] at parallelize at <console>:25
scala> val ch2 = ch1.map(_.toString+"["+System.currentTimeMillis+"]")
ch2: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[36] at map at <console>:27
scala> val ch3 = ch1.map(_.toString+"["+System.currentTimeMillis+"]")
ch3: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[37] at map at <console>:27
scala> ch3.checkpoint
scala> ch2.collect
res62: Array[String] = Array(1[1505480940726], 2[1505480940243])
scala> ch2.collect
res63: Array[String] = Array(1[1505480941957], 2[1505480941480])
scala> ch2.collect
res64: Array[String] = Array(1[1505480942736], 2[1505480942257])
scala> ch3.collect
res65: Array[String] = Array(1[1505480949080], 2[1505480948603])
scala> ch3.collect
res66: Array[String] = Array(1[1505480948683], 2[1505480949161])
scala> ch3.collect
res67: Array[String] = Array(1[1505480948683], 2[1505480949161])
RDD分区
1、Spark提供了RDD的分区操作,主要针对K-V结构,提供了诸如HashPartitioner和RangePartitioner
2、自定义分区器与使用
1、首先需要创建一个新的class继承Partitioner
2、复写抽象方法
3、通过partitionBy操作应用新的分区器。
RDD 累加器
1、累加器的使用
1、首先需要通过sparkcontext去声明一个累加器,方法时accumulator,在声明的过程中需要提供累加器的初始值。
2、你可以在转换操作或者行动上直接使用累加器, 可以通过 += 操作符增加累加器的值,但是不能够读取累加器。
注意: 一般不推荐在转换操作使用累加器。一般推荐在行动操作中去使用。
3、 Driver可以通过 累加器.value 操作类读取累加器的值并输出。
2、自定义累加器
1、你需要继承AccumulatorV2这个虚拟类,然后提供类型参数1、你的增加值类型参数,2、你的输出值类型参数。
1、// 你这个累加器内部数据结构是否为空
override def isZero: Boolean = ???
2、// 让SPark框架能够调用copy函数产生一个新的系统的类=累加器实例。
override def copy(): AccumulatorV2[String, util.Set[String]] = ???
3、// 重置你的累加器数据结构
override def reset(): Unit = ???
4、// 提供修改累加器的方法
override def add(v: String): Unit = ???
5、// 用于合并多个分区的累加器实例
override def merge(other: AccumulatorV2[String, util.Set[String]]): Unit = ???
6、// 通过value方法输出你的累加器最终结果
override def value: util.Set[String] = ???
2、需要创建一个SparkContext
3、需要创建一个自定义累加器实例
4、需要通过SparkContext去注册你的累加器, sc.register(accum, "logAccum")
5、需要在转换或者行动操作中使用累加器。
6、在Driver中输出累加器的结果。
RDD 广播变量
1、广播变量的出现时为了解决只读大对象分发的问题。
2、如果不是广播变量,那么使用的变量会跟分区进行分发,效率比较低。
3、广播变量的使用:
1、通过SparkContext.broadcast(对象) 来声明一个广播变量。
2、通过广播变量的变量名的value方法来获取广播变量的值。
Spark输入输出
1、文本文件它的输入输出:textFile和saveAsTextFile----在写出text文件的时候,每一个partition会单独写出,文件系统支持所有和Hadoop文件系统兼容的文件系统
2、JSON文件或者CSV文件:
这种有格式的文件的输入和输出还是通过文本文件的输入和输出来支持的,Spark Core没有内置对JSON文件和CSV文件的解析和反解析功能,这个解析功能是需要用户自己根据需求来定制的。
注意:JSON文件的读取如果需要多个partition来读,那么JSOn文件一般一行一个json。如果你的JSON是跨行的,那么需要整体读入所有数据,并整体解析。
3、Sequence文件--sc.sequenceFile:HDFS中的文件block数为1,那么Spark设定了最小的读取partition数为2
4、读取或者保存ObjectFile---sc.saveAsObjectFile 输出的是对象的形式
1、对于ObjectFile他的读取和保存使用了读取和保存SequenceFile的API,也最终调用了hadoop的API。
2、ObjectFile的读取使用 objectFile进行。
3、ObjectFile的输出直接使用 saveAsObjectFile来进行输出。
4、需要注意的是,在读取ObjectFile的时候需要制定对象的类型,而并不是K-V的类型。
5.HadoopAPI的读取和输入:
读取:newApiHadoopFile和newApiHadoopRDD两个方法,最终都是调用newApiHadoopRDD来进行实现。
输出:saveAsNewApiHadoopFile和saveAsNewApiHadoopDataset两个方法,最终都是调用saveAsNewApiHadoopDataset这个方法进行实现。
比旧的hadoopApi 主要进行可以 压缩存储文件到HDFS--进行了优化。
6.关系型数据库的输入输出:JdbcRDD 里面包括了驱动,数据库用户名/密码。
1、对于关系型数据库的读取使用JdbcRDD来进行实现,JdbcRDD需要依次传入sparkContext、获取JDBC Connection的无参方法、查询数据库的SQL语句,id的下界、id的上界、分区数、提供解析Result Set的函数。
2、对于关系型数据库的输出,直接采用jdbc执行Insert语句或者Update语句进行实现。