Spark知识体系

来源:互联网 发布:大数据国内外研究现状 编辑:程序博客网 时间:2024/06/07 11:21

1.大数据的四大特征(4V):海量的数据规模,快速的数据流动和动态的数据体系,多样的数据类型,巨大的数据价值

 

2.RDD与DataFrame区别:RDD是一个抽象数据集,DataFrame则相当于一个二维表,带有schema元数据信息,更便于上层应用分析

3.DataFrame和DataSet的区别:每个DataSet有一个被称为DataFrame的类型化视图,它只有在执行action操作时才触发计算

4.DataSet与RDD的区别:DataSet是特定域的对象集合,RDD是任意对象的集合

5.当遇到Task not serializable,一般因为map、filter等的参数使用了外部的变量,而该变量不能序列化,特别是当引用了某个类成员函数或者变量时。

解决方案:
1)对类中不需要序列化的部分用transient修饰

2)将引用的类序列化

3)将需要序列化的变量用更小的class保存,并让该class序列化

4)将该变量在map或者filter中定义

 

6.Spark SQL再学习:

具有如下特征:
1)HQL方面重用Hive中HQL的解析、逻辑执行计划翻译、执行计划优化等逻辑,可以近似认为是把物理执行计划从MR作业变为了Spark作业

2)依赖Hive Metastore和Hive SerDe(兼容各种Hive存储格式)

3)支持对原生RDD的关系查询

 

功能模块:

1)        core处理数据的输入输出,从不同数据源获取数据(RDD,Parquet,Json),将查询结果输出成schemaRDD

2)        catalyst考虑查询语句的整个处理过程,包括解析,绑定,优化,物理计划等,优化器实际上是查询引擎

3)        hive

4)        hive-ThriftServer提供CLI和JDBC/ODBC

 

其中catalyst的实现组件:

1)        sqlParse:完成sql语句的语法解析功能,解析成Tree

2)        Analyzer:主要完成绑定工作,将不同来源的Unresolved LogicalPlan和数据元数据(如hive metastore、Schema catalog)进行绑定,生成resolved LogicalPlan,使用Analysis Rules

3)        Optimizerresolved LogicalPlan进行优化,生成OptimizerLogicalPlan,使用Optimization Rules,对resolved LogicalPlan进行合并,列裁剪,过滤器下推等优化操作转换为OptimizerLogicalPlan

4)        Planner:将LogicalPlan转换成PhysicalPlan,使用Planning Strategies

5)        Cost Model:根据过去的性能统计数据,选择最佳的物理执行计划

 

各组件的实现细节:http://www.aboutyun.com/thread-20910-1-1.html

完整的SQL执行流程:
 sql or hql -> sqlparser(parse)生成 unresolved logical plan-> analyzer(analysis)生成analyzed logicalplan  -> optimizer(optimize)optimized logical plan -> sparkplanner(use strategies to plan)生成physical plan ->采用不同Strategies生成spark plan -> sparkplan(prepare) prepared spark plan -> call toRDDexecute()函数用)执行sql生成RDD

 

 

hiveContext.sql()的执行过程:

1)判断其语法设置,如果dialect =="sql"则采用sqlContext的sql语法执行过程;如果是dialect == "hiveql",则采用hiveql语法执行过程

2)如果判断语法设置为hiveql,则

1.SQL语句经过HiveQl.parseSql解析成Unresolved LogicalPlan,在个解析程中hiveql语句使用getAst()获取AST树,然后再进行解析
2.
使用analyzer结合数据hive源数据Metastore(新的catalog定,生成resolved LogicalPlan
3.
使用optimizerresolved LogicalPlan进行优化,生成optimized LogicalPlan化前使用了ExtractPythonUdfs(catalog.PreInsertionCasts(catalog.CreateTables(analyzed)))进行预处理
4.
使用hivePlannerLogicalPlan转换成PhysicalPlan
5.
使用prepareForExecution()PhysicalPlan转换成可执行物理计划
6.
使用execute()执行可执行物理计划
7.
执行后,使用map(_.copy)SchemaRDD

 

DataFrame的使用方法分为:

1)直接执行相关算子,比如map,filter,groupBy等

2)转化为虚拟二维表,通过sql语句分析

 

 

7. RDD to DataFrame(RDD转换为DataFrame)

先创建一个类

case class Person(name: String, age: Int)

然后将Rdd转换成DataFrame

val people =sc.textFile("/usr/people.txt").map(_.split(",")).map(p=> Person(p(0), p(1).trim.toInt)).toDF()

 

8.利用HiveContext查询虚拟表

 

9.SparkStreaming再学习

sparkstreaming可以实时跟踪统计训练机器学模型或动检测异常等.

创建spark streamingcontext的步骤:

importorg.apache.spark.streaming._

valsc = ... // existing SparkContext

valssc = new StreamingContext(sc, Seconds(1)) //每隔1s封装一次RDD,提交一次Job

使用SparkStreaming Context的步骤:

1)指定输入源

2)准备好流计算指令

3)利用StreamingContext.start()方法接收和处理数据

4)处理过程一直持续,直到streamingContext.stop()

 

SparkStreaming支持一个高的抽象,叫做离散流( discretized stream )或者DStream,它代表连续的数据流。DStream既可以利用从Kafka, FlumeKinesis等源取的入数据流建,也可以在其他DStream的基上通函数得。在内部,DStream是由一系列RDDs组成

 

所有的输入DStream都会绑定一个Receiver对象,用于接收数据源中的数据,并存储在Spark内存中,其中提供了三种内置的数据源支持:
1)基础数据源:StreamingContext中提供了对于文件,socket,Akka Actor的支持

2)高级数据源:诸如kafka,Flume,Kinesis,Twitter等数据源,数据源的支持需要引用其依赖

3)自定义数据源:用于决定如何接收和存储数据

 

如果想要在实时计算中并行接收多条数据流,可以创建多个输入DStream,这样就会创建多个Receiver,从而并行接收多个数据流,值得注意的是一旦Spark Streaming运行起来,这个节点的CPU Core便无法给其他应用使用

 

如果使用本地模式运行程序,至少使用local[2],能保证一条线程分配给Receiver接收数据,一条线程用于处理接收到的数据

 

Windows滑动窗口机制:

(1)  窗口长度(windowlength):即窗口的持续时间

(2)  滑动间隔(slidinginterval):窗口操作执行的时间间隔

这两个参数必须是原始DStream批处理时间间隔的整数倍

 

 

Spark Streaming之所以称为微批处理的原因:DStream(时间片划分,封装RDD)->多个Batch(RDD)->RDD执行Job的操作

指定的是封装RDD的时间间隔为5s,在5s期间任何添加到DStream中的数据都会被加入此RDD中,即Batch中直到时间间隔结束

 

DStreams提供两种类型的操作:

1)transformations:产生新的DStream

transformations又分为有状态和无状态,其中有状态transformation操作需要checkpointing,通过ssc.checkpoint(“hdfs://localhost:9000/usr”)

 

DStream的Operation包括:

a)  transform(func):将旧RDD通过func转换成新的RDD

b)  updateStateByKey(func)根据key更新状态

定义状态:此状态可以是任意数据类型

定义状态更新函数:更新状态

对每个word调用一次更新函数,newValues是最新值,runningCount是之前值

2)output operations:写数据到外部系统

 

checkpoint机制:推荐设置为batch duration的5~10倍

 

Spark Streaming对Kafka做数据接收的方案有两种:

1)Receiver-based Apporach:接收和处理分开,容易使得接收的节点崩溃

Receiver-based是基于kafkaconsumer API实现的。使用Receiver将从kafka接收到的数据存入spark executor中,然后由spark streamingjob理数据。但是种方式在默情况下可能会致数据失,了防止数据失,必spark streamingWAL机制(Write Ahead Logs),它会同步地将所有从kafka接收到的数据保存在分布式文件系中(如HDFS),这样错时也可以恢复。

需要注意几点:
1. kafka
topicpartitionspark streaming中生成RDDpartition不相关。增加KafkaUtils.createStream()中特定主分区的数量只会增加在个接收器中使用的主线程数,并不会增加spark streaming理数据的并行度。

2.
可以使用不同的kafka consumer grouptopic建多个kafka输入DStream这样就可以使用多个Receiver来并行的接收kafka数据。

3.
如果启用了WAL机制,需要将storage level设置为MEMORY_AND_DISK_SER,可以通KafkaUtils.createStream(...,StorageLevel.MEMORY_AND_DISK_SER)置。

2)Direct Apporach:直接把Kafka的partition映射成RDD的partition,只有当需要处理时才从Kafka中取出对应数据

KafkaUtils.createDirectStream()

 

使用direct方式会定期的查询kafka topic+partition的最新的offset,相地会在spark streaming中定每批要理数据的offset range。当理数据的job后,会使用低consumer API来来offsetrange的数据(似于从一个文件中取文件)。

这种方式相比于receiver- based方式有以下几个优势
1.
简化并行度
不需要建多个kafka输入DStream然后unionSpark Streaming建与要消Kafka分区一多的RDD分区,将从Kafka并行取数据。

2.
高效:
使用receiver-based方式来防止数据失需要将数据写入到HDFS中,会多一步复制数据。这样效率会低,因数据被复制了两次,一次是kafka,另一次是WALdirect方式消除了问题,因没有receiver,所以不需要WAL,数据恢复只需要由kafka完成即可。

3. Exactly-once
语义
使用receiver-based方式可以保数据不失(at-least once语义),但是在一些小概率情况下,一些记录可能在一些故障下处理两次这是因为Spark Streaming可靠接收的数据与Zookeeper跟踪的偏移之间存在不一致。因此在direct方式中,使用kafka低级consumer,将offset由spark streaming维护,存入到checkpoint中,这就避免了offset在spark streaming和kafka/zookeeper之间的不一致,所以即便在出现故障的情况下,数据也只会被spark streaming有效地接受一次。为了实现Exactly-once语义将数据保存到外部数据存储的输出操作必须是幂等的,或者是保存结果和偏移量的原子事务。

但是使用种方式不会更新zookeeper中的offset,因此基于zookeeperkafka监测工具会失效,但是可以自己得到每批数据在kafka中的offset,并自己更新zookeeper中的offset

 

 

SparkStreaming日志分析思考、选择方案及部分代码实现
http://www.aboutyun.com/forum.php?mod=viewthread&tid=21593

 

SparkStreaming Checkpoint

1)   Metadata Checkpoint:适用于Driver所在节点出错时恢复,元数据包括Configuration(配置信息):创建Streaming应用程序的配置信息

DStreamOperations:在Streaming应用程序中定义的DStreaming操作

Incompletebatches:队列中未处理完的作业

2)Data checkpointing

对有状态的transformation操作执行checkpoint

 

如果checkpointDirectory存在,那么将从checkpoint数据重新StreamingContext。如果目不存在(即,第一次运行),用函数functionToCreateContext建新的StreamingContextDStreams

 

 

SparkStreaming性能调优:

1)利用集群资源,减少每个批次数据的处理时间

2)给每个批次数据设置合适大小

 

基本调优方式:

1)并行度:通过设置spark.default.parallelism提高算子操作的并行度

2)任务的启动开销:选择高性能的发送任务的负载

3)设置正确的批次数据大小:通过查看日志总的延迟,如果延迟小于批次时间,系统可以稳定。

 

Spark 24/7 Operation

SparkStreaming需要周期性的清理元数据,spark.cleaner.ttl,一旦超过设置时间,Spark Streaming开始清理元数据,并持久化RDD

Monitor:通过Spark内置监控器外,还可以StreamingListener获取批处理时间,查询间隔,全部的端到端试验

 

MemoryTuning:设置spark.streaming.unpersist=true,由spark决定哪些RDD需要持久化,使用concurrent mark-and-sweep GC提供更稳定的批处理

 

命令行提交jar

1)本地:bin/spark-submit--master local[4] --name demo --class com.SparkStreaming.SparkStreamingDemoScala.jar

2)standalone: bin/spark-submit --master spark://localhost:7077 --name demo --classcom.SparkStreaming.SparkStreamingDemo/Users/George/Documents/Scala/out/artifacts/Scala_jar/Scala.jar

3)yarn-client

bin/spark-submit--master yarn-client --name demo --class com.SparkStreaming.SparkStreamingDemoScala.jar

 

bin/spark-submit--master yarn --deploy-mode client --name demo --classcom.SparkStreaming.SparkStreamingDemo/Users/George/Documents/Scala/out/artifacts/Scala_jar/Scala.jar

4)yarn-cluster

bin/spark-submit--master yarn-cluster --name demo --class com.SparkStreaming.SparkStreamingDemoScala.jar

 

yarn-client与yarn-cluster区别:前者client与Driver是同一个节点,因此client可以看到最终结果;而后者executor与Driver是同一个节点,因此在client看不到最终结果,只能在cluster的日志文件中看到

 

10.Spark Mllib再学习

Spark Mllib算法优化:

逻辑回归采用梯度下降法来寻找weight的最优解,其中梯度下降法分为Gradient梯度计算与weight update两个步骤计算

Gradient负责梯度算,包括LogisticGradient、LeastSquaresGradient、HingeGradient三种梯度算,其中data对应x,label对应y,weights对应θ,下面为逻辑失函数(避免过拟象):

J(θ)的导数为gradient,J(θ)即为loss

 

Weightupdate(迭代更新计算)包括SimpleUpdater、L1Updater、SquaredL2Updater三种更新策略

1)SimpleUpdater

2)L1Updater

3)SquaredL2Updater

1)推荐算法

a)基于人口统计学的推荐:假设“一个用有可能会喜与其相似的用所喜的物品”,利用User Profile计算其它用户与其之间的相似度,然后挑选出与其最相似的前K个用,之后利用些用购买和打分信息行推荐

b)基于内容的推荐:假设“一个用可能会喜和他曾欢过的物品相似的物品

c)基于协同过滤的推荐:可以分为基于用的推荐(User-based Recommendation),基于物品的推荐(Item-based Recommendation),基于模型的推荐(Model-based Recommendation),即收集用户过去的行品的显式或者隐式信息,根据用户对于商品的偏好,挖掘出物品或者内容本身的相关性,或者用户的相关性,

 

典型的推荐算法:ALS算法(交替最小二乘法)

基本思想:对用户(user)-商品(item)的评分矩阵进行分解,其一为用户对商品隐含特征的偏好矩阵,其二为商品所包含的隐含特征的矩阵,即基于填充的评分进行推荐

关键参数:

numBlocks
用于并行化算的分个数(-1为自动分配)
rank

模型中藏因子的个数,也就是上面的r
iterations

迭代的次数,推荐10-20
lambda

惩罚函数的因数,是ALS的正化参数,推荐0.01
implicitPrefs

决定了是用性反ALS的版本是用适用性反数据集的版本
alpha

是一个针对性反 ALS 版本的参数,个参数决定了偏好行为强度的基准

 

显式反馈 vs 隐式反馈

显式反馈:基于矩分解的过滤准方法一般将用商品矩中的元素作户对商品的性偏好,比如

式反:比如用,点购买,喜,分享等等,它将数据作为二元偏好值与偏好强度的结合,评价即为所观察到的用户偏好强度,预估一个用户对一个商品的偏好

 

通过计算均方差(Mean Squared Error, MSE)来价ALS模型的好坏

 

典型算法线性回

特征提取:1)类别特征:通过二元祖表示特征,比如水果一共三类分别为苹果,香蕉,梨子;则表示成二元祖分别为001,010,100 2)实数特征:直接使用

Spark Mllib中调用的是trainLinearRegressionModelWithSGD()训练模型模型的两个参数,一个是特征向量对应重向量(weights),另一个是截距(intercept

 

模型评估标准:

1)MSE为均方差

valMSE=true_vs_predicted.map(value=>

 {

   (value._1-value._2)*(value._1-value._2)

 }).mean()

 2)MAE(平均绝对误差)为绝对值差的平均值

 val MAE=true_vs_predicted.map(value=>

 {

   math.abs(value._1-value._2)

 }).mean()

 3)RMSLE(均方根对数误差)对数差的平方的平均值

 val RMSLE=true_vs_predicted.map(value=>

 {

   math.pow(math.log(value._1+1)-math.log(value._2+1),2)

 }).mean()

 

改进模型准确率的方案:

1)调整建模函数的参数

2)归一化处理特征

3)预处理类别特征

 

推荐实例:携程app推荐用户住哪个酒店

1)获取历史数据

可知Alice订购过希尔顿(201),四季酒店(202)

2)预处理数据

即x酒店有哪几个用户住过

3)计算酒店的相似度

即I(i,j)求酒店i与酒店j公共客户有几个

L(i,j)求酒店i的客户数*酒店j的客户数

S(i,j)= I(i,j)/ L(i,j)

4)推荐

由于Alice只住过201和202,因此看201和202的相似度组成

 

由于0.5>0,因此优先推荐204

 

Mllib在淘宝

 

根据淘宝用户的购买行为中使用决策树判断用户性别

1)构建决策树的伪代码:

如果满足终止条件,就返回一个叶节点,根据当前样本的数据为它选择一个标签。

如果不满足终止条件,就需要对这些样本进行划分,首先计算所有可能分割的信息增量,

然后从这些分割中选出信息增量最大的分割。

然后依据这个分割对这些样本进行划分,会得到样本的几个子集,

对其中的每一个子集递归的调用构建决策树的方法构建一颗子树,

然后组合这些子树,作为结果返回。

 

2)在Spark中决策树的训练采用逐层训练方式,一次确定一层所有节点的最好划分,训练决策树主要分为以下步骤:

  a)对每个特征进行划分,即找到每个特征的分割点,对于每一种可能的划分,对应于一个或者多个桶,用来统计样本的数据,从而计算信息增量

  b)特征划分好后,开始逐层训练,即找到当前层所有节点的最好划分

       首先确定每个样本数据应该分配到哪个桶

       聚合每个桶的统计信息

       计算当期分割的信息增量

       找到该层所有节点的最好分割

    c)判断是否满足终止条件,不满足则进入下一层

实现细节:

1)特征划分:

a)抽样+排序,统计每个值出现次数

b)求分位次数=数组长度/(分割点数+1)

c)选择累加次数最接近分位次数的值作为分割点

2)样本分桶

样本到桶的映射,只在开始时计算一次,这次的PR与社区的工作重合了

3)信息增益

ReduceByKey把聚合信息shuffle到Executor上,并在Executor上执行

核心思想:将nodeID作为key,通过reduceByKey把每个node的统计信息shuffle到每个executor,由executor完成相应node的最好分割计算,最后发送给driver的只有最佳分割信息

4)最好分割:前向剪枝(根据下面两个分割条件,当不满足时提前结束分割)

停止分割的两个参数:a)minInfoGain:分割造成的最小信息增量b)minInstancesPerNode:分割出左右儿子的最少样本数

5)最好分割:避免叶节点的计算,所谓叶结点即最后一层结点或者Impurity=0,可以不用再继续分割

多分类:

多分类是通过One-VS-All策略,组合二分类实现多分类

每个类训练一个二分类器,通过Estimator.fit得到多分类器

对每个类进行预测得到概率probs,取概率最大的作为结果

 

 

 

11.Spark GraphX图计算学习

主要用于图和并行图计算,引入了带有顶点和边属性的有向多重图,并提供了很多算子操作(subgraph,joinVertices,and aggregateMessages)以及pregel

属性图:用户为每个顶点和边定义对象,潜在的多重平行边共享相同的源和目的顶点,属性图通过顶点(VD)和边(ED)类型参数化。这些类型分别指与顶点和边相关的对象。GraphX优化了顶点和边类型表示,当它们使用原始数据类型(像 int,double等),使用特殊数组存储它们降低了内存使用。其中属性图是只读的,分布式的,容错的,图被分区通过executors使用一个范围的顶点进行启发式分区。

属性图的源码:

classGraph[VD, ED] {

  val vertices: VertexRDD[VD] //对应于RDD[(VertexID, VD)]

  val edges: EdgeRDD[ED] //RDD[Edge[ED]]

}

图存储模型:采用点分割,虽然增加了存储开销,但是减少了网络开销

图计算模型:遵循BSP,即一次计算即为一个超步过程,一个超步分为并发计算,通信,栅栏同步三部分组成;Pregel借鉴MapReduce思想,提出“像顶点一样思考”的图计算模式,用户只需实现一个顶点更新函数,让框架在遍历顶点时调用即可;GAS(邻居更新模型),它允许用户的自定义函数访问当前节点的整个邻域,可抽象成Gather(从邻居点和边收集数据),Apply(更新顶点Master)和Scatter(更新邻边和邻居点)

Graph结合了点分割和GAS的模式,其核心抽象是Resilient Distributed Property Graph性分布式属性),有Table和Graph两种视图,而只需要一份物理存储

GraphX的底层设计关键在于:

1)对于Graph视图的所有操作,最终都会转化成与其相关的Table视图的RDD操作完成,理论上所有图操作和转换都会产生新图

2)两种视图底层共用物理数据,由RDD[VertexPartition]和RDD[EdgePartition]组成

3)图采用点分割模式,而且使用partitionBy,由用户指定不同的划分策略,划分策略会将边分配到各个EdgePartition,顶点Master分配到各个VertexPartition,EdgePartition也会缓存本地边关联点的Ghost副本

EdgePartition2d效果最好

GraphX的默接口只提供了unpersistVertices方法。如果要g.edges.unpersist()方法才行

 

GraphX最核心的一个接口MapReduceTriplets1map应用于每一个Triplet(源点+属性+点),生成多个消息,每个消息以Triplet的两个点中的任意一个或者两个作标顶点;2)reduce:用于每个Vertex上,将每个点的消息合并起来 其最返回的是VertexRDD[A],包含每个点聚合后的消息(A

其中基于MapReduceTriplets的Pregel模型,会将顶点的多个Ghost副本收到的消息聚合,发送给Master副本,再利用vprog函数更新点值

 

*GraphX在淘宝图鉴体验平台

借助度分布将图中超级节点去掉,然后计算二跳邻居第一次遍,所有点向居点播一个自身ID,生命值为2的消息;第二次遍,所有点将收到的消息向居点再转发一次,生命值为1;最终统计所有点上,接收到的生命值为1ID,并行分组汇总,得到所有点的二跳居;GraphX提供了ConnectedComponentsStronglyConnected-Components算法,使用它可以快速算出相可以一步演化成社区发现算法

借助GraphX实现了多图合并,提供了类似图并集,GraphXouterJoinVertices运算符,能很简单地完成上述操作

 

能量播模型:用于用信誉预测

先生成以用户为点、买卖关系为边的巨型initGraph对选出的种子用,分别赋予相同的初始正能量trustRank & badRank),然后行两随机游走,一好种子播正能量(tr),一坏种子能量(br),然后正能量相减得到finalRank,根据finalRank判断用的好坏

 

B中某的两个点都在A中,该边加入C图(如BD边);若图B中某的一个点在A中,另一个点不在,该边和另一点都加上(如CE边和E点);若A中某的两个点都不在B中,舍弃

图计算主要用于社会网或者PageRank

 

GraphX实现了Pregel算法:与标准Pregel不同的是,GraphX中的仅仅送信息给邻点,并利用用自定的消息函数构造消息。些限制允GraphX进行额外的优

class GraphOps[VD, ED] {

 def pregel[A]

     (initialMsg: A, //配置参数初始消息

      maxIter: Int = Int.MaxValue,//最大迭代数

      activeDir: EdgeDirection = EdgeDirection.Out)//发送消息的边方向

     (vprog: (VertexId, VD, A) => VD,//接收消息

      sendMsg: EdgeTriplet[VD, ED] => Iterator[(VertexId, A)],//计算消息

      mergeMsg: (A, A) => A)//合并消息

   : Graph[VD, ED] = {

   // Receive the initial message at each vertex

   var g = mapVertices( (vid, vdata) => vprog(vid, vdata, initialMsg)).cache()//接收每个顶点的初始消息

   // compute the messages

   var messages = g.mapReduceTriplets(sendMsg, mergeMsg)//计算消息

   var activeMessages = messages.count()

   // Loop until no messages remain or maxIterations is achieved

   var i = 0

   while (activeMessages > 0 && i < maxIterations) {//循环直到没有消息或者达到最大迭代次数

     // Receive the messages:-----------------------------------------------------------------------

     // Run the vertex program on all vertices that receive messages

     val newVerts = g.vertices.innerJoin(messages)(vprog).cache()//运行在顶点程序接受消息

     // Merge the new vertex values back into the graph

     g = g.outerJoinVertices(newVerts) { (vid, old, newOpt) =>newOpt.getOrElse(old) }.cache()//合并新的顶点值

     // Send Messages:------------------------------------------------------------------------------

     // Vertices that didn't receive a message above don't appear in newVertsand therefore don't //没有接受到消息的顶点不会出现在新的顶点集中

      //get to send messages.  More precisely themap phase of mapReduceTriplets is only invoked//发送消息,mapReduceTriplets的map操作执行

     // on edges in the activeDir of vertices in newVerts

     messages = g.mapReduceTriplets(sendMsg, mergeMsg, Some((newVerts,activeDir))).cache()//每条由新顶点集中每个顶点的发送消息的边方向发送消息

     activeMessages = messages.count()

     i += 1

   }

   g

 }

}

1)通过调用vprog()接受每个顶点的消息

2)g.mapReduceTriplets(sendMsg, mergeMsg)算消息

3)判断是否由剩余消息或者达到迭代次数

4)g.vertices.innerJoin(messages)(vprog)每个接收到消息的点运行vprog

5) g.mapReduceTriplets(sendMsg,mergeMsg, Some((newVerts, activeDir)))送消息邻节

 

利用pregel单源最短路径

importorg.apache.spark.graphx._

//Import random graph generation library

importorg.apache.spark.graphx.util.GraphGenerators

//A graph with edge attributes containing distances

valgraph: Graph[Int, Double] =

  GraphGenerators.logNormalGraph(sc,numVertices = 100).mapEdges(e => e.attr.toDouble)

valsourceId: VertexId = 42 // The ultimate source

//Initialize the graph such that all vertices except the root have distanceinfinity.

valinitialGraph = graph.mapVertices((id, _) => if (id == sourceId) 0.0 elseDouble.PositiveInfinity)

valsssp = initialGraph.pregel(Double.PositiveInfinity)(

  (id, dist, newDist) => math.min(dist,newDist), // Vertex Program

  triplet => {  // Send Message

    if (triplet.srcAttr + triplet.attr <triplet.dstAttr) {

      Iterator((triplet.dstId, triplet.srcAttr+ triplet.attr))

    } else {

      Iterator.empty

    }

  },

  (a,b) => math.min(a,b) // Merge Message

  )

println(sssp.vertices.collect.mkString("\n"))

 

GraphLoader.edgeListFile:Graph[Int,Int]//提供了一种从磁盘上的边列表中加载一个图(sourceId,targetId)

object GraphLoader {

  def edgeListFile(

      sc:SparkContext,

      path: String,//边列表所在文件

     canonicalOrientation: Boolean = false, //边方向默认是sourceId<targetId

     minEdgePartitions: Int = 1) //边的最小分区数

    : Graph[Int,Int]

} //默认VD和ED均为1

# This is a comment

2 1

4 1

1 2

 

object Graph {

 def apply[VD, ED]( //允许从顶点和边的RDD上构建属性图

     vertices: RDD[(VertexId, VD)],

     edges: RDD[Edge[ED]],

     defaultVertexAttr: VD = null)

   : Graph[VD, ED]

 def fromEdges[VD, ED](//允许仅从边构建图,并自动创建相关节点赋予默认的VD

     edges: RDD[Edge[ED]],

     defaultValue: VD): Graph[VD, ED]

 def fromEdgeTuples[VD]( //允许仅仅一个边元组构成属性图,边默认是1,自动创建相关顶点

     rawEdges: RDD[(VertexId, VertexId)],

     defaultValue: VD,

     uniqueEdges: Option[PartitionStrategy] = None): Graph[VD, Int]

}

 

PageRank度量每个顶点的重要度,分为静态(运行固定的迭代次数),动态(一直运行直到收敛)

// Load the edges as a graph

val graph =GraphLoader.edgeListFile(sc, "graphx/data/followers.txt")

// Run PageRank

val ranks =graph.pageRank(0.0001).vertices

// Join the ranks with the usernames

val users =sc.textFile("graphx/data/users.txt").map { line =>

 val fields = line.split(",")

 (fields(0).toLong, fields(1))

}

val ranksByUsername =users.join(ranks).map {

 case (id, (username, rank)) => (username, rank)

}

// Print the result

println(ranksByUsername.collect().mkString("\n"))

 

连通体算法利用id标识每个连通体,将连通体中顶点id最小的作为此连通体的id

/ Load the graph as in the PageRankexample

val graph =GraphLoader.edgeListFile(sc, "graphx/data/followers.txt")

// Find the connected components

val cc =graph.connectedComponents().vertices

// Join the connected components withthe usernames

val users =sc.textFile("graphx/data/users.txt").map { line =>

 val fields = line.split(",")

 (fields(0).toLong, fields(1))

}

val ccByUsername = users.join(cc).map{

 case (id, (username, cc)) => (username, cc)

}

// Print the result

println(ccByUsername.collect().mkString("\n"))

 

三角形计数算法用于计算每个顶点的三角形数

// Load the edges in canonical orderand partition the graph for triangle count

 

val graph =GraphLoader.edgeListFile(sc, "graphx/data/followers.txt",true).partitionBy(PartitionStrategy.RandomVertexCut)

 

// Find the triangle count for eachvertex

 

val triCounts =graph.triangleCount().vertices

 

// Join the triangle counts with theusernames

 

val users =sc.textFile("graphx/data/users.txt").map { line =>

 

 val fields = line.split(",")

 

 (fields(0).toLong, fields(1))

 

}

 

val triCountByUsername =users.join(triCounts).map { case (id, (username, tc)) =>

 

 (username, tc)

 

}

 

// Print the result

 

println(triCountByUsername.collect().mkString("\n"))

 

Pregel算法详解:

计算模型如下图所示,重要的有三

1.    作用于每个点的逻辑 vertexProgram

2.    消息送,用于相邻节的通 sendMessage

3.    消息合并逻辑messageCombining

PageRank基于Pregel模型,因此需要实现vertexProgram,sendMessage,messageCombiner三个函数

def vertexProgram(id: VertexId, attr:(Double, Double), msgSum: Double): (Double, Double) = {

     val (oldPR, lastDelta) = attr

     val newPR = oldPR + (1.0 - lastDelta) * msgSum

     (newPR, newPR - oldPR) //计算每个顶点新的属性值

}

 

def sendMessage(edge: EdgeTriplet[(Double, Double), Double]) = {//向邻居点发送属性值

      if (edge.srcAttr._2 >tol) {

        Iterator((edge.dstId,edge.srcAttr._2 * edge.attr))

      } else {

        Iterator.empty

      }

    }

 

def messageCombiner(a: Double, b: Double): Double = a + b //合并属性值

 

Spark onECS(阿里云)

实时消息(SparkStreaming)+实时图构建(GraphX)=动态图模型,用于社区决策

融合模型1(Join+Vote)

1)创建spark streaming

2)得到在线数据流

3)得到边流RDD

4)遍历边流RDD,构建边RDD与stockEdgRDD融合得到新的边RDD

5)通过join操作将两个RDD合并成新的社区RDD

6)利用每个顶点的邻居点对每个顶点进行vote投票,得到新的newCommRDD

7)以及新的stockEdgeRDD


在社交网中,不同用的受迎程度如何(PageRank
基于网中的用户连接来分群(Connected Components
社区发现社交网中的用社区的粘度分析(Triangle Counting

 

原创粉丝点击