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) Optimizer:对resolved 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 toRDD(execute()函数调用)执行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. 使用optimizer对resolved LogicalPlan进行优化,生成optimized LogicalPlan,优化前使用了ExtractPythonUdfs(catalog.PreInsertionCasts(catalog.CreateTables(analyzed)))进行预处理;
4. 使用hivePlanner将LogicalPlan转换成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, Flume和Kinesis等源获取的输入数据流创建,也可以在其他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是基于kafka高级consumer API实现的。使用Receiver将从kafka接收到的数据存入spark executor中,然后由spark streaming启动job来处理数据。但是这种方式在默认情况下可能会导致数据丢失,为了防止数据丢失,必须启动spark streaming的WAL机制(Write Ahead Logs),它会同步地将所有从kafka接收到的数据保存在分布式文件系统中(如HDFS),这样出错时也可以恢复。
需要注意几点:
1. kafka的topic的partition跟spark streaming中生成RDD的partition不相关。增加KafkaUtils.createStream()中特定主题分区的数量只会增加在单个接收器中使用的主题的线程数,并不会增加spark streaming中处理数据的并行度。
2. 可以使用不同的kafka consumer group和topic来创建多个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然后union。Spark Streaming将创建与要消费的Kafka分区一样多的RDD分区,这将从Kafka并行读取数据。
2. 高效:
使用receiver-based方式来防止数据丢失需要将数据写入到HDFS中,这会多一步复制数据。这样效率会低,因为数据被复制了两次,一次是kafka,另一次是WAL。direct方式消除了这个问题,因为没有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,因此基于zookeeper的kafka监测工具会失效,但是可以自己得到每批数据在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以创建新的StreamingContext并设置DStreams
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最核心的一个接口为MapReduceTriplets:1)map:应用于每一个Triplet(源点+边属性+汇点),生成多个消息,每个消息以Triplet关联的两个顶点中的任意一个或者两个作为目标顶点;2)reduce:应用于每个Vertex上,将发送给每个顶点的消息合并起来 其最终返回的是VertexRDD[A],包含每个顶点聚合后的消息(类型为A)
其中基于MapReduceTriplets的Pregel模型,会将顶点的多个Ghost副本收到的消息聚合,发送给Master副本,再利用vprog函数更新点值
*GraphX在淘宝图鉴体验平台
借助度分布将图中超级节点去掉,然后计算二跳邻居第一次遍历,所有点向邻居点传播一个带自身ID,生命值为2的消息;第二次遍历,所有点将收到的消息向邻居点再转发一次,生命值为1;最终统计所有点上,接收到的生命值为1的ID,并进行分组汇总,得到所有点的二跳邻居;GraphX提供了ConnectedComponents和StronglyConnected-Components算法,使用它们可以快速计算出相应的连通图。连通图可以进一步演化变成社区发现算法
借助GraphX实现了多图合并,提供了类似图并集,GraphX的outerJoinVertices等图运算符,能很简单地完成上述操作
能量传播模型:用于用户信誉预测
先生成以用户为点、买卖关系为边的巨型图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
- Spark知识体系
- Spark知识体系完整解读
- Spark知识体系完整解读
- Spark知识体系完整解读
- Spark知识体系完整解读
- Spark知识体系完整解读
- Spark知识体系完整解读
- 【转】Spark知识体系完整解读
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 知识体系
- 我的library类型的module集成极验aar包
- RabbitMq、ActiveMq、ZeroMq、kafka之间的比较,,
- CASS地物及地貌的诠释
- 关于java Annotation的总结
- 【学习笔记】Python爬虫-豆瓣电影所有短评
- Spark知识体系
- Android开发WebView报错:Failed to locate a binder for interface: autofill::mojom::PasswordManagerDriver
- 标题栏渐变效果的实现
- 面试常考的常用数据结构与算法
- ERROR_COLLECTION
- android textview 代码设置字体大小包含适配
- python中的条件判断语句
- c语言笔记-2
- FFmpeg处理视频和音频的相关链接