Apache Spark是一个通用的分布式计算框架,基于内存的特点使得其以高性能著称。Spark应用可以运行在本地模式或者集群模式,集群模式中通过ClusterManager来管理整个应用,目前Spark提供了3中Cluster Manager:Standalone,Mesos,YARN。
除了核心的计算引擎Spark Core以外,Spark还有一系列相关的项目:
Spark Core
Spark Core是Spark的核心组件,其基于核心的RDD抽象,提供了分布式作业分发、调度及丰富的RDD操作,这些操作通过Java、Python、Scala、R语言接口暴露。RDD是分布于集群各节点上的、不可变的弹性数据集,容纳的是Java/Python/Scala/R的对象实例。Spark定义了RDD之上的两种操作:Transformation和Action,RDD上运行一个操作之后,生成另一个RDD或者执行某些动作。广播变量和累积器是Spark提供的另外两个特性。使用Spark来完成单词统计的代码片段如下:
val conf = new SparkConf().setAppName("wiki_test")
val sc = new SparkContext(conf)
val data = sc.textFile("hdfs://master:9000/path/to/data")
val tokens = data.flatMap( _.spalit(" "))
val wordFreq = tokens.map(_,1).reduceByKey(_+_)
wordFreq.sortBy(s=>-s._2).map(x=>(x._2,x._1)).top(10)
Spark Streaming
Spark Streaming基于Spark的计算性能进行流式的分析,将流式数据根据时间切分为小批量的数据(RDD),并在RDD上执行Transformation、Action操作完成分析。这种方式使得现有的很多批处理代码可以直接工作在流式处理的模式下。但是这种模式以牺牲一定的延时为代价,相比于其他基于事件或者消息的流式处理框架(Storm,Samza,Flink),延时比较大。Spark Streaming内置支持来自Kafka,Flume,ZeroMQ,Kinesis,Twitter,TCP/IP Socket的数据。
Spark SQL
Spark SQL引入了称为DataFrames的数据抽象,提供对结构化和半结构化数据操作的支持。Spark提供了一套DSL用于DataFrame的操作,DSL可以通过Scala,Java,Python来表示。同时Spark SQK提供了对标准SQL语言的支持,包括命令行接口和ODBC/JDBC支持(驱动)。下面是使用JDBC的一个代码片段:
import org.apache.spark.sql.SQLContext
val url="jdbc:mysql://ip:port/test?user=user:password=password"
val sqlContext = new org.apache.spark.sql.SQLContext(sc)
val df = sqlContext
.read
.format("jdbc")
.option("url",url)
.option("dbtable","people")
.load()
df.printSchema()
val countsByAge = df.groupBy("age").count()
结构化数据处理和查询
MLib
MLib是基于Spark的机器学习框架,由于Spark的分布式内存框架,其性能相比基于磁盘的Mahout有了大幅度的提升。MLib实现了很多常用的机器学习算法:
- 概要统计、相关性、抽样、假设检验等
- 分类和回归:SVM,逻辑回归,线性回归,决策树,朴素贝叶斯等
- 协同过滤:包含ALS等实现
- 聚类分析:K-Means,LDA
- 降维:SVD奇异值分解,PCA主成分分析等
- 特征提取与转换函数
- 优化算法:SGD,L-BFGS等
下面是使用MLib中的K-Means进行聚类的例子:
// 导入mllib相关类
import org.apache.spark.mllib.clustering.{KMeans, KMeansModel}
import org.apache.spark.mllib.linalg.Vectors
// 数据准备
val data = sc.textFile("data/mllib/kmeans_data.txt")
val parsedData = data.map(s => Vectors.dense(s.split(' ').map(_.toDouble))).cache()
// 聚类
val numClusters = 2
val numIterations = 20
val clusters = KMeans.train(parsedData, numClusters, numIterations)
// 评估聚类效果
val WSSSE = clusters.computeCost(parsedData)
println("Within Set Sum of Squared Errors = " + WSSSE)
// 保存模型
clusters.save(sc, "target/org/apache/spark/KMeansExample/KMeansModel")
//重新使用保存的模型
val sameModel = KMeansModel.load(sc, "target/org/apache/spark/KMeansExample/KMeansModel")
GraphX
GraphX则是基于Spark的分布式图处理框架,对标到Hadoop体系下基于MapReduce(因此基于磁盘)的图处理框架Giraph.由于RDD是不可变的,因此GraphX不适合需要更新操作的场景。GraphX提供了两套API,一套类似于Google Pregel提供的API,另一套则更像是MapReduce的风格。
GraphX与Pregel、Giraph都是基于BSP(Bulk Synchronous Parallel)模型,而GraphLab则基于MPI的异步模型。在分布式切割上,GraphX采用的是顶点分割而不是边分割,主要基于现实世界中的图边的数量往往大于定点的数量。顶点维护一个相关边的路由,记录哪些存储边的机器需要使用到该顶点,数据则通过广播分发到对应的边节点上。
GraphX支持基于Neo4J和Titan的图存储。GraphX将属性图模型抽象为一对RDD,封装顶点和边的属性:
class Graph[VD,ED] {
val vertices: VertexRDD[VD]
val edges: EdgeRDD[ED]
}
VertexRDD[VD]扩展自RDD[(VertexID,VD)]并进行了优化,EdgeRDD[ED]则扩展自RDD[Edge[ED]]。假设我们有下面这样的属性图:
在GraphX中,声明这个图的方式如下:
val userGraph = Graph[(String,String),String]
泛型参数分别代表顶点和边的类型。图的数据除了可以来自原始的文件,也可以来自RDD,GraphX统一通过Graph Builder来完成。下面的代码示例从RDD中构建一个图:
val sc: SparkContext
val users: RDD[VertexId,(String,String)] =
sc.parallelize(Array((3L,("rxin","student")),((7L,("jgonzal","postdoc")),(5L,("franklin","prof")),(2L,("istoica","prof"))))
val relationships : RDD[Edge[Stirng]] =
sc.parallelize(Array(Edge(3L,7L,"collab"),Edge(5L,3L,"advisor"),Edge(2L,5L,"colleague"),Edge(5L,7L,"pi")))
val defaultUser = ("John Doe","Missing")
val graph = Graph(users,relationships,defaultUser)
基于Graph,可以进行各种操作,例如属性操作,结构操作,关联操作等。可以如下过滤边:
graph.edges.filter( e => e.srcId > e.dstId).count