一、二次排序
(1)输入与输出
(2)需求:数据如file1.txt,要求根据第一列降序,如果第一列相等,则根据第二列升序
分析:平时所使用的键值对是不具有比较意义的,也就说他们没法拿来直接比较,可以通过sortByKey,sortBy(pair._2)来进行单列的排序,但是没法进行两列的同时排序。可以自定义一个键值对的比较类来实现比较,类似于JAVA中自定义类实现可比较性实现comparable接口。我们需要继承Ordered和Serializable特质来实现自定义的比较类。
(3)解答:先根据key进行排序,相同再根据value进行排序。可以把键值对当成一个数据有两个数字,先通过第一个数字比大小,再通过第二个数字比大小。
1)我们定义两个Int参数的比较类
2)继承Ordered 和 Serializable 接口 实现 compare 方法实现可以比较
(4)代码如下:
1 package SparkDemo
2 import org.apache.spark.{SparkConf, SparkContext}
3 class UDFSort (val first:Int,val second:Int) extends Ordered[UDFSort] with Serializable {//自定义比较类
4 override def compare(that: UDFSort): Int = {
5 if(this.first - that.first != 0){//第一个值不相等的时候,直接返回大小
6 this.first - that.first //返回值
7 }
8 else {//第一个值相等的时候,比较第二个值
9 this.second - that.second
10 }
11 }
12 }
13 object Sort{
14 def main(args:Array[String]): Unit ={
15 //初始化配置:设置主机名和程序主类的名字
16 val conf = new SparkConf().setAppName("UdfSort");
17 //通过conf来创建sparkcontext
18 val sc = new SparkContext(conf);
19 val lines = sc.textFile("file:///...")
20 //转换为( udfsort( line(0),line(1) ),line ) 的形式
21 val pair = lines.map(line => (new UDFSort(line.split(" ")(0).toInt,line.split(" ")(1).toInt),line))
22 //对key进行排序,然后取value
23 val result = pair.sortByKey().map( x => x._2)
24 }
25 }
二、combinerByKey函数
(1)主要接受5个参数,一般来说我们主要使用前2个参数
1)createCombiner:V=>C 分组内的创建组合的函数。通俗点将就是对读进来的数据进行初始化,其把当前的值作为参数,可以对该值做一些转换操作,转换为我们想要的数据格式
2)mergeValue:(C,V)=>C 该函数主要是分区内的合并函数,作用在每一个分区内部。其功能主要是将V合并到之前(createCombiner)的元素C上,注意,这里的C指的是上一函数转换之后的数据格式,而这里的V指的是原始数据格式(上一函数为转换之前的)
3)mergeCombiners:(C,C)=>R 该函数主要是进行多分取合并,此时是将两个C合并为一个C.
4)partitioner:自定义分区数,默认是hashPartitioner
5)mapSideCombine:Boolean=true 该参数是设置是否在map端进行combine操作
(2)函数工作流程
1)combinByKey会遍历rdd中每一个(k,v)数据对,对该数据对中的k进行判断,判断该(k,v)对中的k是否在之前出现过,如果是第一次出现,则调用createCombiner函数,对该k对应的v进行初始化操作(可以做一些转换操作),也就是创建该k对应的累加其的初始值
2)如果这是一个在处理当前分区之前遇到的k,会调用mergeCombiners函数,把该k对应的累加器的value与这个新的value进行合并操作
(3)示例代码
import org.apache.spark.SparkConf
import org.apache.spark.sql.SparkSession
import scala.collection.mutable
object test {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("testCombineByKey").setMaster("local[2]")
val ssc = new SparkSession
.Builder()
.appName("test")
.master("local[2]")
.config(conf)
.getOrCreate()
val sc = ssc.sparkContext
sc.setLogLevel("error")
val initialScores = Array((("1", "011"), 1), (("1", "012"), 1), (("2", "011"), 1), (("2", "013"), 1), (("2", "014"), 1))
val d1 = sc.parallelize(initialScores)
d1.map(x => (x._1._1, (x._1._2, 1)))
.combineByKey(
(v: (String, Int)) => (v: (String, Int)),
(acc: (String, Int), v: (String, Int)) => (v._1+":"+acc._1,acc._2+v._2),
(p1:(String,Int),p2:(String,Int)) => (p1._1 + ":" + p2._1,p1._2 + p2._2)
).collect().foreach(println)
}
}
1、map端将数据格式化为:(,(String,Int))->("1",("011",1))
2、接着combineByKye函数会逐个的读取map之后的每一个k,v数据对,当读取到第一个("1",("011",1)),此时回判断,“1”这个是否在之前的出现过,如果该k是第一次出现,则会调用createCombiner函数,经过转换,该实例中是对该value值么有做任何的改变原样返回,此时这个该value对应的key回被comgbineByKey函数创建一个累加其记录
3、当读取到第二个数据("1",("012",,1))的时候,回对“1”这个key进行一个判断,发现其在之前出现过,此时怎直接调用第二个函数,mergeValues函数,对应到该实例中,acc即为上一函数产生的结果,即("1",("011",1)),v即是新读进来的数据("1",("012",1))
4、此时执行该函数:(acc: (String, Int), v: (String, Int)) => (v._1+":"+acc._1,acc._2+v._2)将新v中的第一个字符串与acc中的第一个字符串进行连接,v中的第二个值,与acc中的第二个值进行相加操作
5、当所有的分区内的数据计算完成之后,开始调用mergeCombiners函数,对每个分区的数据进行合并,该实例中p1和p2分别对应的是不同分区的计算结果,所以二者的数据格式是完全相同的,此时将第一个分区中的字符串相连接,第二个字符相加得到最终结果
(2,(014:013:011,3))
(1,(012:011,2))
三、aggregate算子
(1)定义:aggregate接收两个函数,和一个初始化值。seqOp函数用于聚集每一个分区,combOp用于聚集所有分区聚集后的结果。每一个分区的聚集,和最后所有分区的聚集都需要初始化值的参与。
(2)代码如下:
1 def seqOp(p1: Int, p2: Int): Int = {
2 p1 * p2
3 }
4
5 def combOp(p3: Int, p4: Int): Int = {
6 p3 + p4
7 }
8
9 //调用makeRDD()方法的时候,会为每个集合对象创建最佳分区,而这对后续的调用优化很有帮助。
10 //当调用parallelize()方法的时候,不指定分区数的时候,使用系统给出的分区数rdd=sc.parallelize(List(1,2,3,4,5,6,7,8,9,10),3)
11
12 rdd=sc.makeRDD(List(1,2,3,4,5,6,7,8,9,10))
13 //分区为:(1 , 2 , 3 ) ,(4 , 5 , 6 ),(7 , 8 , 9 , 10 )
14 rdd2.aggregate(2)(pfun1, pfun2) //结果为10334
15
16 解析:
17 seqOp 函数会分别在 RDD 的每个分区中应用一次,其中标红的为初始值
18 2 * 1 * 2 * 3 = 12
19 2 * 4 * 5 * 6 = 240
20 2 * 7 * 8 * 9 * 10 = 10080
21
22 seqOp输出有3个值即12,240,10080,这三个值+初始值作为combOp的输入
23 2 + 12 + 240 + 10080 = 10334