3.持久化
我们知道RDD采用惰性机制,这对于迭代计算而言,代价是很大的,因为迭代计算经常需要多次重复使用同一组数据。
为了解决这一问题,我们可以通过持久化(缓存)机制来避免这种重复计算的开销。具体方法是使用persist()方法将一个RDD标记为持久化。
持久化RDD会占用内存空间,当不再需要一个RDD时,就可以使用unpersist()方法手动地把持久化的RDD从缓存中移除,释放内存空间。
4.分区
4.1分区的作用
RDD是弹性分布式数据集,通常RDD很大,会被分成很多个分区,分别保存在不同的节点上。
对RDD进行分区,第一个作用是增加并行度,第二个作用是减少通信开销。
4.2分区的原则
RDD分区的一个原则是时得分区的个数尽量等于集群中CPU核心(Core)数目。对于不同的Spark部署模式,都可以通过设置spark.default.parallelism这个参数的值,来配置默认的分区数目。
设置分区的个数
4.2.1创建RDD时手动设置分区个数
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName(this.getClass.getName)
val sc = new SparkContext(conf)
sc.textFile(path,minPartitions)
}
//path指定加载文件的路径,minPartitions用于指定分区数
4.2.2使用repartition()方法重新设置分区个数
>>> data = sc.parallelize([1,2,3,4,5],2)
>>> len(data.glom().collect()) #显示data这个RDD的分区数量
2
>>> rdd = data.repartition(1) #对data这个RDD进行重新分区
>>> len(rdd.glom().collect()) #显示rdd这个RDD的分区数量
1
4.2.3自定义分区方法
Spark提供了自带的HashPartitioner(哈希分区)与RangePartitioner(区域 分区),能够满足大多数应用场景的需求。与此同时,Spark也支持自定义分 区方式,即通过提供一个自定义的分区函数来控制RDD的分区方式,从而利 用领域知识进一步减少通信开销
5 键值对RDD
键值对RDD是指每个RDD元素都是(Key,Values)键值对类型,是一种常见的RDD类型,可以应用于很多场景。
5.1 键值对RDD的创建
键值对RDD的创建主要有两种方式:从文件中加载生成RDD;通过并行集合(列表)创建RDD。
这和上面RDD创建类似,就是我们要对每个key值赋一个values。
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName(this.getClass.getName)
val sc = new SparkContext(conf)
val rdd: RDD[Int] = sc.makeRDD(1 to (10),1)
val rdd2: RDD[(Int, Int)] = rdd.map((_, 1))
}
5.2 常用的键值对转换操作
reduceByKey()和groupByKey()上面已经介绍过,不在重复介绍
5.2.1 keys()
键值对RDD每个元素都是(Key,Values)的形式,keys方法啊只会把键值对RDD中的key返回,形成一个新的RDD。
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName(this.getClass.getName)
val sc = new SparkContext(conf)
val rdd: RDD[(String, Int)] = sc.makeRDD(Array(("jack", 18), ("tom", 16), ("luck", 20)))
val rddKey: RDD[String] = rdd.keys
println(rddKey.collect().toBuffer) //结果ArrayBuffer(jack, tom, luck)
}
5.2.2 values()
values操作只会把键值对RDD中的values返回,形成一个新的RDD。
val rddValues: RDD[Int] = rdd.values
println(rddValues.collect().toBuffer) //结果ArrayBuffer(18, 16, 20)
5.2.3sortByKey()
sortByKey()的功能是返回一个根据key排序的RDD。
val rdd: RDD[(String, Int)] = sc.makeRDD(Array(("jack", 18), ("tom", 16), ("luck", 20),("david",12)))
val rddSort: RDD[(String, Int)] = rdd.sortByKey() //括号内填false即倒序
println(rddSort.collect().toBuffer)
}
//结果ArrayBuffer((david,12), (jack,18), (luck,20), (tom,16))
//字符串根据字典顺序排序
5.2.4 sortBy()
可以根据其他字段进行排序。
val rdd: RDD[(String, Int)] = sc.makeRDD(Array(("jack", 18), ("tom", 16), ("luck", 20),("david",12)))
val rddSort: RDD[(String, Int)] = rdd.sortBy(_._2) //根据数字排序
println(rddSort.collect().toBuffer) //结果 ArrayBuffer((david,12), (tom,16), (jack,18), (luck,20))
5.2.5 mapValues(func)
mapValues(func)对键值对RDD中每个value都应用一个函数,但是,key不会发生变化。
val rdd: RDD[(String, Int)] = sc.makeRDD(Array(("jack", 18), ("tom", 16), ("luck", 20), ("david",12)))
val rddSort: RDD[(String, Int)] = rdd.mapValues((_*10))
println(rddSort.collect().toBuffer)
//结果 ArrayBuffer((jack,180), (tom,160), (luck,200), (david,120))
5.2.6 join()
join表示内连接,对于给定的两个输入数据集(K,V1),(K,V2),只有在两个数据集中都存在的key才会被输出,最终得到一个(K,(V1,V2))类型的数据集。
val rdd1: RDD[(String, Int)] = sc.makeRDD(Array(("jack", 80), ("tom", 68), ("luck", 78),("david",88)))
val rdd2: RDD[(String, Int)] = sc.makeRDD(Array(("jack", 81), ("tom", 87), ("luck", 58),("david",87)))
val res: RDD[(String, (Int, Int))] = rdd1.join(rdd2)
println(res.collect().toBuffer)
//结果 ArrayBuffer((luck,(78,58)), (tom,(68,87)), (david,(88,87)), (jack,(80,81)))