3. Stuctured Streaming 的体系和结构
目标
了解 Structured Streaming 的体系结构和核心原理, 有两点好处, 一是需要了解原理才好进行性能调优, 二是了解原理后, 才能理解代码执行流程, 从而更好的记忆, 也做到知其然更知其所以然步骤
WordCount 的执行原理
Structured Streaming 的体系结构
3.1 无限扩展的表格
目标
Structured Streaming 是一个复杂的体系, 由很多组件组成, 这些组件之间也会进行交互, 如果无法站在整体视角去观察这些组件之间的关系, 也无法理解 Structured Streaming 的全局步骤
了解 Dataset 这个计算模型和流式计算的关系
如何使用 Dataset 处理流式数据?
WordCount 案例的执行过程和原理
3.1.1 Dataset
和流式计算
可以理解为 Spark
中的 Dataset
有两种, 一种是处理静态批量数据的 Dataset
, 一种是处理动态实时流的 Dataset
, 这两种 Dataset
之间的区别如下
-
流式的
Dataset
使用readStream
读取外部数据源创建, 使用writeStream
写入外部存储 -
批式的
Dataset
使用read
读取外部数据源创建, 使用write
写入外部存储
3.1.2 如何使用 Dataset
这个编程模型表示流式计算?
-
可以把流式的数据想象成一个不断增长, 无限无界的表
-
无论是否有界, 全都使用
Dataset
这一套API
-
通过这样的做法, 就能完全保证流和批的处理使用完全相同的代码, 减少这两种处理方式的差异
3.1.3 WordCount
的原理
-
整个计算过程大致上分为如下三个部分
-
Source
, 读取数据源 -
Query
, 在流式数据上的查询 -
Result
, 结果集生成
-
-
整个的过程如下
-
随着时间段的流动, 对外部数据进行批次的划分
-
在逻辑上, 将缓存所有的数据, 生成一张无限扩展的表, 在这张表上进行查询
-
根据要生成的结果类型, 来选择是否生成基于整个数据集的结果
-
3.1.4 总结
-
Dataset
不仅可以表达流式数据的处理, 也可以表达批量数据的处理 -
Dataset
之所以可以表达流式数据的处理, 因为Dataset
可以模拟一张无限扩展的表, 外部的数据会不断的流入到其中
3.2 体系结构
目标
Structured Streaming
是一个复杂的体系, 由很多组件组成, 这些组件之间也会进行交互, 如果无法站在整体视角去观察这些组件之间的关系, 也无法理解Structured Streaming
的核心原理步骤
体系结构
StreamExecution
的执行顺序
3.2.1 体系结构
在 Structured Streaming
中负责整体流程和执行的驱动引擎叫做 StreamExecution
StreamExecution
在流上进行基于 Dataset
的查询, 也就是说, Dataset
之所以能够在流上进行查询, 是因为 StreamExecution
的调度和管理
StreamExecution
如何工作?
StreamExecution
分为三个重要的部分
-
Source
, 从外部数据源读取数据 -
LogicalPlan
, 逻辑计划, 在流上的查询计划 -
Sink
, 对接外部系统, 写入结果
3.2.2 StreamExecution
的执行顺序
根据进度标记, 从 Source
获取到一个由 DataFrame
表示的批次, 这个 DataFrame
表示数据的源头
val source = spark.readStream
.format("socket")
.option("host", "127.0.0.1")
.option("port", 9999)
.load()
.as[String]
这一点非常类似 val df = spark.read.csv()
所生成的 DataFrame
, 同样都是表示源头
根据源头 DataFrame
生成逻辑计划
val words = source.flatMap(_.split(" "))
.map((_, 1))
.groupByKey(_._1)
.count()
上述代码表示的就是数据的查询, 这一个步骤将这样的查询步骤生成为逻辑执行计划
优化逻辑计划最终生成物理计划
这一步其实就是使用 Catalyst
对执行计划进行优化, 经历基于规则的优化和基于成本模型的优化
执行物理计划将表示执行结果的 DataFrame / Dataset
交给 Sink
整个物理执行计划会针对每一个批次的数据进行处理, 处理后每一个批次都会生成一个表示结果的 Dataset
Sink
可以将每一个批次的结果 Dataset
落地到外部数据源
执行完毕后, 汇报 Source
这个批次已经处理结束, Source
提交并记录最新的进度
3.2.3 增量查询
核心问题
上图中清晰的展示了最终的结果生成是全局的结果, 而不是一个批次的结果, 但是从 StreamExecution
中可以看到, 针对流的处理是按照一个批次一个批次来处理的
那么, 最终是如何生成全局的结果集呢?
状态记录
在 Structured Streaming
中有一个全局范围的高可用 StateStore
, 这个时候针对增量的查询变为如下步骤
-
从
StateStore
中取出上次执行完成后的状态 -
把上次执行的结果加入本批次, 再进行计算, 得出全局结果
-
将当前批次的结果放入
StateStore
中, 留待下次使用
3.3 总结
-
StreamExecution
是整个Structured Streaming
的核心, 负责在流上的查询 -
StreamExecution
中三个重要的组成部分, 分别是Source
负责读取每个批量的数据,Sink
负责将结果写入外部数据源,Logical Plan
负责针对每个小批量生成执行计划 -
StreamExecution
中使用StateStore
来进行状态的维护
3.4 我的笔记
1.RDD、DateFrame、DataSet的优缺点
2.一个Dataset在逻辑上表示什么?
就是一个表
怎么让它处理流式数据呢?
让表无线的网下延展,动态扩展的
(使用read创建的就是一个不能扩展的表格,使用readStream创建的就是一个可以无限扩展的表格)
StructuredStreaming的编程模型:
Source :读取数据源
↓
Query :再流式数据上查询
↓
Sink :结果集生成
我的理解:增量处理,计算出结果,再和原来的合并
3.无限扩展的表,这个模型很好,但是存在问题
DataFrame时间长了,会太大
不应该一直追加数据,而是处理增量的数据
(每一个批次处理完生成的结果,我们称为状态;
每个批次处理完保存下当前的最新的状态;
每个批次处理之前,拉出最新的状态,合并到处理过程中,一起处理,然后生成新结果)
这里的状态通过StateStore来实现
异常记录:
Exception in thread "main" org.apache.spark.sql.AnalysisException: Complete output mode not supported when there are no streaming aggregations on streaming DataFrames/Datasets;;
原因:使用OutputMode.Complete() 其操作必须要是聚合的操作 ,可以修改为:OutputMode.Append()