为什么需要Spark
MapReduce的缺点
1.抽象层次太低,大量底层逻辑需要开发者手工完成
2.只有map和reduce两个操作
3.每一个job的计算结果都会存储在HDFS中,所以每一步计算成本很高
4.只支持批处理,却反对流数据处理的支持
Spark支持各种丰富的操作,而且速度远超MapReduce
下图是Spark和Hadoop上运行逻辑回归算法的运行时间对比
在任务(task)级别上,Spark是多线程模型,MapReduce是多进程模型
Spark并不是完全替代Hadoop,因为Hadoop还包含了很多组件:
数据存储层:分布式文件存储系统HDFS,分布式数据库存储HBase
数据处理层:进行数据处理的MapReduce,负责集群和资源管理的YARN
数据访问层:Hive,Pig,Mahout
Spark生态圈如下
Spark有5个主要扩展库
1.支持结构化数据的Spark SQL
2.处理实时数据的Spark Streaming
3.用于机器学习的MLlib
4.用于图计算的GraphX
5.用于统计分析的SparkR
RDD
弹性分布式数据集Reilient Distributed Dataset
RDD是逻辑上的一个大数组,数组中的每个元素代表一个分区Partition,可以被存储在内存或者磁盘中
这些分区是独立的,可以被放在不同节点上
RDD中的每个分区存有在该RDD中的index,通过RDD的ID和分区的index可以唯一确定对应的数据块的编号,从而通过底层存储层的接口提取到数据进行处理
RDD具有不可变性,只能对现有的RDD进行转换Transformation操作,得到新的RDD
RDD的分区和不可变性使得它支持并行操作
RDD的结构
- SparkConf提供了一些参数配置信息
- SparkContext是所有Spark功能的入口,它代表了与Spark节点的连接,可以用来创建RDD对象以及在节点中的广播变量等
- Partitions代表RDD中的数据逻辑结构,每个Partition会映射到某个节点内存或者硬盘的一个数据块,目前两个主流的分区方式Hahs分区和Range分区
- Dependencis是依赖关系,包括宽依赖 或者窄依赖
- Checkpoint,跟关系型数据库类似,某个RDD的依赖关系复杂计算时间长,加入检查点后,就删除之前的依赖关系加速恢复过程
- 存储级别Storage Level用来记录RDD持久化的存储级别
- 迭代函数Iterator和计算函数Compute是用来标示RDD怎样通过父RDD计算得到的
- 迭代函数首先会判断缓存中是否有要计算的RDD,如果有就直接读取,没有就查找想要计算的RDD是否被检查点处理过,如果有就读取没有就调用计算函数向上递归查找父RDD进行计算
RDD的转换操作
操作分为两种,转换Transformation ,动作Action
转换是把一个RDD转换成另一个RDD,动作是通过计算返回一个结果
转换的操作包括:
map,filter,mapPartitions,groupByKey
动作的操作包括:
collect,reduce,count,countByKey
RDD的持久化
Spark的persist(),cache()函数支持将RDD的数据缓存到内存或者磁盘上
当下一次对同一个RDD进行Action操作时,可以直接读取RDD的结果
存储级别
MEMORY_ONLY:只缓存在内存中,如果内存空间不够则不缓存多出来的部分,这是RDD的默认存储级别
MEMORY_AND_DISK:存储在内存中,如果空间不够则缓存在硬盘中
DISK_ONLY:只缓存在硬盘中
MEMORY_ONLY_2和MEMORY_AND_DISK_2,和上面的级别功能相同,只不过每个分区再集群中的两个几点上建立副本
依赖关系
包括宽依赖Wide Dependency,窄依赖 Narrow Dependency
窄依赖是父RDD的分区可以一一对应到子RDD的分区
宽依赖是父RDD的每个分区可以被多个子RDD的分区使用
窄依赖允许子RDD的每个分区可以被并行处理产生,而宽依赖必须等待父RDD的所有分区都被计算好之后才能开始处理
map,filter会产生窄依赖关系
join,groupBy会产生宽依赖关系
Spark之所以有宽窄依赖是出于以下两点考虑
- 窄依赖可以支持在同一个节点上链式执行多条命令,如在执行了map后,紧接着执行filter,反之宽依赖需要所有父分区都是可用的,可能还需要调用类似MapReduce之类的操作进行跨节点传递
- 从失败恢复的角度考虑,窄依赖的失败恢复更有效,因为它只需要重新计算丢失的父分区即可,而宽依赖牵扯到RDD各级的多个分区
Spark SQL
最早是在Hive基础之上搞了一个Shark,用来支持SQL查询
本质就是修改Hive做了优化,速度是Hive的10-100倍,但因为依赖Hive,后来Spark团队就把这个项目交给Hive管理了
之后开始开发SparkSQL
SparkSQL摒弃了Shark的引擎,换成自己团队的引擎
支持Hive,RDD,JSON文件,CSV文件等,也加快了Spark生态圈的发展
SparkSQL的架构
提供了SQL查询操作,允许数据仓库应用程序直接获取数据,允许使用者通过命令行操作来交互查询数据
提供了两个API,DataFrame API,DataSet API
上层应用通过这两个API来操作RDD,当然应用程序也可以直接操作RDD
使用SparkSQL会让开发者好像是在操作关系型数据库一样,而不是在操作RDD
Spark底层对SQL进行优化
DataSet
也就是数据集的意思,是1.6引入的新接口
和RDD类似,也是不可变的,支持map,filter等操作,也是惰性计算的
DataSet锁描述的数据都被组织到有名字的列中,就像关系型数据库的表一样
SparkSQL可以清楚的知道数据集中包含哪些列,也可以依靠优化器把需要的列取出来
DataFrame
可以看做是一种特殊的DataSet,它的每一行是固定的Row,可以被当做DataSet[Row]来处理,必须要通过解析才能获取各个列的值
对于DataSet可以用类似people.name来访问一个人的名字,对于DataFrame用类似people.get AS[String]("name")来访问
RDD,DataFrame,DataSet对比
从发展历史看,RDD API在第一代Spark中就存在,是整个Spark框架的基石
为了方便熟悉关系型数据库和SQL开发人员使用,在RDD基础上,Spark创建了DataFrame API,方便对列进行操作
DataSet在DataFrame基础上添加了对数据的每一列类型的限制
DataSet和DataFrame都是不变的,可以通过简单的API让他们在RDD之间无缝切换
优化器会对DataSet,DataFrame进行优化,比RDD性能更好
RDD API对非结构化的数据处理有独特的优势,比如文件流数据,而且更方便我们做底层的操作,在开发中要根据实际情况选择具体API
RDD和DataSet都是类型安全的,DataFrame不是,因为他不存储每一列的信息
Spark Straming
无论是DataSet,还是DataFrame都是基于批处理模式对静态数据进行处理的,比如某个特定时间对一天的日志进行处理
Spark的流处理组件Streaming可以对流数据进行处理
流处理的核心思想跟微积分类似,把一个矩阵分成无限细分的小矩形
Spark Straming提供了一个对于流数据的抽象DataStream,DataStream可以由来自Kafka,Flume,HDFS的流数据生成,也可以由别的DSStream经过各种转换操作得来
底层DStream也是由很多个序列化的RDD构成,按照时间片比如一秒,切分成的每个数据单位都是一个RDD,然后Spark核心引擎将对DStream的转换操作变为针对Spark中对RDD的转换操作,将RDD经过操作变成中间结果保存在内存中
无论是DataFrame,DStream都具有RDD不变形,分区性,容错性等特质
Spark是一个高度统一的平台,所有的高级API都有相同的性质,它们之间可以很容易的相互转换,Spark的野心就是用一这套工具统一所有数据处理的场景
Spark Streaming将底层细节都封装起来了,对于开发者只需要操作DStream就可以了
DStream
下图是DStream的内部形式,即一个连续的RDD序列,每一个RDD代表一个时间窗口的输入数据流
对DStream的转换操作,意味着对它包含的每一个RDD进行同样的转换操作
sc = SparkContext(master, appName)
ssc = StreamingContext(sc, 1)
lines = sc.socketTextStream("localhost", 9999)
words = lines.flatMap(lambda line: line.split(" "))
如下图所示
本质上对一个DStream进行flatMap操作,就是对它里面的每个RDD进行flatMap操作,生成一系列新的RDD,构成了一个新的代表赐予的DStream
滑动窗口操作
任何Spark Streaming的程序都要先创建一个StreamingContext的对象,它是所有Streaming操作的入口
比如,可以通过StreamingContext来创建DStream,它最终的参数是批处理的时间间隔,即把流数据细分成数据块的粒度
这个时间间隔决定了流处理的延迟性,上面的例子中每一秒会生成一个RDD进行运算
有些场景汇总,我们需要每隔一段时间,去统计过去某个时间段内的数据,比如对热点搜索词语进行统计
每隔10秒钟输出过去60秒内排名前10的热点词,这是流处理的一个基本应用场景,很多流处理框架如Flink都有原生的支持
Spark也支持滑动窗口操作
从热点词看,滑动窗口有两个基本操作
- 窗口长度,每次统计的数据的时间跨度,如60秒
- 滑动时间,每次统计的时间间隔,如10秒
Spark Straming的优缺点
它是基于RDD实现的,所有RDD的优良特点Spark Streaming都支持,比如容错性等
运行速度,也支持persist()优化
它是Spark生态的一部分,可以和Spark的核心引起,SparkSQL,MLlib等无缝衔接,对实时处理出来的中间数据,可以立即在程序中无缝进行批处理,交互查询等操作,使得基于Spark Streaming的应用程序很容易扩展
缺点是实时计算延迟较高,一般在秒级,这是由于Spark Streaming不支持爱笑的批处理的时间间隔
而Storm就可以做到毫秒级
Structured Streaming
DataFream是高级API,可以用SQL的方式去开发,SparkSQL执行引起会自动优化DataFream程序
Structured Streaming模型
流处理最基本的问题就是如何对不断更新的无边界数据建模
Spark Streaming就是把流数据按一定的时间间隔分割成许多个小的数据块进行批处理,在Structured模型中,要把数据看成一个无边界的关系型数据表,每一个数据都是表中的一行,不断会有新的数据行被添加到表里来,我们可以对这个表做任何类似批处理的查询,Spark会帮我们不断对新的数据进行处理,并更新计算结果
参考