spark graphx 图计算浅析

spark graphx 图计算浅析

 总认为版本低点,分析难度小些,主要为了解思想,以spark-0.9.0-incubating版本为分析对象,以下为其例子程序PregelSuite。
  test("chain propagation") {
    withSpark { sc =>
      val n = 5
      val chain = Graph.fromEdgeTuples(
        sc.parallelize((1 until n).map(x => (x: VertexId, x + 1: VertexId)), 3),
        0).cache()
      assert(chain.vertices.collect.toSet === (1 to n).map(x => (x: VertexId, 0)).toSet)
      val chainWithSeed = chain.mapVertices { (vid, attr) => if (vid == 1) 1 else 0 }.cache()
      assert(chainWithSeed.vertices.collect.toSet ===
        Set((1: VertexId, 1)) ++ (2 to n).map(x => (x: VertexId, 0)).toSet)
      val result = Pregel(chainWithSeed, 0)(
        (vid, attr, msg) => math.max(msg, attr),
        et => if (et.dstAttr != et.srcAttr) Iterator((et.dstId, et.srcAttr)) else Iterator.empty,
        (a: Int, b: Int) => math.max(a, b) )
      assert(result.vertices.collect.toSet ===
        chain.vertices.mapValues { (vid, attr) => attr + 1 }.collect.toSet)
    }
  }

看看图的定义

class GraphImpl[VD: ClassTag, ED: ClassTag] protected (
    @transient val vertices: VertexRDD[VD],
    @transient val edges: EdgeRDD[ED],
    @transient val routingTable: RoutingTable,
    @transient val replicatedVertexView: ReplicatedVertexView[VD])
  extends Graph[VD, ED] with Serializable ```

看到标注transient,意味着顶点、 边等数据集很少多次传输,当这些数据集(RDD算子)传到worker后,数据很少变动。
GraphX 采用顶点切分方式进行分布式图分割,下面是分割示意。
在这里插入图片描述
边切分与顶点切分
GraphX 不是沿着边沿分割图形,而是沿着顶点分割图形,这可以减少通信和存储开销,在逻辑上,这对应于将边缘分配给机器并允许顶点跨越多台机器。
以上面的例子,跟踪可以得到下图(图、顶点表、边表、路由表):
在这里插入图片描述
从上图看,顶点表和边表分区并不一样,在Spark节点初始化后,如下图。
在这里插入图片描述

在上面的分区图中,RoutingTable、ReplicatedVertexView、Edges等RDD算子(数据集)不太可能正好在一个worker中,没什么关系,计算时Spark可以通过RDDid直接将计算过的数据集读过来,也相当于在一个Worker。
程序在Spark中运行时,为了减少数据传输,路由表RoutingTable数据集、顶点表数据集(Vertices)、边表数据集(Edges)cache后,将不发生变化;真正变化计算数据的是updatedVerts,也就是ReplicatedVertexView重复顶点视图中的updatedVerts,为上面红框内的内容,变化发生在g.outerJoinVertices程序中。

class ReplicatedVertexView[VD: ClassTag](
updatedVerts: VertexRDD[VD],
edges: EdgeRDD[_],
routingTable: RoutingTable,
prevViewOpt: Option[ReplicatedVertexView[VD]] = None)

Pregel程序如下。

def apply[VD: ClassTag, ED: ClassTag, A: ClassTag]
     (graph: Graph[VD, ED],
      initialMsg: A,
      maxIterations: Int = Int.MaxValue,
      activeDirection: EdgeDirection = EdgeDirection.Either)
     (vprog: (VertexId, VD, A) => VD,
      sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)],
      mergeMsg: (A, A) => A)
    : Graph[VD, ED] =
  {
    var g = graph.mapVertices((vid, vdata) => vprog(vid, vdata, initialMsg)).cache()
    // compute the messages
    var messages = g.mapReduceTriplets(sendMsg, mergeMsg)
    var activeMessages = messages.count()
    // Loop
    var prevG: Graph[VD, ED] = null
    var i = 0
    while (activeMessages > 0 && i < maxIterations) {
      // Receive the messages. Vertices that didn't get any messages do not appear in newVerts.
      val newVerts = g.vertices.innerJoin(messages)(vprog).cache()

      // Update the graph with the new vertices.
      prevG = g
      g = g.outerJoinVertices(newVerts) { (vid, old, newOpt) => newOpt.getOrElse(old) }
      g.cache()
      
      val oldMessages = messages
      messages = g.mapReduceTriplets(sendMsg, mergeMsg, Some((newVerts, activeDirection))).cache()
      activeMessages = messages.count()

      // Unpersist the RDDs hidden by newly-materialized RDDs
      oldMessages.unpersist(blocking=false)
      newVerts.unpersist(blocking=false)
      prevG.unpersistVertices(blocking=false)
      // count the iteration
      i += 1
    }

    g
  } // end of apply

活动顶点(1,1),查找RoutingTable,与UpadateVerts组合,生成本分区重复顶点视图ReplicatedVertexView,通过边表(Edges)进行MapReduce计算,得到下一个活动顶点(2,1),运行跟踪结果如下。
在这里插入图片描述

活动顶点(2,1),在1分区计算和上面类似,最后得到活动顶点(3,1);但在0分区计算,由于ActiveVerts和UpdatedVerts有重复,该顶点(2,1)在该分区失效。
在这里插入图片描述

关于Spark Graphx网上分析比较多,感觉缺一个调试脉络,就写了这篇博文,希望有所帮助。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值