Spark2.1.1<通俗易懂理解combineByKey-combineByKeyWithClassTag>

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Gpwner/article/details/73349589

combineByKey是Spark中一个比较核心的高级函数, groupByKey,reduceByKey的底层都是使用combineByKey实现的,这是我要弄清楚它的原因

1.6.0版的函数名更新为combineByKeyWithClassTag

combineByKey会调用combineByKeyWithClassTag,源码

  def combineByKey[C](
      createCombiner: V => C,
      mergeValue: (C, V) => C,
      mergeCombiners: (C, C) => C): RDD[(K, C)] = self.withScope {
    combineByKeyWithClassTag(createCombiner, mergeValue, mergeCombiners)(null)
  }

所以弄清楚combineByKey就能弄懂后者了

2.首先我们来看一例子

我有三类书

数据结构 操作系统 算法
大话数据结构 深入理解操作系统 算法导论
数据结构C++版本 Linux操作系统 算法基础
数据结构Java版本 Windows操作系统 算法心得

,每一本数都有一个价格,我们现在的任务是要把所有的书籍都卖出去,假设我们要为每一类书籍都买一个钱包来装这些书籍卖出去的价格

思路就是,当我们卖掉每一类书籍的第一本书的时候,将得到的钱去买一个钱包,然后其他同类书籍卖掉的钱直接装进钱包就好了,下面是代码实现


case class Book(val name: String, val price: Int) {
  //卖书然后买一个十块钱的钱包来装钱
  def sellBook_buyWallet: Wallet = Wallet(price - 10)
}

case class Wallet(var money: Int) {
  //将不同的钱包的钱累加起来
  def addMoney(that: Book): Wallet = Wallet(money + that.price)
}


object BookCombineByKey {
  def main(args: Array[String]): Unit = {
    val sparkconf = new SparkConf().setAppName("Book_Sell").setMaster("local")
    val sc = new SparkContext(sparkconf)
    val bookRDD = sc.makeRDD(Array(
      ("数据结构", Book("大话数据结构", 50)),
      ("数据结构", Book("数据结构C++版本", 80)),
      ("数据结构", Book("数据结构Java版本", 50)),
      ("算法", Book("算法导论", 50)),
      ("算法", Book("算法基础", 20)),
      ("算法", Book("算法心得", 50)),
      ("操作系统", Book("深入理解操作系统", 30)),
      ("操作系统", Book("Linux操作系统", 70)),
      ("操作系统", Book("Windows操作系统", 50))
    ))
    println(bookRDD.partitions.size+"===================")
    val aggresult = bookRDD.combineByKeyWithClassTag(
      (book: Book) => book.sellBook_buyWallet,
      (w: Wallet, b: Book) => w.addMoney(b),
      (w1: Wallet, w2: Wallet) => {
        println("================ call ===============")
        Wallet(w1.money + w2.money)
      }
    ).collectAsMap().foreach(println)
  }
}

其余的代码相当简单我就不讲解了,主要讲解combineByKey的参数,

  • 第一个参数是说(book: Book) => book.sellBook_buyWallet
    满足了我们的需求,同类第一本书籍卖掉的时候,将得到的钱去买一个钱包,然后将卖书得到的钱减掉买钱包的钱,然后装入钱包。
  • 第二个参数(w: Wallet, b: Book) => w.addMoney(b)满足我们的第二个需求,同类其他书籍卖掉得到的钱直接装入本类对应的钱包

第三个参数我们姑且不看,先将程序跑起来

看看执行结果:

这里写图片描述

算法类对应的钱包的钱数怎么来的?:

第一本书50元,卖掉得到50块钱,然后再买一个钱包,花了十块钱,现在还有50-10=40元,装进钱包,算法类第二本书卖掉得到20块钱,装进钱包,第三本买到得到50装进钱包,现在这个钱包有多少钱?50-10+20+50

以此类推,操作系统的钱怎么来的?
30-10+70+50=140
数据结构的钱?
50-10+80+50=170

这是不是很像,reduceBykey?

3.接下来我们来讲解第三个参数

 (w1: Wallet, w2: Wallet)

你可以看到我在这个函数,里打印了一句话,但是控制台并没有输出这句话?

 println("================ call ===============")

日志过长,我就不粘贴进来了

这是为什么呢?原来只有当同类的事物在不同的分区的时候,这个函数才会被触发。这在我们这个例子上对应的是什么呢?

这个就是,不同的地区的都同时在卖这些书籍,每一个地区都有三个不同的钱包,当所有的书籍都卖完了之后,我们再将这些钱包依次合并为三个大的钱包。并入地区一的数据结构的钱包转了1000块,地区二的数据结构的钱包装了500块,最终数据结构这个大钱包装了1500块。

那我们怎么样才能在上面的例子中触发这个函数呢?

在上面的代码运行时候的控制台中,我打印出了这个RDD的分区数目,可以看到只有一个分区
这里写图片描述

现在对代码做一点点修改,我创建RDD的时候,指定分区的数量是2,这以为这这些Book对象会分布到不同的分区上

这里写图片描述
看一下控制台:
这里写图片描述

可以看到现在的第三个函数的结果也被调用了

对比一下结果,我们第一次得到的结果是:

(算法,Wallet(110))
(操作系统,Wallet(140))
(数据结构,Wallet(170))

修改代码之后得到的结果是:

(算法,Wallet(100))
(操作系统,Wallet(140))
(数据结构,Wallet(170))

这是为什么?因为分区将算法类的书籍都分到了两个分区,操作系统和数据结构的所有书籍都在同一个分区

这里写图片描述

类似这样,算法类在两个分区分别都买了一个钱包,所以最终算法类有
50-10+20-10+50=100

其余两类没有变化。

猜你喜欢

转载自blog.csdn.net/Gpwner/article/details/73349589
今日推荐