大家好,欢迎来到IT知识分享网。
PageRank是执行多次连接的一个迭代算法,因此它是RDD分区操作的很好demo,算法维护两个数据集
- (pageID,listList) 包含每个页面的相邻页面列表。
- (pageID,rank) 包含每个页面的当前排序值, pageRank计算过程大致如下:
- 将每个页面的排序值初始化为1.0
- 在每次迭代中,对页面p,向其每个相邻页面(有直接连接的页面)发松一个值为 rank(p)/numNeighbors(p)的贡献值。
- 将每个页面的排序值设定为 0.15 + 0.85 *contributionsReceived 其中2 跟3 会重复循环几次,在此过程中算法会逐渐收敛于每个页面的实际PageRank值,实际操作中一般迭代10次。
package com.sowhat.spark import org.apache.spark.rdd.RDD import org.apache.spark.{HashPartitioner, SparkConf, SparkContext} /** * links = (pageID,LinkList) * ranks = (pageID,rank) **/ object MyPageRank { def main(args: Array[String]): Unit = { val conf: SparkConf = new SparkConf().setMaster("local[*]").setAppName("pagerank") //创建SparkContext,该对象是提交spark App的入口 val sc = new SparkContext(conf) val links: RDD[(String, Seq[String])] = sc.objectFile[(String, Seq[String])]("filepwd").partitionBy(new HashPartitioner(100)).persist() var ranks: RDD[(String, Double)] = links.mapValues(x => 1.0) for (i <- 0 until 10) { val totalRDD: RDD[(String, (Seq[String], Double))] = links.join(ranks) val contributions: RDD[(String, Double)] = totalRDD.flatMap( { case (pageID, (links, rank)) => links.map(dest => (dest, rank / links.size)) } ) ranks = contributions.reduceByKey(_ + _).mapValues(v => 0.15 + 0.85 * v) } ranks.saveAsTextFile("ranks") } }
算法从ranksRDD的每个元素的值初始化为1.0开始,然后每次迭代都都不断的更新ranks值,其中主要优化部分如下。
- linksRDD每次迭代都会跟ranks发生连接操作,因此将大数据集links进行partitionBy 会节约相当多的网络通信优化开销。
- 跟上面的原因一样,用persist 可以将数据保存早内存中,以供每次迭代使用。
- 我们在第一次创建ranks时, 我们用mapValues而不是map() 来保留父RDD links的分区方式,这样对第一次连接操作开销减少很多。
- 循环体中 reduceByKey后使用mapValues 因为reduceByKey已经是哈希分区了,下一次迭代时候效率更快。
建议:为最大化分区相关优化潜在作用,在无需更改元素键的时候尽量使用 mapValues 或 flatMapValues。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/88010.html