创建RDD:什么是RDD请点击该链接
基于已有的数据集合并行化创建RDD
val conf = new SparkConf().setAppName("").setMaster("local")
val sc = new SparkContext(conf)
val array = Array(1, 2, 3, 4, 5)
//并行化创建RDD,并且指定该RDD的分区数量为3
val rdd: RDD[Int] = sc.parallelize(array,3)
val sparkConf = new SparkConf().setMaster("local").setAppName("RDDTest")
val sc = new SparkContext(sparkConf)
//调动SparkContext的makeRDD方法,该方法底层其实就是调用并行化创建RDD
//的方法parallelize。该方创建RDD并且指定分区位置索引
val mRDD: RDD[(Int, Array[String])] = sc.makeRDD(List((1, Array("a", "b", "c")), (2, Array("d", "e", "f"))))
mRDD.foreach(x =>{
val index: Int = x._1
for (elem <- x._2) {
print(index,elem)
}
})
基于外部文件创建RDD
val conf = new SparkConf().setAppName("").setMaster("local")
val sc = new SparkContext(conf)
//基于本地文件创建RDD,并且指定该RDD的分区数量为3
val rdd: RDD[String] = sc.textFile("C:\\a.txt",3)
基于Hadoop文件系统创建RDD
val conf = new SparkConf().setAppName("").setMaster("local")
val sc = new SparkContext(conf)
//基于Hdfs文件创建RDD,格式一定是hdfs://....,并且指定该RDD的分区数量为3
val rdd: RDD[String] = sc.textFile("hdfs://hadoop001:8020/spark.txt",3)
其他
SparkContext
的wholeTextFiles()
方法,可以针对一个目录中的大量小文件,返回由(fileName,fileContent)
组成的Tuple
。该方法返回的是文件名字和文件中的具体内容;而普通的textFile()
方法返回的RDD
中,每个元素就是文本中一行文本。
val conf = new SparkConf().setAppName("").setMaster("local")
val sc = new SparkContext(conf)
//(String,String):第一个是文件名,第二个是文件的具体内容,并且指定该RDD分区数量为3
val pairRDD: RDD[(String, String)] = sc.wholeTextFiles("xxxx",3)
SparkContext
的sequenceFileK,V
方法,可以针对SequenceFile
创建RDD
,K
和V
泛型类型就是SequenceFile
的key
和value
的类型。K
和V
要求必须是Hadoop
的序列化机制,比如IntWritable
、Text
等。
val conf = new SparkConf().setAppName("").setMaster("local")
val sc = new SparkContext(conf)
//基于sequenceFile常见RDD,并指定分区数目为3
sc.sequenceFile("hdfs://hadoop001:8020/squence.txt", classOf[IntWritable], classOf[Text],3)
SparkContext
的hadoopRDD()
方法,对于Hadoop
的自定义输入类型,可以创建RDD
。该方法接收JobConf
、InputFormatClass
、Key
和Value
的Class
。SparkContext
的objectFile()
方法,可以针对之前调用的RDD
的saveAsObjectFile()
创建的对象序列化的文件,反序列化文件中的数据,并创建一个RDD
。Transformation(转换)
Transformation
是一种RDD
的转换算子,它利用一个RDD
经过转换算子生成一个新的RDD
,该转换是一种延迟算子,当从一个RDD
转换成新的RDD
的时候,具体的转换算子不会立即执行,而是知识记录了由旧的RDD
转换成新的RDD
的逻辑操作。方法
方法名 | 方法解释 |
---|---|
map(func) | 通过func 函数作用在每个元素上从而返回一个新的数据集 |
filter(func) | 通过func 函数对每个元素进行过滤,然后返回一个新的数据集 |
flatMap(func) | 类似于Map ,只不过是利用func 函数将每个单一的元素映射成多个区段,返回的是Seq |
mapPartitions(func) | 类似于Map ,只不过是在RDD 的每个分区上运行,所以在T类型的RDD 上运行时,func 必须是Iterator |
mapPartitionsWithIndex(func) | 类似于mapPartitions ,但也为func 提供了一个表示分区索引的整数值,因此当运行在类型为T 的RDD 上时。func 类型必须是(Int, Iterator) => Iterator 。 |
sample(withReplacement, fraction, seed) | |
union(otherDataset) | 将两个数据集合并成一个数据集 |
intersection(otherDataset) | 返回一个新的数据集,该数据己是两个数据集的交集 |
distinct([numPartitions])) | 对RDD中的数据去重 |
sortByKey([ascending], [numPartitions]) | 返回以Key 排序的(K,V) 键值对组成的RDD ,accending 为true 时表示升序,为false 时表示降序,numPartitions 设置分区数,提高作业并行度 |
groupByKey([numPartitions]) | 对于K,V 类型的数据集,按照K 分组,将相同的Key 合并,返回Key 对应值集合的数据类型,(key,Iterabel |
reduceByKey(func, [numPartitions]) | 按Key 进行分组,使用给定的func 函数聚合value 值, numPartitions 设置分区数,提高作业并行度例 |
aggregateByKey(zeroValue)(seqOp, combOp, [numPartitions]) | aggregateByKey 算子其实相当于是针对不同“key” 数据做一个map+reduce 规约的操作.seqOp 操作会聚合各分区中的元素,然后combOp 操作把所有分区的聚合结果再次聚合,两个操作的初始值都是zeroValue . seqOp 的操作是遍历分区中的所有元素(T) ,第一个T 跟zeroValue 做操作,结果再作为与第二个T 做操作的zeroValue ,直到遍历完整个分区。combOp 操作是把各分区聚合的结果,再聚合。aggregate 函数返回一个跟RDD 不同类型的值。因此,需要一个操作seqOp 来把分区中的元素T合并成一个U ,另外一个操作combOp 把所有U 聚合。 |
sortByKey([ascending], [numPartitions]) | 数据集类型为K,V,那么该算子就是根据Key对数据集里的每个元素进行排序 |
join(otherDataset, [numPartitions]) | 对两个RDD 先进行cogroup 操作形成新的RDD ,再对每个Key 下的元素进行笛卡尔积,numPartitions 设置分区数,提高作业并行度 |
cogroup(otherDataset, [numPartitions]) | 对两个RDD (如:(K,V)和(K,W))相同 Key的元素先分别做聚合,最后返回(K,Iterator RDD, numPartitions`设置分区数,提高作业并行度 |
cartesian(otherDataset) | 对两个RDD 中的所有元素进行笛卡尔积操作 |
coalesce(numPartitions) | 对RDD 的分区进行重新分区,shuffle 默认值为false ,当shuffle=false 时,不能增加分区数目,但不会报错,只是分区个数还是原来的 |
repartition(numPartitions) | 重新分区 必须shuffle 参数是要分多少区 少变多 |
repartitionAndSortWithinPartitions(partitioner) | 重新分区,并且在分区中进行排序 |
转换例子
mapPartitions(func)
:传入一个func
函数,然后在分区上执行RDD
转换操作
package com.lyz
import org.apache.spark.rdd.RDD
import org.apache.spark.{SparkConf, SparkContext}
object RDDTest {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local").setAppName("RDDTest")
val sc = new SparkContext(sparkConf)
val rdd: RDD[(String, String)] = sc.makeRDD(List(("1", "x"), ("2", "y"), ("3", "y"), ("4", "x")))
rdd.mapPartitions(partitionFunction).foreach(print(_))
}
def partitionFunction(iter: Iterator[(String, String)]): Iterator[String] = {
var list = List[String]()
while (iter.hasNext) {
val value: (String, String) = iter.next()
value match {
case (_, "y") => list = value._1 :: list
case _ =>
}
}
list.iterator
}
}
Simple(withReplacement, fraction, seed)
:抽样,来估计应用数据分布。
withReplacement
:抽样以后是否放回。fraction
:格式为小数,抽象占数据的比例。 def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setMaster("local").setAppName("RDDTest")
val sc = new SparkContext(sparkConf)
val rdd: RDD[Int] = sc.makeRDD(Array(1,2,3,4,5,6,7,8,10))
//true:是否放回,0.5抽取元素的比例,4:随便填
rdd.sample(true,0.5,4).foreach(print(_))
}
//打印结果为1,3,6,8,10
union(otherDateSet)
:将两个RDD
合并成一个RDD
。otherDataSet
:其他集合形式数据集
def unionTest(sc: SparkContext): Unit = {
val rdd_1: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6, 7, 8, 10))
val rdd_2: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 5, 6, 7, 8, 10))
rdd_1.union(rdd_2).foreach(print(_))
//打印结果为两个RDD的所有元素
}
intersection(otherDataSet)
:求两个集合的交集。otherDataSet
:其他形式的数据集。
//求两个RDD的交际
def intersectionTest(sc: SparkContext): Unit = {
val rdd_1: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4))
val rdd_2: RDD[Int] = sc.makeRDD(Array(3, 4, 5, 6))
rdd_1.intersection(rdd_2).foreach(println(_))
//运行结果为:3,4
}
distinct([numPartitions])
:对数据集进行去重。numPartitions
是一个可选参数,标识分区数,提高并行度。
def distinctTest(sc: SparkContext): Unit = {
val rdd_1: RDD[Int] = sc.makeRDD(Array(1, 2, 3, 4, 1))
rdd_1.distinct().foreach(println(_))
//执行结果为 1,2,3,4
}
sortByKey([ascending],[numPartition])
:对k-v
结构的数据集的元素进行排序。
ascending
:设置排序的方式,默认是true
,表示升序,反之表示降序。numPartiton
是可选参数,标识分区数,提高并行度。//对k-v结构的数据集的元素进行排序
def sortByKey(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "c"), (4, "d")))
//以key对键值对的数据集进行排序,第一个参数默认是true,表示升序排列,第二个参数是分区书,提供计算的并行度
val sortRDD: RDD[(Int, String)] = rdd_1.sortByKey(true, 2)
//打印结果为(1, "a"), (2, "b"), (3, "c"), (4, "d")
}
groupByKey([numPartitions])
:对key-value
结构的数据集按照key
分组,然后把相同key
的value
放入一个迭代器里,并且返回key对应value的迭代器。
numPartitions
:是一个可选参数,标识分区数,提高并行度。def groupByKey(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "c"), (2, "d")))
//对k-v的数据集的元素按照key来分组,返回key对应value的Iterable迭代器,迭代器可以迭代出key对应的所有value
val groupRDD: RDD[(Int, Iterable[String])] = rdd_1.groupByKey()
groupRDD.foreach(println(_))
/** 打印结果为,元素第二个元素为value的Iterable
* (1,CompactBuffer(a, c))
* (2,CompactBuffer(b, d))
*/
}
reduceByKey(func,[numParitions])
:对k-v
结构的数据集按照相同key
进行分组,然后按照给定的func
函数对value进行操作。func
:对value
操作的函数。
numPartitions
:分区数,提高作业并行度。def reduceByKeyTest(sc: SparkContext) {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "c"), (2, "d")))
rdd_1.reduceByKey(_ + _).foreach(println(_))
/**
* 打印结果为:
*
* (1,ac)
* (2,bd)
*/
}
aggregateByKey(zeroValue)(seqOp, combOp, [numPartitions])
:相当于是针对不同"key"
数据做一个map+reduce
操作。
zeroValue
:按照业务需求初始化任意类型的值。seqOp
:它是一个函数,这个函数功能就是能够遍历各个分区上的所有元素,它有两个参数,一个参数是zeroValue
类型,一个是k-v元素中v元素的类型,它的返回值为zeroValue
类型值,它的功能就是利用zeroValue
与各个分区上k-v
中的v
进行不同的操作。combOp
:通过seqOp
操作每个分区得出的结果再次进行处理操作,它的做操对象就是相同key
对应的value
。numPartitions
:分区数,提高作业并行度。def aggregateByKeyTest(sc: SparkContext): Unit = {
//所有元素放在一个分区上,来验证zeroValue与value相加的规则
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "c"), (2, "d")), 1)
rdd_1.aggregateByKey("a")(seqOp, comOp).foreach(println(_))
/**
* 打印结果为
*
*(1,0ace)
*(2,0bd)
*由此得出结论就是 相同key的情况下zeroValue只会与value相加一次,也就是说遇到相同的key,不会从初始值开始相加,而是与上一次该key的计算结果相加
*/
}
/**
* 这个函数作用就是遍历当前分区的所有的元素,在遍历过程中,将初始值"a"与分区上的元素的value进行相加
* 相加的规则就是遇到相同的key累加这个key之前加过后得到的值,如果遇到不同的key就再次从初始值开始相加,
* 我们把所以元素都放在一个分区中就可以看到zeroValue与value相加的规则。
*
* @param value1 : "a"
* @param value2 : 分区上元素key对应的value
* @return
*/
def seqOp(value1: String, value2: String): String = {
value1 + value2
}
/**
* 这个函数时把各个分区处理后的结果再次进行处理,处理方式就是将相同key的value加在一起
*
* @param value1 : 各个分区处理过后的结果,结果中相同key对应的value
* @param value2 : 各个分区处理过后的结果,结果中相同key对应的value
* @return
*/
def comOp(value1: String, value2: String): String = {
value1 + value2
}
join(otherDataset,[numPartitions])
:将两个RDD
利用cogroup
算子处理,将相同key
的元素放入一个分区,然后返回一个新的RDD
。再将新的RDD
里元素的value
进行笛卡尔积生成多个元组,每一个key
都对应多个元组。然后返回由(k,(value1,valu2))
结构的组成的RDD
。需要注意的是,join算子的处理规则就是只会处理两个RDD中都具有相同key的数据,如果一个RDD的key在另个RDD中没有。join算子是不会处理这种数据的。
def joinTest(sc:SparkContext): Unit ={
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"),(1, "e")))
val rdd_2: RDD[(Int, String)] = sc.makeRDD(Array((1, "d"), (2, "f")))
val joinRDD: RDD[(Int, (String, String))] = rdd_1.join(rdd_2)
println(joinRDD.collect().toList)
/**
* 打印结果为:
* (1,(a,f)), (1,(e,f)), (2,(b,d))
*/
}
cogroup(otherDataSet,[numPartitions]):
def cogroupTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "e")))
val rdd_2: RDD[(Int, String)] = sc.makeRDD(Array((1, "d"), (2, "f")))
val cogroupRDD: RDD[(Int, (Iterable[String], Iterable[String]))] = rdd_1.cogroup(rdd_2)
cogroupRDD.foreach(println(_))
/**
* 打印结果为
* (1,(CompactBuffer(a, e),CompactBuffer(d)))
* (2,(CompactBuffer(b),CompactBuffer(f)))
*/
}
cartesian(otherDataSet)
:对两个RDD
里的元素进行笛卡尔积操作
def cartesianTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "e")))
val rdd_2: RDD[(Int, String)] = sc.makeRDD(Array((1, "d"), (2, "f")))
rdd_1.cartesian(rdd_2).foreach(println(_))
/**
* 打印结果为
* ((1,a),(1,d))
* ((1,a),(2,f))
* ((2,b),(1,d))
* ((2,b),(2,f))
* ((1,e),(1,d))
* ((1,e),(2,f))
*/
}
coalesce(numPartitions,shuffle)
:对RDD
进行重新分区。可以不触发shuffle
操作
numPartitions
:重新分区的个数。shuffle
:分区的时候是否shuffle
,默认值为false
,不能增加分区数,可以减少分区数。如果为true
,可以增加分区数或者减少分区数。def coalesceTest(sc:SparkContext): Unit ={
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "e")),4)
//打印结果为 2,说明重新分区成功了
println(rdd_1.coalesce(2).partitions.size)
}
repartition(numPartitions)
:重新分区,而且必须shuffle
操作
def repartitionTest(sc:SparkContext): Unit ={
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "e")),4)
//打印结果为5,,说明你成功分区了
println(rdd_1.repartition(5).partitions.size)
}
repartitionAndSortWithinPartitions(partitioner:Partitioner)
:对RDD
从新分区,并在分区内按照Key
进行排序,这个在分区的算子中性能是比较高的。官方推荐使用。
partitioner
:它是一个分区器,决定了按照什么分区策略进行分区。这个分区器也可以按照业务规则自己定义分区策略。后续会讲到def repartitionAndSortWithinPartitionsTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (1, "e")), 4)
rdd_1.repartitionAndSortWithinPartitions(new HashPartitioner(2)).foreach(println(_))
/**
* 打印结果为
* (2,b)
* (1,a)
* (1,e)
*
*/
}
Action(算子)
Action
是一种操作算子,它的操作结果是得到一个值或者是一个结果,当程序遇到操作算子的API
的时候,就会真正的触发该操作算子之前的所有的Transformation
和Action
。方法:
方法名 | 方法解释 |
---|---|
reduce(func) | 通过func 函数先对每个分区上的数据集进行聚合,然后再聚合分区之间的数据集,接受两个参数,第一个参数是上一次计算得出的结果,直到计算到最后一个元素,然后将结果返回 |
collect() | 以数据的方式返回数据集中每个元素给Driver 程序,为防止Driver 程序内存溢出,要控制这个数据的大小 |
count() | 返回数据集中元素的个数 |
first() | 返回数据集中的第一个元素,类似于take(1) |
take(n) | 以数组的形式返回数据集前n个元素 |
takeSample(withReplacement, num, [seed]) | 从RDD 中随机抽取元素以数组的方式返回,num 为返回的元素个数,withReplacement 为Boolean 类型,代表采用哪种算法,seed 为可选参数,代表生成器种子,在算法中充当随机参数 |
takeOrdered(n, [ordering]) | 返回数据集的前n 个元素,返回的形式为自然顺序,或者自己定义排序规则 |
saveAsTextFile(path) | 将数据集保存到本地文件系统、hdfs 、或者任何Hadoop 支持的文件系统中,spark 将在每个元素上调用toString 方法,将其转换为一行文本文件进行保存 |
saveAsSequenceFile(path) | 将数据集的每个元素写到给定的目录的hadoop 本地文件系统,hdfs 或者hadoop 支持的各种文件系统。这中K,V 形式的RDD 需要实现Hadoop 的Writable 接口,在scala 中也可以利用隐式转换来与正常数据类型进行转换 |
saveAsObjectFile(path) | 将RDD 中元素序列成对象后存储到文件系统中,对于HDFS,默认采用SequenceFile保存。 |
foreach(func) | 在数据集的每个元素上运行函数func |
Action算子例子
reduce(func)
:它是一个高阶函数,可以传入一个函数作为参数。该方法的是对RDD
里的数据记性业务逻辑的计算。func
针对RDD元素进行逻辑计算的函数
def reduceTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
/**
* reduce是一个高阶函数,需要传入一个函数作为参数,下边的书写方式就是传入了一个匿名函数
* 高阶函数可以理解它比普通方法更加强大,普通方法不不能接受一个函数作为参数的,但是高阶函数是可以的
*/
val result1: (Int, String) = rdd_1.reduce((x, y) => (x._1 + y._1, x._2 + y._2))
/**
* reduce是一个高阶函数,需要传入一个函数作为参数,下边的书写方式为传入一个普通的scala函数,
* 其实与上边是等价的
* 注意:如果这么写的话,该高阶函数传入函数一定要在算子的代码之前定义完成,否则会报
* forward reference extends over definition of value tuple异常!!!!
*/
def reduceFunc(x: (Int, String), y: (Int, String)): (Int, String) = {
(x._1 + y._1, x._2 + y._2)
}
val result2: (Int, String) = rdd_1.reduce(reduceFunc)
println(result1)
println(result2)
/**
* 打印结果都为:(10,abcde)
*/
}
collect()
:返回数据集的所有元素给Driver
,防止Driver
内存溢出,在数据量大的时候要控制返回数据量的大小
//以数据格式返回数据给Driver,如果数据比较大的话尽量不要使用,反之Driver内存不够
def collectTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
println(rdd_1.collect().toList)
}
count()
:返回数据集中的元素的个数。代码就不在阐述了
def countTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
println("rdd_1中的元素有"+rdd_1.count()+"个") //打印结果为 rdd_1中的元素又4个
}
first()
:返回数据集中的第一个元素
def firstTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
println("rdd_1中的第一个元素为"+rdd_1.first()) //打印结果为 rdd_1中的第一个元素为(1, "a")
}
take(n:Int):获取数据集的前n个元素
//获取数据集里的前n个元素
def takeTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
//打印结果为:rdd_1中前2个元素为(1, "a"), (2, "b")
println("rdd_1中前2个元素为" + rdd_1.take(2))
}
takeSample(withReplaceMent:boolean,num:Int,[seed])
:从RDD中随机抽取元素以数组的方式返回。
num
为返回的元素个数。withReplacement
为Boolean
类型,代表采用哪种算法。seed
为可选参数,代表生成器种子,在算法中充当随机参数def takeSampleTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
val takeSampleArray: Array[(Int, String)] = rdd_1.takeSample(true, 2, 10)
}
takeOrdered(n:Int)
:抽取数据集中的n
个元素,并按照自然顺序进行排序。
//从数据集中抽取n个数据,并按照自然顺序排序
def takeOrderedTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
val tuples: Array[(Int, String)] = rdd_1.takeOrdered(2)
}
saveAsTextFile(path:String)
:存储RDD
数据到本地文件系统,hdfs
文件系统,以及其他支持hdfs
文件系统
//保存数据集到本地文件系统,hdfs文件系统,以及支持hadoop文件系统,
// spark会调用toString方法将数据一行一行的写入文件中
def saveAsTestFileTest(sc: SparkContext): Unit = {
val rdd_1: RDD[(Int, String)] = sc.makeRDD(Array((1, "a"), (2, "b"), (3, "d"), (4, "e")))
rdd_1.saveAsTextFile("D://spark.txt")
}
RDD的持久化
spark
的RDD
持久化是非常重要的,当我们将RDD
持久化以后,那么各个节点就会将它自己操作的RDD
的partition
数据缓存到自己的内存中,当对RDD
进行下一次操作的时候,该节点会就使用它自己节点上缓存的Partition
数据,这样大大的提高了计算效率。persist()
和cache()
方法。这两种方法的区别就是cache()
方法是persist()
方法的简化方式,cache()
方法底层就是调用了persist()
的空参方法,也就是persist(MEMORY_ONLY)
方法。清除缓存:调用unpersist()
方法
持久化级别
持久化级别 | 解释 |
---|---|
MEMORY_ONLY | 将没有序列化的java 对象持久化到内存中,spark 的默认持久化级别,如果有的分区内存不够就不会在该分区上持久化 |
MEMORY_AND_DISK | 将没有序列化的java 对象持久化到内存中,当内存中不够用的时候,将一部分数据持久化到磁盘中 |
MEMORY_ONLY_SER | 将RDD存储 为序列化的Java对象(每个分区一个字节数组)。与反序列化对象相比,这通常更节省空间,特别是在使用快速序列化器时,但是读起来更需要cpu。 |
MEMORY_AND_DISK_SER | 与MEMORY_ONLY_SER 类似,但超出内存的数据溢出到磁盘中 |
DISK_ONLY | 只将RDD 分区存储在磁盘上。 |
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. | 以副本的方式持久化数据 |
cache
和checkpoint
的区别:
cache
是将RDD
缓存在节点的内存中,RDD
之间的依赖关系依然存在。checkpoint
是以多副本的方式存储在节点的磁盘上,RDD
之间的依赖关系就不存在了。cache
缓存不用指定节点地址,但是checkpoint
需要指定节点的地址。RDD分区
HashPartition
:根据Key
的哈希值来分去,可能造成数据倾斜RangePartition
:水塘抽样算法实现数据的分区,数据能够均匀的分区,但是不能保证数据的顺序。Partitioner
类,然后s实现里边的方法,方法内定义分区策略