1. 程式人生 > 其它 >Spark GraphX入門

Spark GraphX入門

一、概述

  GraphX 是 Spark 四大核心元件之一,它也是使用 Spark 作為計算引擎的,GraphX 是用於圖形和圖形平行計算的元件,實現了大規模圖計算的功能。GraphX 的出現使 Spark 生態系統變得更加完善和豐富,同時它能夠與 Spark 生態系統的其它元件天然融合,再加上它強大的圖資料處理能力,在業屆得到了廣泛的運用。

  在高層次上,GraphX 通過引入一個新的圖形抽象來擴充套件 Spark RDD :即頂點帶有特性的有向多重圖。GraphX 提供了一些基本運算子以及優化了的 谷歌的Pregel API。 此外,GraphX 提供了大量的圖演算法和構建器,以簡化圖形分析任務。

1.1 圖

  圖是由很多個節點(vertex)構成的,節點之間通過邊(edge)進行連線。圖在網路科學中被稱為網路。實際應用中,有很多圖結構資料:

  • 計算機網路:由許多節點(計算機或者路由器)以及節點之間的邊(網線)構成的網路;
  • 城市交通系統:由節點(路口)和邊(道路)構成的圖;
  • 微信社交網路:由節點(個人或公眾號)和邊(關注或點贊)構成的圖;
  • 淘寶的交易網路:由節點(個人或商品)和邊(購買或收藏)構成的圖;
  • 網頁的連結網路:由節點(網頁)和邊(連結)構成的圖。

  在GraphX中,有兩個RDD, 一個是點RDD,一個是邊RDD。一個是節點(vertex),另一個是邊(edge)。

  在GraphX的實際應用場景中,比如:Twitter、Facebook、微博、微信等,人與人之間存在著很多的關係鏈。這些地方產生的資料更加適合使用圖處理來進行計算。圖的分散式或者並行處理其實是把圖拆分成很多個子圖,然後分別對這些子圖進行計算,計算的時候可以分別迭代進行分階段的計算,即對圖進行平行計算。

1.2 圖的處理技術

圖處理技術包括圖資料庫圖資料查詢圖資料分析圖資料視覺化

  • 圖資料庫:Neo4j、Titan、OrientDB、DEX和InfiniteGraph等基於遍歷演算法的、實時的圖資料庫;
  • 圖資料查詢:對圖資料庫中的內容進行查詢;
  • 圖資料分析:Google Pregel、Spark GraphX、GraphLab等圖計算軟體。傳統的資料分析方法側重於事物本身,即實體,例如銀行交易、資產註冊等等。而圖資料不僅關注事物,還關注事物之間的聯絡。例如,如果在通話記錄中發現張三曾打電話給李四,就可以將張三和李四關聯起來,這種關聯關係提供了與兩者相關的有價值的資訊,這樣的資訊是不可能僅從兩者單純的個體資料中獲取的。
  • 圖資料視覺化:OLTP風格的圖資料庫或者OLAP風格的圖資料分析系統(或稱為圖計算軟體),都可以應用圖資料庫視覺化技術。需要注意的是,圖視覺化與關係資料視覺化之間有很大的差異,關係資料視覺化的目標是對資料取得直觀的瞭解,而圖資料視覺化的目標在於對資料或演算法進行除錯。

  通常,在進行大規模圖資料分析時,都會組合使用圖資料庫(比如Neo4j)和圖計算軟體(比如Spark Gtaph X)。如果只是簡單地儲存實體之間的關係,那麼,只要使用圖資料庫即可,不需要使用圖計算軟體。業界的一個發展趨勢是,會出現可以同時處理OLAP和OLTP型別應用的圖系統,也就是說,在圖資料庫與圖計算框架之間的緊密整合,或者通過Neo4j這樣的圖資料庫與Spark GraphX這樣的圖處理系統間的無縫互操作來實現,或者在將來某一天這些功能會出現在同一產品中。

二、GraphX的核心概念及實操

官方文件地址:https://spark.apache.org/docs/latest/graphx-programming-guide.html

首先需要將 Spark 和 GraphX 匯入到專案中,如下所示:

import org.apache.spark._
import org.apache.spark.graphx._
import org.apache.spark.rdd.RDD

2.1 屬性圖

  屬性圖是一個有向多重圖,它的每個頂點和每條都附有使用者定義的物件。作為有向圖,有向多重圖可能有多個平行的邊來共享相同的源頂點和目標頂點。作為多重圖,它支援並行邊,這個特性簡化了許多涉及多重關係的建模場景。每個頂點的主鍵是一個長度為64 bit的唯一識別符號(VertexID)。GraphX沒有為頂點新增任何順序的約束。類似地,每一條邊有對應的源頂點和目標頂點的識別符號。

  假設我們要構造一個由 GraphX 專案上的各種協作者組成的屬性圖。頂點屬性可能包含使用者名稱和職業。我們可以用一個描述協作者之間關係的字串來註釋邊緣:

  屬性圖的引數是通過頂點(VD)和邊的型別(ED)來決定的。在某些情況下,你可能希望在同一個圖裡面,頂點能夠有不同的屬性型別。這個想法可以通過繼承實現。舉個例子,我們可以對使用者和產品進行建模,將其作為一個二分圖,然後進行如下的定義:

class VertexProperty()
case class UserProperty(val name: String) extends VertexProperty
case class ProductProperty(val name: String, val price: Double) extends VertexProperty
// 圖可能會有這個型別
var graph: Graph[VertexProperty, String] = null

  類似於 RDD,屬性圖是不可變的、分散式的,並且具有容錯性。對於圖而言,它的值或者結構上的改變,是通過產生帶有預期改變的新圖來完成的。需要注意的是,原始圖的大部分(即:不受影響的結構、屬性和索引等)在新圖中被重用,從而降低了這種固有的功能性資料結構的成本。通過使用大量的啟發式頂點分割槽,圖在不同的執行器裡被劃分。就像 RDD 一樣,圖的每個分割槽可以在發生故障時被不同的機器重建。

  屬性圖在邏輯上對應於一對型別化集合(RDD),該集合編碼了每個頂點和每條邊屬性。因而,圖類包含了可以訪問圖的頂點和邊的成員,它的定義如下:

class Graph[VD, ED] {
  val vertices: VertexRDD[VD]
  val edges: EdgeRDD[ED]
}

  VertexRDD[VD]類和EdgeRDD[ED]類分別繼承和優化了RDD[(VertexID, VD)]類和RDD[Edge[ED]]類。兩者都提供基於圖計算和內部優化構建的額外功能。在此你可將其簡單地理解為以RDD[(VertexID, VD)]和RDD[Edge[ED]]形式定義的 RDD。

  有許多方法可以構建屬性圖,例如從原始檔案、RDD 以及合成生成器(graph builders),最通用的方法是使用 Graph 物件。首先介紹從RDD的集合中構造一個圖,例如:

// Assume the SparkContext has already been constructed
val sc: SparkContext
// Create an RDD for the vertices
val users: RDD[(VertexId, (String, String))] =
  sc.parallelize(Seq((3L, ("rxin", "student")), (7L, ("jgonzal", "postdoc")),
                       (5L, ("franklin", "prof")), (2L, ("istoica", "prof"))))
// Create an RDD for edges
val relationships: RDD[Edge[String]] =
  sc.parallelize(Seq(Edge(3L, 7L, "collab"),    Edge(5L, 3L, "advisor"),
                       Edge(2L, 5L, "colleague"), Edge(5L, 7L, "pi")))
// 定義的預設使用者,以防缺少與使用者的關係
val defaultUser = ("John Doe", "Missing")
// Build the initial Graph
val graph = Graph(users, relationships, defaultUser)

  在上面的例子中,我們使用了 Edge 樣例類邊有一個 srcId 和一個 dstId對應於源和目標頂點識別符號。 此外,Edge 類還有一個儲存邊緣屬性的 attr 成員。

  我們還可以分別使用 graph.vertices 和 graph.edges 方法將圖解構為各自的頂點和邊檢視,如下所示:

//接上面的程式碼
val graph: Graph[(String, String), String] 
// 計算所有博士後使用者
graph.vertices.filter { case (id, (name, pos)) => pos == "postdoc" }.count
// 計算所有 src > dst 的邊
graph.edges.filter(e => e.srcId > e.dstId).count

  請注意,graph.vertices 返回一個 VertexRDD[(String, String)],它擴充套件了 RDD[(VertexId, (String, String))],因此我們使用 scala case 表示式來解構元組。 另一方面,graph.edges 返回一個包含 Edge[String] 物件的 EdgeRDD,同樣可以使用 case 類型別建構函式,如下所示:

graph.edges.filter { case Edge(src, dst, prop) => src > dst }.count

2.2 三元組檢視

  除了屬性圖的頂點和邊檢視之外,GraphX 還公開了一個三元組檢視。 三元組檢視在邏輯上連線了頂點和邊的屬性,產生一個包含 EdgeTriplet 類例項的 RDD[EdgeTriplet[VD, ED]]。 這個連線可以用下面的 SQL 表示式來表示:

SELECT src.id, dst.id, src.attr, e.attr, dst.attr
FROM edges AS e LEFT JOIN vertices AS src, vertices AS dst
ON e.srcId = src.Id AND e.dstId = dst.Id

或圖形化為:

  EdgeTriplet 類(邊三元組表示一條邊及其相鄰頂點的頂點屬性,是Edge的子類)通過新增分別包含源和目標屬性的 srcAttr 和 dstAttr 成員來擴充套件 Edge 類。 我們可以使用圖的三元組檢視來呈現描述使用者之間關係的字串集合。

// 程式碼接上面
val graph: Graph[(String, String), String] 
// Use the triplets view to create an RDD of facts.
val facts: RDD[String] =
  graph.triplets.map(triplet =>
    triplet.srcAttr._1 + " is the " + triplet.attr + " of " + triplet.dstAttr._1)
facts.collect.foreach(println(_))