Structured Streaming 编程指南

鉴于有些词没有很好的中文对应,保留英文原词 比如 exactly-once

概览

--快速实例

--编程模型

----基本概念

----处理即时(event_time)和迟到数据

----容错语法

使用DataSets(暂时翻译为数据集,简写ds) 和 DataFrame(暂时翻译数据帧,简写df)

--创建数据帧和数据集

----输入源

----表概要推断和流式数据集和数据帧的分区


概览

   结构化数据流(structed streaming,后续简写为ss)是一个基于spark sql引擎的 ,可变的,容错的流式处理引擎。你可以用处理静态数据批次计算的方式处理流式数据。spark SQL引擎 将会逐渐和持续的运行struced streaming,并且随着数据持续到达更新最后的结果。你可以使用数据集或者数据帧提供的接口(scala,java,python 或者R 都可以),实现流式的聚合,即时窗口,流式到批式的聚合等。计算的流程在相同的Spark SQL引擎上执行。最终系统通过checkpointing 和WAL(write ahead log ,预写日志)确保end-to end,exactly-once,容错。

快速示例

比方说,你现在需要 统计单词数量,这些单词是从一个监听tcp socker的服务上输入的。让我们来看一下你应该使用ss如何实现。这块比较简单先不翻译了。

编程模型

ss关键的思路是像处理一个表的数据一样处理实时的数据流。然而这个表的数据是持续增加的。这就导致了一个新的处理模型,非常类似于目前的批处理模型。你可以像在静态表的批量查询一样,实现你的流式计算就。并且spark 运行这种查询是在在一个无限的输入表中以一种递增的方式查询。让我们一起更深入的理解这个模型。

基础概念

把输入的数据流当做 Input Table(输入表) ,到达数据流的每个数据项就像是一个新增到这个input table 新的一行。


Structured Streaming 编程指南_第1张图片

对于输入的查询将会产生一个 Result Table (结果表)。每隔触发器时间(比方说,每隔1s),新的行会被追加到输入表中,这些新的行最终会更新结果表。无论任何时候结果表被更新,我们需要把那些变化的结果行写到一个外部的存储。


Structured Streaming 编程指南_第2张图片

输出 被定义为被写入到外部存储的内容。输出有多种不同的模式。

complete mode-全部更新的结果表将会被写入到外部存储。这个取决于存储的连接决定如何处理全部表的写入。

append mode-只有从上一次触发后结果表中新增的行才会被写入到外部存储。适用于结果表中的数据不会改变的查询场景。

update mode-只有从上一次触发后结果表中被更新的行才会写入到外部存储(从2.1.1开始支持)。需要注意的是,这种模式不同于complete mode的地方是,这种模式只会输出从上次触发后改变的行。如果查询不包含聚合操作,则等价于append mode。

注意:这些模式适用于一些特征的查询场景。后续我们会讨论。

为了演示一下这个模式的使用,让我们一起理解一下上文中的快速示例。第一个lines 的dataFrame 就是输入表,最后的wordCounts 的dataFrame 就是结果表。需要指出的是,从流式lines DataFrame到 wordCounts 的DataFrame 的查询将会和静态DataFrame的一样。然而,当查询启动的时候,Spark 将会持续的检查来自socket连接的新的数据。如果有新的数据,spark 将会运行一个"递增"的查询,这个查询将会把以前的运行中的统计值和新到的数据进行一个计算,并更新统计值,就像下面演示的一样。


Structured Streaming 编程指南_第3张图片

这个模型明显与其他的流式处理引擎不同。许多流式系统需要用户自己保持一个运行时的聚合,因而必须考虑容错,和数据的一致性(至少一次 at-least-once,至多一次 at-most-once,绝对一次 exactl-once)。在这个模型中,spark 负责当新数据到达时,更新结果表,因而用户不需要在考虑这些问题了。举个例子,我们来看看 这个模型是如何处理即刻数据以及迟到数据的。

处理即刻和迟到数据

event-time 就是数据本身的时间。对很多应用来说,你想要在event-time这个层面进行操作。举个例子,如果你想要获取每分钟lot设备产生的事件数量,你可能需要使用的是数据产生的时间(数据中的即刻时间,好拗口),而不是spark 收到这些数据的时间。这个即刻时间在这个模型中天生支持-每个来自设备的事件是表中的一行,而event-time 是这行数据的一列。这样就使基于窗口的聚合成为可能,由于把时间这个列作为一种特殊的grouping 和聚合。每个时间窗口就是一个group,并且每行数据都可以属于多个组(或窗口)。因而,event-time-window-based 的聚合的查询可以定义为静态的数据集也可以是数据流,使得用户的生活更简单(原文就这么说的)。

另外 这个模型天生处理那些相比即刻数据迟到的数据。由于Spark 一直会更新结果表,它可以在有数据迟到时完全的控制更新老的聚合结果,并且为了限制中间状态数据的大小,可以清除老的聚合数据。从spark 2.1 开始,我们支持wartermarking (水印),水印用于标识 迟到数据的一个门限值,并且允许引擎以此来清除老状态的数据。 在窗口操作的章节将会有更详尽的介绍。

容错语义

传输end-to-end exactly-once 语义是structed streaming设计的一个关键目标。为了达到这个目标,我们设计了structed Streaming 输入源,输出和执行引擎用来可靠的追踪准确的处理。因而它可以通过重启或者重新运行来处理任何失败。每个streaming的输入源 都被假定有一个偏移量(类似kafka的偏移量,或者kinesis的序列号)用于追踪。

API 使用DataSets 和 DataFrames

从spark 2.0,DataFrames 和Datasets 可以代表静态的,有限的数据,也可以是流式的无限的数据。和静态的df,ds 类似的,你可以使用通用的入口 SparkSession从流式的输入源中 去创建流式的df和ds,并且像在使用静态的ds,df一样。如果你不熟悉df和ds,强烈建议你去熟悉一下,可以使用这个DataFrame/Dataset Programming Guide.

创建流式的ds和流式的df

流式的df 可以通过DataStreamReader 的接口 SparkSession.readStream().如果是R语言,使用read.stream() 方法。与创建静态的df相似,你可以定义一个输入的详情,数据的格式,纲要和选项等。

输入源

在spark2.0中,有许多支持的数据源。

File Source - 读取一个文件夹下的文件作为数据流 支持的文件格式有 text,csv,json,parquet。参考DataStreamReader的文档获得最新的支持列表,和其他文件格式的一些选项。需要指出的是,所有的文件必须自动放置在给定的目录下,

Kafka source-从kafka获取数据,支持kafka 0.10.0和更高的版本。更详细的细节参考.Kafka Integration Guide

Socket source(for testing) -从Socketde 连接中读取UTF-8格式的数据。坚挺的服务端的socket在dirver端。注意这个仅仅用于测试,不支持end-to-end的容错保证。

一些来源不能保证容错因为他们不能在失败的时候使用checkpointed 的偏移量来保证数据可以重放。查看前面的章节fault-tolerance semantics。下面是所有spark的输入源的细节



输入源    选项                                                     容错                           注意


文件源   path:输入的文件目录,对所有的格式都是一样的。    yes    支持glob paths,但是不支持多个逗号分隔的文件夹。

socket 源 :host:必填

                      port:必填

kafka: 参考Kafka Integration Guide.

示例:

valspark:SparkSession=...// Read text from socketvalsocketDF=spark.readStream

.format("socket")

.option("host","localhost")

.option("port",9999).load()

socketDF.isStreaming// Returns True for DataFrames that have streaming sourcessocketDF.printSchema// Read all the csv files written atomically in a directoryvaluserSchema=newStructType().add("name","string").add("age","integer")

val csvDF=spark.readStream.option("sep",";")

.schema(userSchema)// Specify schema of the csv files

.csv("/path/to/directory")// Equivalent to format("csv").load("/path/to/directory")

语义推断和流式df/ds的分区

默认,基于文件源的strucete streaming 需要指定(schema),而不是自动依赖spark。这个限制使得使用streaming query时候的schema保持一致,甚至当发生失败的情况。对于点对点的使用情况,你可以通过设置spark.sql.streaming.schemaInference 为true的方式开启语义推断。

当以/key=value/命名的子目录准备好的的时候,分区发现开始执行,并且自动的递归进这些目录。如果这些列在用户提供的表头中出现,这些数据将会被spark根据文件读入的路径进行填充。 当查询开始的时候决定分区的目录必须是准备好的并且是静态的。比方说, 当前是 /data/year=2015/的时候,添加/data/year=2016/是ok的。但是改变分区的列是无效的,比如创建目录 /data/date=2016-04-17/

基本操作:选择,设计,聚合




实时的窗口操作

使用structed  Streaming 在滑动窗口上的聚合操作是直截了当的,和分组的聚合很相似。在一个分组的聚合中,对于每个用户指定的分组列中特定值,这些聚合的值都会被保持。基于窗口的聚合,每个窗口的聚合值随着每行数据的进入都会保持着。通过示例来学习一下。

假设快速示例改变了,现在流包含产生时间的多行数据,这次我们不计算单词的数量,我们想要统计10分钟窗口的单词数量,并且每五分钟更新一次。





你可能感兴趣的:(Structured Streaming 编程指南)