源码:https://github.com/NickyWooden/graphx-demo.git
给部门内部做的培训
1.图论基础
- 点
- 边
- 有向图、无向图
- 度(入度、出度)
- 环
- DAG
- 联通图
- 子图
2.属性图
- 定义
属性图是一个有向多图,每个顶点和边都有用户定义的对象(属性)
- Vertex
(VertexId,顶点属性)
- Edge
(srcVertexId,desVertexId,边属性)
3. 图存储
- 边分割
沿着边切分图,同一边切成两条边分到不同的2个分区,切分边对应的顶点的副本进入其他分区
- 点分割
沿着顶点切分图,两条边的公共顶点被分成两份到2个不同分区,而边只有一份副本
spark grapx为啥用点分割?
1. 减少分布式计算是的网络IO
边分割需要同步发送点属性数据,现实中总是边要远远大于顶点数,这样按边拆分会存在大量的顶点副本,都需要同步走网络IO发送顶点属性数据导致IO瓶颈
2. 减少存储空间
边切分一条边要存两次,顶点存一次;点分割是顶点存两次,边只存一次;现实世界中总是边要远远超过顶点数,所以边分割随着数据量的增加所占空间会越来越大,与点分割占空间差距越来越明显
- 数据分区
spark graphx分区策略:
CanonicalRandomVertexCut, 经典的随机点分割分区,源顶点和目标顶点不区分方向进行hash分区
EdgePartition1D,源顶点Id*1125899906842597L%分区数 进行分区
EdgePartition2D,利用生成二维矩阵进行分区
RandomVertexCut,随机点分割分区,源顶点和目标顶点二元组进行hash分区,区分方向
要更改图分区方式,只需要调用graph.pertitionBy方法指定分区策略即可
- 数据结构
1.VertexTable
2.EdgeTable
3.RoutingTable
负责将查找顶点属性位置并广播发送到计算节点
4. 图计算框架
- 深度优先搜索算法(DFS)
- 广度优先搜索算法(BFS)
- 基于遍历(DFS/BFS)算法的:Neo4j、OrientDB、DEX和 Infinite Graph
- 基于BSP(Bulk Synchronous Parallel Computing Model)模型,以图顶点 为中心的、基于消息传递批处理的并行引擎:GoldenOrb、Giraph、Pregel和Hama
- Pregal图计算框架 基于BSP模型的图计算框架
- Spark graphx就是基于BSP图计算模型实现的图计算框架,并封装了Pregal图计算框架API
- Spark GraphX 核心
- 编程基础:Spark core RDD编程
- RDD[VertexId,属性]
VertexId实际是个scala.Long类型
- RDD[Edge(属性)],Edge构建时参数Edge(srcId,desId,属性)
- RDD[EdgeTriplet]
EdgeTriplet继承自Edge并增加了源点属性和目标点属性
- Graph[顶点属性,边属性],构建时参数Graph(RDD[Vertex],RDD[Edge],defaultVertex)
- VertexRDD
继承自RDD[VertexId,属性],扩展一些独有方法,内部检索索引重用,避免进行遍历,提高计算效率
- EdgeRDD
继承自RDD[Edge],并扩展一些独有方法,内部索引重用,避免进行遍历,提高计算效率
5.属性图构建
- 方式一:从RDD创建
def createGraphFromRDD ={
log("从RDD构建Graph")
val sc = ss.sparkContext
val users: RDD[(VertexId, (String, String))] =
sc.parallelize(
Array(
(3L, ("15239871100", "陆小凤")),
(7L, ("15287679231", "花满楼")),
(5L, ("15287842135", "西门吹雪")),
(2L, ("15285461279", "司空摘星"))
))
// Create an RDD for edges
val relationships: RDD[Edge[(String,String)]] =
sc.parallelize(
Array(
Edge(3L, 7L, ("电话","2019-03-05 19:12:20")),
Edge(5L, 3L, ("短信","2019-03-08 09:20:05")),
Edge(2L, 5L, ("电话","2019-03-10 05:12:23")),
Edge(5L, 7L, ("短信","2019-05-21 22:20:45")),
Edge(5L, 8L, ("短信","2019-08-15 15:13:02"))
))
// Define a default user in case there are relationship with missing user
val defaultUser = ("None", "Nobody")
// Build the initial Graph
Graph(users, relationships, defaultUser)
}
val contactGraph = createGraphFromRDD
val triplet: RDD[EdgeTriplet[(String, String), (String, String)]] = contactGraph.triplets
triplet.collect().foreach(println)
//程序输出
((2,(15285461279,司空摘星)),(5,(15287842135,西门吹雪)),(电话,2019-03-10 05:12:23))
((3,(15239871100,陆小凤)),(7,(15287679231,花满楼)),(电话,2019-03-05 19:12:20))
((5,(15287842135,西门吹雪)),(3,(15239871100,陆小凤)),(短信,2019-03-08 09:20:05))
((5,(15287842135,西门吹雪)),(7,(15287679231,花满楼)),(短信,2019-05-21 22:20:45))
((5,(15287842135,西门吹雪)),(8,(None,Nobody)),(短信,20