第一,MapReduce 模型的抽象层次低,大量的底层逻辑都需要开发者手工完成
第二,只提供 Map 和 Reduce 两个操作。
第三,在 Hadoop 中,每一个 Job 的计算结果都会存储在HDFS中,所以每一步计算都要进行硬盘的读取和写入,大大增加了系统的延迟。
第四,只支持批数据处理,欠缺对流数据处理的支持。
RDD 有以下基本特性:分区、不可变和并行操作。
分区
分区代表同一个 RDD 包含的数据被存储在系统的不同节点中,这也是它可以被并行处理的前提。
逻辑上,我们可以认为 RDD 是一个大的数组。每个元素代表一个分区(Partition)。
在物理存储中,每个分区指向一个存放在内存或者硬盘中的数据块(Block),而这些数据块是独立的,它们可以被存放在系统中的不同节点。
不可变性
不可变性代表每一个 RDD 都是只读的,它所包含的分区信息不可以被改变。既然已有的 RDD 不可以被改变,我们只可以对现有的 RDD 进行转换(Transformation)操作,得到新的 RDD 作为中间计算的结果。
并行操作
由于单个 RDD 的分区特性,使得它天然支持并行操作,即不同节点上的数据可以被分别处理,然后产生一个新的 RDD。
RDD的结构
SparkContext
是所有 Spark 功能的入口,它代表了与 Spark 节点的连接,可以用来创建 RDD 对象以及 在节点中的广播变量等。一个线程只有一个 SparkContext。
SparkConf
一些参数配置信息。
Partitions
RDD 中数据的逻辑结构,每个 Partition 会映射到某个节点内存或硬盘的一个数据块。
Partitioner
Partitioner 决定了 RDD 的分区方式,目前有两种主流的分区方式:Hash partitioner 和 Range partitioner。Hash,顾名思义就是对数据的 Key 进行散列分区,Range 则是按照 Key 的排序进行均匀分区。此外我们还可以创建自定义的 Partitioner。
依赖关系
每一步产生的 RDD 里都会存储它的依赖关系,即它是通过哪个RDD 经过哪个转换操作得到的。
checkpoint
如果一个 RDD 的依赖链比较长,而且中间又有多个 RDD 出现故障的话,进行恢复可能会非常耗费时间和计算资源。
在计算过程中,对于一些计算过程比较耗时的 RDD,我们可以将它缓存至硬盘或 HDFS 中,标记这个 RDD有被检查点处理过,并且清空它的所有依赖关系。
CheckpointRDD 可以用来从硬盘中读取 RDD 和生成新的分区信息。
这样,当某个子 RDD 需要错误恢复时,回溯至该 RDD,发现它被检查点记录过,就可以直接去硬盘中读取这个 RDD,而无需再向前回溯计算。
RDD的动作和操作
转换是生成新的 RDD,动作是把 RDD 进行计算生成一个结果。
所有转换操作都很懒,它只是生成新的 RDD,并且记录依赖关系。但是 Spark 并不会立刻计算出新 RDD 中各个分区的数值。直到遇到一个动作时,数据才会被计算,并且输出结果给 Driver。
RDD的持久化缓存
每当我们对 RDD 调用一个新的 action 操作时,整个RDD 都会从头开始运算。因此,如果某个 RDD 会被反复重用的话,每次都从头计算非常低效,我们应该对多次使用的 RDD 进行一个持久化操作。
Spark 的 persist() 和 cache() 方法支持将 RDD 的数据缓存至内存或硬盘中,这样当下次对同一 RDD 进行 Action 操作时,可以直接读取 RDD 的结果,大幅提高了 Spark 的计算效率。
对 RDD 进行持久化操作和记录 Checkpoint区别
区别在于Checkpoint会清空该RDD的依赖关系,并新建一个CheckpointRDD依赖关系,让该RDD依赖,并保存在磁盘或HDFS文件系统中,当数据恢复时,可通过CheckpointRDD读取RDD进行数据计算;持久化RDD会保存依赖关系和计算结果至内存中,可用于后续计算。
为了方便对hadoop的操作,Hive应运而生,Spark团队基于Hive修改了其内存模块,使其效率提升10-100倍,称为Shark。
Shark 对于 Hive 的依赖严重影响了 Spark 的发展,Spark 想要定义的是一个统一的技术栈和完整的生态,不可能允许有这样的外在依赖。
所以,2014 年 7 月 1 日,Spark 团队就将 Shark交给了Hive进行管理,转而开发了Spark SQL。
Spark SQL架构
Spark SQL 本质上是一个库,它运行在 Spark 的核心执行引擎之上。
DataFrame 和 DataSet 是 Spark SQQL 提供的基于RDD 的结构化数据抽象。
它既有 RDD 不可变、分区、存储依赖关系等特性,又拥有类似于关系型数据库的结构化信息。所以,基于 DataFrame 和 DataSet API 开发出的程序会被自动优化,使得开发人员不需要操作底层的 RDD API 来进行手动优化,大大提升开发效率。
但是 RDD API 对于非结构化的数据处理有独特的优势,比如文本流数据,而且更方便我们做底层的操作。所以在开发中,我们还是需要根据实际情况来选择使用哪种 API。
Spark Streaming 提供一个对于流数据的抽象 DStream。DStream 可以由来自 Apache Ka
fka、Flume 或者 HDFS 的流数据生成,也可以由别的 DStream 经过各种转换操作得来。
底层 DStream 也是由很多个序列化的 RDD 构成,按时间片(比如一秒)切分成的每个数据单位都是一个 RDD。然后,Spark 核心引擎将对 DStream 的 TranDStream 的 Transformation 操作变为针对 Spark 中对 RDD 的 Transformation 操作,将 RDD 经过操作中间结果保存在内存中。
DStream
下图就是 DStream 的内部形式,即一个连续的 RDD序列,每一个 RDD 代表一个时间窗口的输入数据流。
对 DStream 的转换操作,意味着对它包含的每一个 RDD 进行同样的转换操作。
滑动窗口操作
窗口长度(window length):每次统计的数据的时间跨度
滑动间隔(sliding interval):每次统计的时间间隔
优缺点
Spark Streaming 的优点很明显,由于它的底层是基于 RDD 实现的,所以 RDD 的优良特性在它这里都有体现。
而且,Spark Streaming 是 Spark 生态的的一部分。所以,它可以和 Spark 的核心引擎、Spark SQL、MLlib 等无缝衔接。换句话说,对实时处理出来的中间数据,我们可以立即在程序中无缝进行批处理、交互式查询等操作。这个特点大大增强了 Spark Streaming 的优势和功能,使得基于 Spark Streaming 的应用程序很容易扩展。
而 Spark Streaming 的主要缺点是实时计算延迟是实时计算延迟较高,一般在秒的级别。这是由于 Spark Streaming 不支持太小的批处理的时间间隔。
Structured Streaming 是基于 Spark SQL 引擎实现的,依靠 Structured Streaming,在开发者眼里,流数据和静态数据没有区别。我们完全可以像批处理静态数据那样去处理流数据。随着流数据的持续输入,Spark SQL 引擎会帮助我们持续地处理新数据,并且更新计算结果。
与 Spark Streaming 类似,Structured Streaming 也是将输入的数据流按照时间间隔(以一秒为例)划分成数据段。每一秒都会把新输入的数据添加到表中,Spark 也会每秒更新输出结果。输出结果也是表的形式,输出表可以写入硬盘或者 HDFS。
Structured Streaming的三种输出模式
1.完全模式(Complete Mode):整个更新过的输出表都被写入外部存储;
2.附加模式(Append Mode):上一次触发之后新增加的行才会被写入外部存储。如果老数据有改动则不适合这个模式;
3.如果老数据有改动则不适合这个模式;
需要注意的是,Structured Streaming 并不会完全存储输入数据。每个时间间隔它都会读取最新的输入,进行处理,更新输出表,然后把这次的输入删除。Structured Streaming 只会存储更新输出表所需要的信息。
Structured Streaming 与 Spark Streaming 对比
简易度和性能
Spark Streaming 提供的 DStream API 与 RDD API 很类似,相对比较低 level。
当我们编写 Spark Streaming 程序的时候,本质上就是要去构造 RDD 的 DAG 执行图,然后通过 Spark Engine 运行。这样开发者身上的担子就很重,很多时候要自己想办法去提高程序的处理效率。这不是 Spark 作为一个数据处理框架想看到的。对于好的框架来说,开发者只需要专注在业务逻辑上,而不用操心别操心别的配置、优化等繁杂事项。
Structured Streaming 提供的 DataFrame API 就是这么一个相对高 level 的 API,大部分开发者都很熟悉关系型数据库和SQL。这样的数据抽象可以让他们用一套统一的方案去处理批处理和流处理,不用去关心具体的执行细节。
而且,DataFrame API 是在 Spark SQL 的引擎上执行的,Spark SQL 有非常多的优化功能,执行计划优化和内存管理等,所以 Structured Streaming 的应用程序性能很好。
实时性
在上一讲中我们了解到,Spark Streaming 准实时的,它能做到的最小延迟在一秒左右。
虽然 Structured Streaming 用的也是类似微批处理思想,每过一个时间间隔就去拿来最新的数据加入到输入数据表中并更新结果,但是相比起 Spark Streaming 来说,它更像是实时处理,能做到用更小的时间间隔,最小延迟在 100 毫秒左右。
而且在 Spark 2.3 版本中,Structured Streaming 引入了连续处理的模式,可以做到真正的毫秒级延迟,这无疑大大拓展了 Structured Streaming的应用广度。不过现在连续处理模式还有很多限制。
对事件时间的支持
Structured Streaming 对基于事件时间的处理有很好的支持。
由于 Spark Streaming 是把数据按接收到的时间切分成一个个 RDD 来进行批处理,所以它很难基于数据本身的产生时间来进行处理。如果某个数据的处理时间和事件时间不一致的话,就容易出问题。比如,统计每秒的词语数量,有的数据先产生,但是在下一个时间间隔才被处理,这样几乎不可能输出正确的结果。
Structured Streaming 还有很多其他优点。比如,它有更好的容错性,保证了端到端 exactly once 的语义等等。所以综合来说,Structured Streaming 是比 Spark Streaming更好的流处理工具。