技术背景
- Spark Streaming会接收实时数据源的数据,并切分成很多小的batches,然后被Spark Engine执行,产出同样由很多小的batchs组成的结果流。本质上,这是一种micro-batch(微批处理)的方式处理,用批的思想去处理流数据.这种设计让Spark Streaming面对复杂的流式处理场景时捉襟见肘。
- spark streaming这种构建在微批处理上的流计算引擎,比较突出的问题就是处理延时较高(无法优化到秒以下的数量级),以及无法支持基于event_time的时间窗口做聚合逻辑。
- 在这段时间,流式计算一直没有一套标准化、能应对各种场景的模型,直到2015年google发表了The Dataflow Model的论文
设计目的
解决真实时计算场景,基于事件计算
设计思想
对无边界,无序的数据源,允许按数据本身的特征进行窗口计算,得到基于事件发生时间的有序结果,并能在准确性、延迟程度和处理成本之间调整。
将实时到达的数据看作是一个不断追加的unbound table无界表,到达流的每个数据项(RDD)就像是表中的一个新行被附加到无边界的表中
技术本质
一个流的数据源从逻辑上来说就是一个不断增长的动态表格,随着时间的推移,新数据被持续不断地添加到表格的末尾。
核心特性
- 简洁的模型;用户可以直接把一个流看做是一个无限增长的表格
- 一致的API; Structured Stream 和SparkSql共用大部分API,
- 卓越的性能; Structured Stream也直接使用了SparkSQL的Catalyst优化器和Tungsten,数据处理性能十分出色
- 多语言支持; scala,java,python,R,sql
DataFlow模型
- 背景:
作为数据工作者,不能把无边界数据集(数据流)切分成有边界的数据,等待一个批次完整后处理。相反地,应该假设永远无法知道数据流是否终结,何时数据会变完整。唯一确信的是,新的数据会源源不断而来,老的数据可能会被撤销或更新。 - 概念:
一种基于事件的流式处理数据的思想 - 思想:
对无边界,无序的数据源,允许按数据本身的特征进行窗口计算,得到基于事件发生时间的有序结果,并能在准确性、延迟程度和处理成本之间调整。 - 内容:
- 基于事件时间的计算: 真实时计算,产生一条处理一条,flink,storm
- Event_time: 事件时间,一般在数据产生时记录
- Process_time: 处理时间,事件开始处理的时间
- ingest_time: 到达时间
- 基于窗口的计算: 准实时计算,微批处理,spark stream
- fixed window: 固定窗口,窗口间隔等于窗口长度
- sliding window: 滑动窗口,窗口间隔不等于窗口长度,容易造成数据丢失或数据重复
- sessions: 以某一事件作为窗口起始,通常以时间定义窗口大小(也有可能是事件次数),发生在超时时间以内的事件都属于同一会话
- 基于事件时间的计算: 真实时计算,产生一条处理一条,flink,storm
Structured Stream
概念:
Structured Streaming是一个基于Spark SQL引擎的可扩展、容错的流处理引擎。统一了流、批的编程模型,你可以使用静态数据批处理一样的方式来编写流式计算操作。并且支持基于event_time的时间窗口的处理逻辑。-
内容:
- 随着数据不断地到达,Spark 引擎会以一种增量的方式来执行这些操作,并且持续更新结算结果。
- Structured Streaming会通过checkpoint和预写日志等机制来实现Exactly-Once语义。
- 结构化流式查询使用微批处理引擎进行处理,该引擎将数据流作为一系列小批处理作业进行处理,从而实现端到端的延迟,最短可达100毫秒,并且完全可以保证一次容错。自Spark 2.3以来,引入了一种新的低延迟处理模式,称为连续处理,它可以在至少一次保证的情况下实现低至1毫秒的端到端延迟。也就是类似于 Flink 那样的实时流,而不是小批量处理。实际开发可以根据应用程序要求选择处理模式,但是连续处理在使用的时候仍然有很多限制,目前大部分情况还是应该采用小批量模式。
-
对比:
- Spark Streaming 采用的数据抽象是DStream,而本质上就是时间上连续的RDD,对数据流的操作就是针对RDD的操作
- Structured Streaming是Spark2.0新增的可扩展和高容错性的实时计算框架,它构建于Spark SQL引擎,把流式计算也统一到DataFrame/Dataset里去了。Structured Streaming 相比于 Spark Streaming 的进步就类似于 Dataset 相比于 RDD 的进步,最主要的一个原因就是希望用户不再需要分别为批处理和流处理编写不同代码,而是直接使用同一套代码。不过需要注意的是尽管在2.2.0以后 Structured Streaming 被标注为稳定版本,生产环境中可以使用了,但是,相对来说,Structured Streaming还处于比较初级的阶段,很多功能与dataflow相比还是有差距
数据源
- Socket source (for testing):
从socket连接中读取文本内容。 - File source:
以数据流的方式读取一个目录中的文件。支持text、csv、json、parquet等文件类型。 - Kafka source:
从Kafka中拉取数据,与0.10或以上的版本兼容,后面单独整合Kafka
输出方式
- output mode:以哪种方式将result table的数据写入sink
- append mode;默认方式,新增的行才输出,每次更新结果集时,只将新添加到结果集的结果行输出到接收器,不支持聚合
- complete mode;所有内容都输出,每次触发后,整个结果表将输出到接收器。聚合查询支持此功能。仅适用于包含聚合操作的查询。
- update mode;更新的行才输出,每次更新结果集时,仅将被更新的结果行输出到接收器(自Spark 2.1.1起可用),不支持排序
- format/output sink的一些细节:数据格式、位置等。
- file sink
- kafka sink
- foreach sink
- foreachbatch sink
- console sink
- memory sink
- query name:指定查询的标识。类似tempview的名字
- trigger interval:触发间隔,如果不指定,默认会尽可能快速地处理数据
- checkpoint地址:一般是hdfs上的目录。注意:Socket不支持数据恢复,如果设置了,第二次启动会报错 ,Kafka支持
流处理模式
有状态
- 概念:
当前批次计算的结果与之前批次的计算结果有关系,需要进行聚合,聚合的结果作为这个批次最后的结果
无状态
- 概念:
每个计算批次之间是没有关系,只对当前自己这个批次的数据做计算,这个批次的结果就是最后输出的结果
窗口模式
- 概念:
基于时间窗口的计算,按照数据产生的时间event_time来计算这个数据的结果,而不是按照数据到达的时间来表示数据的结果 - 问题:
基于时间事件event_time的窗口计算存在一个问题,当遇到网络延迟时,已经过了时间窗口,还需不需要计算 - 水印 water mark
- 概念:窗口中允许最大的事件时间-用户定义的超时时间(如10min)=当前的水位线
- 场景:数据延迟到达太久,考虑是否需要计算的问题
- 内容: 高于水印就计算,低于水印就不计算
容错语义
- 概念:
Structured Streaming的核心设计理念和目标之一,就是支持一次且仅一次Extracly-Once的语义 - 内容:
- 每个streaming source都被设计成支持offset,进而可以让spark来追踪读取的位置。
- spark基于checkpoint和wal来持久化保存每个trigger interval内处理的offset的范围
- sink被设计成可以支持在多次计算处理时保持幂等性,就是说,用同样的一批数据,无论多少次去更新sink,都会保持一致和相同的状态
- 总结:
综合利用基于offset的source,基于checkpoint和wal的execution engine,以及基于幂等性的sink,可以支持完整的一次且仅一次的语义。