当前大数据处理平台存在的问题

如图1是目前大数据处理平台最常见的Lambda架构,它的优势在于实时处理与批处理统一,但是它的缺点也很明显:

  1. 实时处理一条路径,批处理另外一条路径,不同的路径采用了不同的计算组件,这就增加了系统的复杂度;
  2. 数据存储多组件化、多份化,如下图,同样的数据会被存储在ElasticSearch 里、S3对象存储系统里、Kafka里、HDFS里以及Cassandra里,而且考虑到数据的可靠性,数据还都是多份冗余的,这就极大的增加了用户的存储成本;
  3. 系统里组件太多太复杂,也增加了用户的运维成本。

lambda架构

​图1. Lambda架构

因此,为了解决Lambda架构的以上三大缺点,流式架构被提出。在流式架构里,流计算一般选用Flink作为计算组件,那么对于存储来说又意味着什么呢?为了降低系统复杂度、减少用户的存储成本与运维成本,我们推出了 流存储,目的之一就是为了重构Lambda架构里的存储栈,这样流式架构就可以由”流计算+流存储“组成。

第4种存储类型 - 流存储

首先,流式大数据处理平台里的数据一般被称之为“流数据”,流数据在百度百科里是这样被定义的:

流数据是一组顺序、大量、快速、连续到达的数据序列,一般情况下,数据流可被视为一个随时间延续而无限增长的动态数据集合。应用于网络监控、传感器网络、航空航天、气象测控和金融服务等领域。

那么目前又有哪种存储系统最适合用于“流数据”呢?正如当前技术条件下最适合“流数据”计算的是类似Flink这样的流计算应用,最适合“流数据”存储的应当是流存储系统。

如图2所示,从 存储的视角来说,每种类型的数据都有其原生的属性和需求,对应有最佳的适用场景以及最合适的存储系统。

存储类型

​ 图2. 4大存储类型

简单来说就是传统数据库这类对于IOPS要求高的业务需要块存储系统。文件共享场景下需要在用户间共享文件进行读写操作,因此适合采用分布式文件存储系统。而互联网业务文件以及图片、视频等适合采用对象存储系统。

流数据存储具有性能要求高、严格次序保证、连续而又无限、大规模租户隔离等特点,而目前市面上又没有这样一个专门针对流数据进行设计的存储系统。因此,为了满足业务需求、平衡商业成本与技术成本,也为了给流数据提供最佳最合适的存储系统,分布式流存储Pravega被推出。

I/O路径隔离

​ 图3. 日志结构

如图3所示:在Pravega里,日志是作为共享存储原语而存在的。Pravega被推出的目的之一就是为了 重构Lambda架构里的存储栈:流批统一、降低存储成本以及运维成本。 一般数据的批处理对应于处理历史数据,因此Pravega支持高吞吐量的追赶读;数据的流处理对应于处理实时数据,因此Pravega又支持低时延的尾部读取以及写入;同时Pravega通过分层存储以及资源自动伸缩降低了用户的存储成本以及运维成本。

Pravega关键架构

架构目标

  • 持久化:在客户端确认写入前,数据被复制并且写入磁盘;
  • 严格的顺序保证以及恰好一次语义:支持追赶读、尾部读以及从中间任意位置读,支持事务
  • 轻量级:一个流就如同一个文件,可以在单集群里创建千万量级起的流;
  • 可弹性:可基于负载和吞吐量智能地动态扩展或者收缩流;
  • 无限性:存储空间大小不受单个节点的容量限制;
  • 高性能:写入延迟低于10ms,吞吐量仅受网络带宽限制,读模式(例如:追赶读)不影响写性能;

逻辑架构

”技术在某种程度上一定是来自此前已有技术的新的组合“ – 《技术的本质》,布莱恩·阿瑟

Pravega为连续而又无限的数据提供了一种新的存储原语 - 流存储,然而Pravega也并不是凭空发明出来的,它是以前成熟技术与新技术的组合,例如Pravega的 范围、流、段、事件就跟Kafka的主题、分区、段、消息对应,而一层存储又用了Bookkeeper,协调器用了Zookeeper等,如图4 :Pravega的逻辑架构。

逻辑架构图

​ 图4. 逻辑架构

  1. Pravega提供了一个用Java编写的客户端库,抽象出了流协议层接口,用于支持客户端应用,例如Flink、Spark以及一些检索系统等;
  2. Pravega实现了一个流数据抽象层,用于事件流和字节流的抽象;
  3. Pravega遵循软件定义存储的设计规则,其控制面与数据面分离,控制实例组成控制面,实现了检索流信息、监控集群、收集相关指标等功能,同时为了实现高可用,通常有多个(建议至少3个)控制实例同时对外提供服务;
  4. Pravega采用Zookeeper作为集群中的协调组件;
  5. Pravega的第1层存储系统由bookkeeper实现,第2层存储系统由开源的HDFS、Ceph、GlusterFS、Swift或者商业存储产品组成。

流批统一 - 降低系统复杂度

通过使用Pravega,实现了流批统一的大数据处理架构,重构了大数据处理平台的存储栈,有效降低了系统复杂度.

存储分层 - 降低存储成本

如图4所示,在Pravega里,底层存储系统由两部分组成:第1层为低时延存储层,主要关注性能,用于存储热点数据,由bookkeeper实现,保证了存储系统的低时延、高性能。第2层为长期存储层,主要关注低成本、高吞吐量以及高可扩展性,提供数据的长期存储,由开源的或者商业的存储产品组成。随着数据的老化,第1层中的数据将自动分层流入第2层。通过这种方式,冷热数据分离有效降低了数据存储成本。

资源自动缩放 - 减少运维成本

在Pravega里,当流中的负载上升或下降时,流中段的数量会随着负载自动增长或收缩,此特性被称之为“自动缩放”,该特性无需人工干预自动完成,有效减少了系统的运维成本。当创建流时,可以使用缩放策略配置流,该策略确定流如何响应其负载变化,目前支持三种策略:1)固定,流段的数量不随负载而变化;2)基于写入的字节数,当每秒写入流的数据字节数增量超过某个目标速率时,流段的数量增加,相应的如果它低于某个流速时,流段数量减少;3)基于事件的个数,与基于字节数的扩展策略类似,不同之处在于它使用事件的个数而不是字节数。

Pravega的一些关键概念与特性

本章节将简要介绍一些Pravega的关键特性。

范围(scope):在Pravega里,范围是流的命名空间,例如可以把一台机器命名为一个范围,也可以把一个无人车命名为一个范围,还可以把整个工厂命名为一个范围。

流(stream):在同一个范围内流具有命名唯一性,所有流的名称在同一个范围内都是唯一的。在pravega里数据被组织到流中的,流是一种可持久化、可伸缩、仅附加、字节大小无限制的序列,具有高性能和强一致性的特性。

段(segment):流由段组成,段是流的分片。

事件(event): 段由事件组成,事件存储在段里,事件是流中的可以表示为一组字节的任何事物。例如:来自温度传感器的读数,它包含少量的字节且由时间戳,度量标识符和温度值组成。另外事件也可以是与用户点击网站或APP相关联的日志数据等。

写客户端(writers):写客户端是一个可以创建事件并将事件写入流中的应用,所有的事件数据都可以通过附加到流的尾部来写入。

读客户端(readers):读客户端是一个可以从流中读取事件的应用,读客户端可以从流中的任何一点读取,比如头部、尾部、中间任何一点。

读者组(readerGroups):读者组由读客户端组成,读者组本质上是为了实现同一个组内读客户端的平衡以及不同组的扇出。同一个读者组内的读客户端可以一起并行读取给定的一组流段内的事件,比如一个读客户端对应一个段。不同的应用可以定义不同的读者组实现扇出,比如定义一个Flink读者组,再定义一个检索读者组,这样二者互不影响,互不干涉,可以优雅而又和谐地一起读取同一个流段内的事件。

顺序保证:流是由段组成的,写入流的事件被写入单个段,在同一个段内的事件具有顺序性。对于读客户端来说,可以分配多个可并行读取的段,从多个段读取的也许是交错的事件,但在同一个段内读取的数据是有严格有序的。

检查点:Pravega为应用提供了在读者组上初始化检查点的功能,使用检查点的意图是通过使用检查点事件来确保每个读客户端能保存原来的使用状态。

事务: Pravega提供了事务功能,事务是写客户端可以“批处理”一堆事件并将它们作为一个处理单元原子性地提交到流中。这一堆事件要么所有都处理成功,要么所有都处理失败。在提交事务之前,发布到事务中的事件永远不会被读客户端看到。

状态同步器: Pravega也提供了在分布式计算环境中作为协调器的功能,类似Zookeeper、ETCD这样的提供分布式共识和领导者选举能力。这样的组件在Pravega里被称作“状态同步器”。状态同步器为在集群中运行的多个进程之间的共享状态提供同步机制,使用户可以轻松地构建高级服务,从而使用户更加的容易构建分布式应用。

恰好一次: Pravega确保每个事件只被处理一次,即使客户端、服务器或网络出现故障也能保证精确的处理顺序。

性能: Pravega的延迟目标为毫秒级(<10ms);

永久保留: Pravega将流的抽象与实际数据存储分离,这使得Pravega可以透明地将数据从低延迟、持久的存储层移到云存储服务层。

高效存储: Pravega统一了流(有序)数据和批量(并行)数据的访问,可以将批量和实时应用程序结合起来而无需为流式计算流水线(比如Flink)的每个步骤复制数据从而有效的提高了数据的存储效率。



## 与kafka对比

前面我们已经提到过Pravega是从 存储的视角来看待流数据,而Kafka本身的定位是消息系统而不是存储系统,它是从 消息的视角来看待流数据。消息系统与存储系统的定位是不同的,简单来说,消息系统是消息的传输系统,关注的是数据传输与生产消费的过程。而存储系统除了关注存储用的物理媒介,数据的持久化、安全、可靠性、一致性、隔离等都是它的原生属性,它关注数据的生产、传输、存放、访问等整个数据的生命周期。

这里我们把Pravega与Kafka做了对比,大体在功能上的差异如下表所示。功能上的差异也只是说明各个产品针对的业务场景不同,看待数据的视角不同,并不是说明这个产品不好,另外每个产品自身也在演进,因此本对比仅供参考。

名称 Kafka 2.1.0 Pravega GA
自动扩容缩容 部分支持 支持
完全不丢数据 不支持 支持
多协议可入 支持 支持
无限个流 不支持 支持
事务 支持 支持
恰好一次 支持 支持
顺序保证 支持 支持
兼容Kafka API 支持 支持
数据链接与汇聚 支持 部分支持
多种二层存储支持(ECS,HDFS,S3,etc) 不支持 支持
安全与加密 支持 支持
无限多租户 不支持 部分支持
服务质量保证 部分支持 部分支持
流计算应用集成 支持 支持
数据治理 不支持 支持

总结

本文讲述了推出分布式流存储Pravega的原因,介绍了一些Pravega的关键架构以及关键特性,另外还与Kafka做了简要对比。有关Pravega的更多详细信息,请参阅官方网站以及关注我们的后续文章。另作者能力有限,如有不足之处欢迎留言批评指正。

问题思考

最后给大家留一个问题:一般来说从开源项目到商业产品还是有一段距离的(注意这里的用词:开源的“项目”,商业的“产品”),那么对于设计开发人员来说应该如何弥补这段距离,从而使得开源项目产品化?

Pravega架构

”技术在某种程度上一定是来自此前已有技术的新的组合“ – 《技术的本质》,布莱恩·阿瑟

Pravega为连续而又无限的数据提供了一种新的存储原语 - 流存储,然而Pravega也并不是凭空发明出来的,它是以前成熟技术与新技术的组合,例如Pravega的 范围、流、段、事件就跟Kafka的主题、分区、段、消息对应,而一层存储又用了Bookkeeper,协调器用了Zookeeper等。

设计原则与目标

  • 持久化:在客户端确认写入前,数据被复制并且写入磁盘;

  • 保序:段内严格保序;

  • 恰好一次:支持恰好一次语义;

  • 轻量级:一个流就如同一个文件,可以在单集群里创建千万量级起的流;

  • 可弹性:可基于负载和吞吐量智能地动态扩展或者收缩流;

  • 无限性:存储空间大小不受单个节点的容量限制;

  • 高性能:写入延迟低于10ms,吞吐量仅受网络带宽限制,读模式(例如:追赶读)不影响写性能;

Pravega设计创新

  1. 支持“无限流”分层

  2. 零接触动态缩放

    • 根据负载和SLO自动调整读/写并行度

    • 没有服务中断

    • 无需手动重新配置客户端
    • 无需手动重新配置服务资源
  3. 智能工作负载分配

    • 无需为峰值负载过度配置服务器
  4. I / O路径隔离

    • 支持尾部写入
    • 支持尾部读
    • 支持追赶读
  5. 支持“恰好一次”事务

逻辑架构

下图为Pravega的逻辑架构图:


逻辑架构图

  1. 首先,Pravega提供了一个用Java编写的客户端库,抽象出了流协议层接口,用于支持客户端应用,例如Flink、Spark以及一些检索系统等;
  2. 其次,Pravega实现了一个流数据抽象层,用于事件流和字节流的抽象;
  3. 再者,从整体架构上来讲Pravega符合软件定义存储的设计规则,其控制面与数据面分离,数据面的集合统称为段存储层,控制实例组成控制面,实现了检索流信息、监控集群、收集相关指标等功能,同时为了实现高可用,通常有多个(建议至少3个)控制实例同时对外提供服务。
  4. Pravega采用Zookeeper作为集群中的协调组件。
  5. Pravega的存储系统由两部分组成:第1层为短期存储层,主要关注性能,用于存储热点数据,由bookkeeper实现,保证了存储系统的低时延、高性能。第2层为长期存储层,主要关注成本,提供数据的持久性以及长期存储,由开源的或者商业的存储产品组成。第1层保留热点数据,随着第1层中数据的老化,数据将自动分层流入第2层。

数据架构

下图展示了Pravega的数据架构图以及数据流分层:

数据架构图

  1. Pravega客户端可以通过调用控制器接口管理流的创建、删除和缩放以及进行事务管理:启动事务、创建事务、跟踪事务状态;
  2. 所有的数据对读来说都是透明的,客户端的读写操作直接与段存储(数据面)进行交互,而不通过控制器;
  3. 段存储里有缓存组件保证了读写的高性能,热点数据放在bookkeeper里作为一层存储;
  4. 数据老化后会自动流转到长期存储(例如:对象存储系统,文件存储系统,HDFS等)里以便降低存储成本;

关键子功能 - 零接触缩放

零接触缩放:段的动态拆分与合并

段的拆分与合并

如上图所示,1)拆分:在t1时刻系统负载加大,段0被拆分成段1和段2,同时段0封装不再写入;t2时刻系统负载继续加大,段2被拆分成段3与段4,同时段2被封装不再写入;t3时刻系统负载又继续加大,段1被拆分成段5和段6,同时段1被封装不再写入;2)合并:t4时刻系统负载降低,段6与段3被合并成段7,同时段6与段3被封装不再写入。而且所有的这些行为都是Pravega里自动完成的无需人工干预。

零接触缩放:写并行 - 与Kafka比较

写并行

当并行写入的时候:

  1. 在Pravega里流段的数量会根据负载和服务质量目标而动态变化,并且段的拆分与合并都是自动进行的无需人工干预,同时拆分或合并流段是,写客户端的配置是静态不变的;

  2. 在Kafka里主题分区数(写并行性)是静态的,添加或删除分区时需要手动配置服务并且当分区数更改时,必须手动更新生产者配置。

零接触缩放:读并行 - 与Kafka比较

读并行

并行读取时:

  1. 在Pravega里,当拆分或者合并流段时,读客户端通过流协议获得通知从而使得读并行与流段缩放保持同步;
  2. 在Kafka里,当分区数更改时,必须手动更改使用者配置。

关键子功能 - 智能工作负载分配

智能工作负载分配 - 与Kafka比较

智能工作负载分配

在Pravega里,热点段会自动拆分,子段在整个集群中重新分配缓解热点,同时最大限度地利用集群的可用IOPS能力;而在Kafka里没有减轻“热点”分区的机制,其强制部署并且过度配置资源以获得处理其“峰值负载”的能力。

关键子功能 - I/O路径隔离

I/O路径隔离

流存储的基础数据结构为仅附加写入的日志结构。考虑到高吞吐量,Pravega支持追赶读,同时为了保证低时延,Pravega还支持尾部读取以及尾部写入,从而进行了IO路径的隔离。

关键子功能 - 事务

智能工作负载分配

Pravega提供了事务功能,事务是写客户端可以“批处理”一堆事件并将它们作为一个处理单元原子性地提交到流中。这一堆事件要么所有都处理成功,要么所有都处理失败。在提交事务之前,发布到事务中的事件永远不会被读客户端看到。如上图所示,第一步,先将一堆事件封装在一个事务里;第二步,提交这个事务。这个事务里所有的事件要么全部都处理成功要么全部都处理失败。

总结

本文分析了物联网场景下的数据存储商业现状以及技术现状,为平衡商业成本与技术成本推出了分布式流存储系统Pravega,同时本文还介绍了流存储的特殊需求点以及与Kafka做了简要对比,此外还介绍了一些Pravega的关键架构以及一些关键特性。有关Pravega的更多详细信息,请参阅官方网站。另作者能力有限,如有不足之处欢迎留言批评指正。

任务和算子链

对于分布式执行,Flink将算子子任务链接到任务中。每个任务由一个线程执行。将算子链接到任务中是一项有用的优化:它可以减少线程到线程切换和缓冲的开销,并在降低延迟的同时提高整体吞吐量。可以配置链接行为; 有关详细信息,请参阅链接文档。

下图中的示例数据流由五个子任务执行,因此具有五个并行线程。

算子链接到任务

作业管理器,任务管理器,客户端

Flink运行时包含两种类型的进程:

  • JobManagers(也称为主作业)协调分布式执行。他们调度任务,协调检查点,协调故障恢复等。

总是至少有一个Job Manager。高可用性配置将具有多个JobManagers,其中一个始终是领导者,其他人则是备用者。

  • TaskManagers(也叫工作者)执行数据流的任务(或者更具体地说,子任务),并且缓冲和交换数据流。

必须至少有一个TaskManager。

JobManagers和TaskManagers可以通过多种方式启动:直接作为独立集群、在容器中、或由YARN或Mesos等资源框架管理。TaskManagers连接到JobManagers,宣布它们自己是可用,并被分配工作。

客户端不是运行时和程序执行的一部分,而是被用来准备和发送的数据流的JobManager。之后,客户端可以断开连接或保持连接以接收进度报告。客户端既可以作为触发执行的Java / Scala程序的一部分运行,也可以在命令行进程中运行./bin/flink run …。

执行Flink数据流所涉及的过程

任务槽和资源

每个worker(TaskManager)都是一个JVM进程,可以在不同的线程中执行一个或多个子任务。为了控制worker接受的任务数量,worker有所谓的任务槽(至少一个)。

每个任务槽代表TaskManager的固定资源子集。例如,具有三个插槽的TaskManager将其托管内存的1/3专用于每个插槽。对资源进行分隔意味着子任务不会与来自其他作业的子任务竞争托管内存,而是具有一定数量的保留托管内存。请注意,此处不会发生CPU隔离; 当前插槽只分离任务的托管内存。

通过调整任务槽的数量,用户可以定义子任务如何相互隔离。每个TaskManager有一个插槽意味着每个任务组在一个单独的JVM中运行(例如,可以在一个单独的容器中启动)。拥有多个插槽意味着更多子任务共享同一个JVM。同一JVM中的任务共享TCP连接(通过多路复用)和心跳消息。它们还可以共享数据集和数据结构,从而减少每任务开销。

具有任务槽和任务的TaskManager

默认情况下,Flink允许子任务共享插槽,即使它们是不同任务的子任务,只要它们来自同一个作业。结果是一个槽可以容纳整个作业的管道。允许此插槽共享有两个主要好处:

Flink集群需要与作业中使用的最高并行度一样多的任务槽。无需计算程序总共包含多少任务(具有不同的并行性)。

更容易获得更好的资源利用率。如果没有插槽共享,非密集型源/ map()子任务将阻止与资源密集型窗口子任务一样多的资源。通过插槽共享,将示例中的基本并行性从2增加到6可以充分利用插槽资源,同时确保繁重的子任务在TaskManagers之间公平分配。

具有共享任务槽的TaskManagers

API还包括可用于防止不期望的插槽共享的资源组机制。

根据经验,一个好的默认任务槽数就是CPU核心数。使用超线程,每个插槽然后需要2个或更多硬件线程上下文。

状态后端

存储键/值索引的确切数据结构取决于所选的状态后端。一个状态后端将数据存储在内存中的哈希映射中,另一个状态后端使用RocksDB作为键/值存储。除了定义保存状态的数据结构之外,状态后端还实现逻辑以获取键/值状态的时间点快照,并将该快照存储为检查点的一部分逻辑。

检查点和快照

保存点

用Data Stream API编写的程序可以从保存点恢复执行。保存点允许更新程序和Flink群集,而不会丢失任何状态。

保存点是手动触发的检查点,它将程序的快照写入状态后端。他们依赖于常规的检查点机制。在执行期间,程序会周期性地在工作节点上创建快照并生成检查点。对于恢复,仅需要最后完成的检查点,并且一旦新的检查点完成,就可以安全地丢弃旧的检查点。

保存点与这些定期检查点类似,不同之处在于它们由用户触发,并且在完成较新的检查点时不会自动过期。可以从命令行或通过REST API取消作业时创建保存点。

抽象层次

Flink提供不同级别的抽象来开发流/批处理应用程序。

抽象层次

  • 最低级抽象只提供有状态流。它通过Process Function嵌入到DataStream API中。它允许用户自由处理来自一个或多个流的事件,并使用一致的容错状态。此外,用户可以注册事件时间和处理时间回调,允许程序实现复杂的计算。

  • 实际上,大多数应用不需要上述低级抽象,而是针对Core API编程, 如DataStream API(有界/无界流)和DataSet API (有界数据集)。这些流动的API提供了用于数据处理的通用构建块,例如各种形式的用户指定的转换,连接,聚合,窗口,状态等。在这些API中处理的数据类型在相应的编程语言中表示为类。

低级Process Function与DataStream API集成,因此只能对某些操作进行低级抽象。DataSet API提供的有限数据集的其他原语,如循环/迭代。

  • Table API是以表为中心的声明性DSL,其可以是动态地改变的表(表示流时)。Table API遵循(扩展)关系模型:表附加了一个模式(类似于在关系数据库中的表),API提供了类似的操作,如选择,项目,连接,分组依据,聚合等。Table API程序以声明方式定义应该执行的逻辑操作,而不是准确指定 操作代码的外观。虽然Table API可以通过各种类型的用户定义函数进行扩展,但它的表现力不如Core API,但使用更简洁(编写的代码更少)。此外,Table API程序还会通过优化程序,在执行之前应用优化规则。

可以在表和DataStream / DataSet之间无缝转换,允许程序混合Table API以及DataStream 和DataSet API。

  • Flink提供的最高级抽象是SQL。这种抽象在语义和表达方面类似于Table API,但是将程序表示为SQL查询表达式。在SQL抽象与 Table API紧密地相互作用,和SQL查询可以在Table API中定义的表上执行。

程序和数据流

Flink程序的基本构建块是流和转换。(请注意,Flink的DataSet API中使用的DataSet也是内部流 - 稍后会详细介绍。)从概念上讲,流是(可能永无止境的)数据记录流,而转换是将一个或多个流作为输入,并产生一个或多个流输出的结果。

执行时,Flink程序映射到流数据流,由流和转换运算符组成。每个数据流都以一个或多个源开头,并以一个或多个接收器结束。数据流类似于任意有向无环图 (DAG)。尽管通过迭代结构允许特殊形式的循环 ,但为了简单起见,我们将在大多数情况下对此进行掩饰。

DataStream程序及其数据流

通常,程序中的转换与数据流中的运算符之间存在一对一的对应关系。但是,有时一个转换可能包含多个转换运算符。

源流和接收器记录在流连接器和批处理连接器文档中。DataStream运算符和DataSet转换中记录了转换。

并行数据流

Flink中的程序本质上是并行和分布式的。在执行期间,流具有一个或多个流分区,并且每个运算符具有一个或多个运算符子任务。运算符子任务彼此独立,并且可以在不同的线程中执行,并且可能在不同的机器或容器上执行。

运算符子任务的数量是该特定运算符的并行度。流的并行性始终是其生成运算符的并行性。同一程序的不同运算符可能具有不同的并行级别。

并行数据流

流可以以一对一(或转发)模式或以重新分发模式在两个算子之间传输数据:

  • 一对一流(例如,在上图中的Source和map()算子之间)保留元素的分区和排序。这意味着map()算子的subtask [1] 将以与Source算子的subtask [1]生成的顺序相同的顺序看到相同的元素。

  • 重新分配流(在上面的map()和keyBy / window之间,以及 keyBy / window和Sink之间)重新分配流。每个算子子任务将数据发送到不同的目标子任务,具体取决于所选的转换。实例是 keyBy() (其通过散列密钥重新分区),广播() ,或重新平衡() (其重新分区随机地)。在重新分配交换中,元素之间的排序仅保留在每对发送和接收子任务中(例如,map()的子任务[1] 和子任务[2]keyBy / window)。因此,在此示例中,保留了每个密钥内的排序,但并行性确实引入了关于不同密钥的聚合结果到达接收器的顺序的非确定性。

有关配置和控制并行性的详细信息,请参阅并行执行的文档。

视窗

聚合事件(例如,计数,总和)在流上的工作方式与批处理方式不同。例如,不可能计算流中的所有元素,因为流通常是无限的(无界)。相反,流上的聚合(计数,总和等)由窗口限定,例如“在最后5分钟内计数”或“最后100个元素的总和”。

Windows可以是时间驱动的(例如:每30秒)或数据驱动(例如:每100个元素)。一个典型地区分不同类型的窗口,例如翻滚窗口(没有重叠), 滑动窗口(具有重叠)和会话窗口(由不活动的间隙打断)。

时间和计数Windows

更多窗口示例可以在此博客文章中找到。更多详细信息在窗口文档中。

时间

当在流程序中引用时间(例如定义窗口)时,可以参考不同的时间概念:

  • 事件时间是创建事件的时间。它通常由事件中的时间戳描述,例如由生产传感器或生产服务附加。Flink通过时间戳分配器访问事件时间戳。

  • 摄取时间是事件在源操作员处输入Flink数据流的时间。

  • 处理时间是执行基于时间的操作的每个算子的本地时间。

事件时间,摄取时间和处理时间

有关如何处理时间的更多详细信息,请参阅事件时间文档

有状态的操作

虽然数据流中的许多操作只是一次查看一个单独的事件(例如事件解析器),但某些操作会记住多个事件(例如窗口操作符)的信息。这些操作称为有状态。

状态操作的状态保持在可以被认为是嵌入式键/值存储的状态中。状态被分区并严格地与有状态算子读取的流一起分发。因此,只有在keyBy()函数之后才能在键控流上访问键/值状态,并且限制为与当前事件的键相关联的值。对齐流和状态的密钥可确保所有状态更新都是本地操作,从而保证一致性而无需事务开销。此对齐还允许Flink重新分配状态并透明地调整流分区。

状态和分区

有关更多信息,请参阅有关状态的文档。

容错检查点

Flink使用流重放和检查点的组合实现容错。检查点与每个输入流中的特定点以及每个算子的对应状态相关。通过恢复算子的状态并从检查点重放事件,可以从检查点恢复流数据流,同时保持一致性(恰好一次处理语义)。

检查点间隔是在执行期间用恢复时间(需要重放的事件的数量)来折衷容错开销的手段。

容错内部的描述提供了有关Flink如何管理检查点和相关主题的更多信息。有关启用和配置检查点的详细信息,请参阅检查点API文档。

批处理流

Flink执行批处理程序作为流程序的特殊情况,其中流是有界的(有限数量的元素)。数据集做为数据流在内部处理。因此,上述概念以适用于流程序相同的方式应用于批处理程序,只是少数例外:

  • 批处理程序的容错不使用检查点。而是通过完全重放流来恢复。这是可能的,因为输入是有界的。这会使成本更多高,但却使常规处理更便宜,因为它避免了检查点。

  • DataSet API中的有状态操作使用简化的内存/核外数据结构,而不是键/值索引。

  • DataSet API引入了特殊的同步(基于超前的)迭代,这在有界流上是可行的。有关详细信息,请查看迭代文档

下一步

Flink的Distributed Runtime。

Apache Flink文档

本文档适用于Apache Flink master版。

Apache Flink是一个用于分布式流和批处理数据处理的开源平台。Flink的核心是流数据流引擎,为数据流上的分布式计算提供数据分发,通信和容错。Flink在流引擎之上构建批处理,涵盖原生的迭代支持,受管理的内存和程序优化。

第一步

概念:从Flink的数据流编程模型分布式运行时环境的基本概念开始。这将有助于您了解文档的其他部分,包括配置和编程指南。我们建议您先阅读这部分内容。

教程:

编程指南:您可以阅读我们关于基本API概念DataStream APIDataSet API的指南,以了解如何编写您的第一个Flink程序。

部署

在将Flink作业投入生产之前,请阅读生产准备清单

发行说明

发行说明涵盖了Flink版本之间的重要更改。如果您计划将Flink升级到更高版本,请仔细阅读这些说明。

外部资源

关于Flink项目,一般会经常被问到以下问题。

常见问题

Apache Flink仅用于(近)实时处理用例吗?

Flink是一个非常通用的系统,用于数据处理和数据驱动的应用程序,数据流作为核心构建块。这些数据流可以是实时数据流,也可以是存储的历史数据流。例如,在Flink的视图中,文件是存储的字节流。因此,Flink支持实时数据处理和应用,以及批处理应用。

流可以是无界的(没有结束,事件不断发生)或受限制(流有开始和结束)。例如,来自消息队列的Twitter馈送或事件流通常是无界流,而来自文件的字节流是有界流。

如果一切都是流,为什么Flink中有DataStream和DataSet API?

有界流通常比无界流更有效。在(近)实时处理无限事件流需要系统能够立即对事件起作用并产生中间结果(通常具有低延迟)。处理有界流通常不需要产生低延迟结果,因为无论如何数据都是旧的(相对而言)。这允许Flink以简单且更有效的方式处理数据。

DataStream API通过支持低延时的结果和对事件和时间(包括事件时间)灵活反应的模型捕获无界流和有界流的连续处理,

DataSet API具有加快有界数据流的处理的技术。将来,社区计划将这些优化与DataStream API中的技术相结合。

Flink如何与Hadoop堆栈相关?

Flink独立于Apache Hadoop,并且在没有任何Hadoop依赖性的情况下运行。

但是,Flink与许多Hadoop组件集成得非常好,例如HDFS,YARN或HBase。与这些组件一起运行时,Flink可以使用HDFS读取数据,或写入结果和检查点/快照。Flink可以通过YARN轻松部署,并与YARN和HDFS Kerberos安全模块集成。

Flink运行的其他堆栈是什么?

Flink可以在Kubernetes,Mesos, Docker上运行 ,甚至作为独立服务运行。

使用Flink有哪些先决条件?

您需要Java 8来运行Flink作业/应用。
Scala API(可选)依赖于Scala 2.11。
Apache ZooKeeper需要高度可用且没有单点故障的设置。
对于可以从故障中恢复的高可用流处理设置,Flink需要某种形式的分布式存储用于检查点(HDFS / S3 / NFS / SAN / GFS / Kosmos / Ceph / …)。

Flink支持多大的规模?

用户在非常小的设置(少于5个节点)和1000个节点以及状态的TB上运行Flink作业。

Flink是否仅限于内存数据集?

对于DataStream API,Flink支持大于内存的状态来配置RocksDB状态后端。

对于DataSet API,所有操作(delta迭代除外)都可以扩展到主内存之外。

常见错误消息

“ 获得帮助”页面上列出了常见错误消息。

参考资料

[1].https://flink.apache.org/faq.html

用例

Apache Flink因其丰富的功能集而成为开发和运行多种不同类型应用程序的绝佳选择。Flink的功能包括对流和批处理的支持,复杂的状态管理,事件时间处理语义以及状态的恰好一次一致性保证。此外,Flink可以部署在各种资源管理集群(如YARN,Apache Mesos和Kubernetes)上,也可以部署为裸机硬件上的单个群集。Flink配置为高可用性,没有单点故障。Flink已经被证明可以扩展到数千个核心和万亿字节的应用状态,提供高吞吐量和低延迟,并为世界上一些最苛刻的流处理应用程序提供支持。

下面,我们将探讨由Flink提供支持的最常见类型的应用程序,并指出实际示例。

  • 事件驱动的应用
  • 数据分析应用
  • 数据管道应用

事件驱动的应用

什么是事件驱动的应用?

事件驱动的应用程序是一个有状态的应用程序,它从一个或多个事件流中提取事件,并通过触发计算,状态更新或外部操作对传入事件做出响应。

事件驱动的应用程序是传统应用程序设计的演变,具有分离的计算和数据存储层。在传统应用的体系结构中,应用从远程事务数据库中读取数据并将数据持久化到远程事务数据库。

相比之下,事件驱动的应用程序基于有状态流处理应用程序。在这种设计中,数据和计算是共同定位的,这产生了本地(内存或磁盘)数据访问。通过定期将检查点写入远程持久存储来实现容错。下图描绘了传统应用程序体系结构和事件驱动应用程序之间的差异。

事件驱动的应用有哪些优点?

事件驱动的应用程序不是查询远程数据库,而是在本地访问其数据,从而在吞吐量和延迟方面发挥更好的性能。远程持久存储的定期检查点可以异步和递增完成。因此,检查点对常规事件处理的影响非常小。但是,事件驱动的应用程序设计提供的不仅仅是本地数据访问。在分层体系结构中,多个应用程序共享同一数据库是很常见的。因此,需要协调数据库的任何更改,例如由于应用程序更新或扩展服务而更改数据布局。由于每个事件驱动的应用程序都负责自己的数据,因此对数据表示的更改或扩展应用程序需要较少的协调。

Flink如何支持事件驱动的应用?

事件驱动应用程序的限制由流处理器处理时间和状态的程度来定义。Flink的许多杰出功能都围绕着这些概念。Flink提供了一组丰富的状态原语,可以管理非常大的数据量(最多几TB),并且具有恰好一次的一致性保证。此外,Flink支持事件时间,高度可定制的窗口逻辑,以及通过ProcessFunction实现高级业务逻辑提供的细粒度时间控制。此外,Flink还提供了一个用于复杂事件处理(CEP)的库,用于检测数据流中的模式。

但是,Flink针对事件驱动应用程序的突出特点是保存点功能。保存点是一致的状态图像,可用作兼容应用程序的起点。给定保存点,可以更新应用程序或调整其规模,或者可以启动应用程序的多个版本以进行A / B测试。

什么是典型的事件驱动应用?

  • 欺诈识别
  • 异常检测
  • 基于规则的警报
  • 业务流程监控
  • Web应用程序(社交网络)

数据分析应用

什么是数据分析应用?

分析工作从原始数据中提取信息和洞察力。传统上,分析是在有记录事件的有界数据集上作为批查询或应用程序来执行的。为了将最新数据合并到分析结果中,必须将其添加到分析的数据集中,并重新运行查询或应用程序。结果将写入存储系统或作为报告发出。

借助先进的流处理引擎,还可以实时地执行分析。流式查询或应用程序不是读取有限数据集,而是摄取实时事件流,并在消耗事件时不断生成和更新结果。结果要么写入外部数据库,要么保持为内部状态。仪表板应用程序可以从外部数据库读取最新结果或直接查询应用程序的内部状态。

Apache Flink支持流式和批量分析应用程序,如下图所示。

流式分析应用有哪些优势?

与批量分析相比,连续流分析的优势不仅限于因消除定期导入和查询执行而从事件到洞察的低得多的延迟。与批量查询相比,流式查询不必处理输入数据中的人为边界,这些边界是由定期导入和输入的有界性质引起的。

另一方面是更简单的应用程序架构。批量分析管道由若干独立组件组成,以周期性地调度数据提取和查询执行。可靠地操作这样的管道并非易事,因为一个组件的故障会影响管道的后续步骤。相比之下,在像Flink这样的复杂流处理器上运行的流分析应用程序包含从数据摄取到连续结果计算的所有步骤。因此,它可以依赖于引擎的故障恢复机制。

Flink如何支持数据分析应用?

Flink为连续流式传输和批量分析提供了非常好的支持。具体来说,它具有符合ANSI标准的SQL接口,具有用于批处理和流式查询的统一语义。无论是在记录事件的静态数据集上还是在实时事件流上运行,SQL查询都会计算相同的结果。对用户定义函数的丰富支持可确保在SQL查询中执行自定义代码。如果需要更多的自定义逻辑,Flink的DataStream API或DataSet API提供更多的低级控制。此外,Flink的Gelly库为批量数据集上的大规模和高性能图形分析提供算法和构建块。

什么是典型的数据分析应用?

  • 电信网络的质量监控
  • 分析移动应用程序中的产品更新和实验评估
  • 对消费者技术中的实时数据进行特别分析
  • 大规模图分析

数据管道应用

什么是数据管道?

提取 - 转换 - 加载(ETL)是在存储系统之间转换和移动数据的常用方法。通常会定期触发ETL作业,以便将数据从事务数据库系统复制到分析数据库或数据仓库。

数据管道与ETL作业具有相似的用途。它们可以转换和丰富数据,并可以将数据从一个存储系统移动到另一个存储系统 但是,它们以连续流模式运行,而不是周期性地触发。因此,他们能够从连续生成数据的源中读取记录,并以低延迟将其移动到目的地。例如,数据管道可能会监视文件系统目录中的新文件并将其数据写入事件日志。另一个应用程序可能会将事件流实现到数据库,或者逐步构建和优化搜索索引。

下图描述了定期ETL作业和连续数据管道之间的差异。

数据管道有哪些优势?

连续数据流水线优于周期性ETL作业的显著优势是减少了将数据移动到目的地的延迟。此外,数据管道更加通用,可用于更多用例,因为它们能够连续消耗和发送数据。

Flink如何支持数据管道?

Flink的SQL接口(或表API)可以解决许多常见的数据转换或丰富任务,并支持用户定义的函数。通过使用更通用的DataStream API,可以实现具有更高级要求的数据管道。Flink为各种存储系统(如Kafka,Kinesis,Elasticsearch和JDBC数据库系统)提供了丰富的连接器。它还具有连续的文件系统源,用于监视以时间分区方式写入文件的目录和接收器。

什么是典型的数据管道应用?

  • 电子商务中的实时搜索索引构建
  • 电子商务中持续的ETL

参考资料:

[1].https://flink.apache.org/usecases.html

Apache Flink是一个框架和分布式处理引擎,用于对无界和有界数据流进行有状态计算。Flink设计为在所有常见的集群环境中运行,以内存速度和任何规模执行计算。

在这里,我们解释了Flink架构的重要方面。

无界和有界数据的处理

任何类型的数据都是作为事件流产生的。信用卡交易,传感器测量,机器日志或网站或移动应用程序上的用户交互,所有这些数据都作为流生成。

数据可以作为无界或有界流处理。

  1. 无界流 有一个开始,但没有定义的结束。它们不会终止并提供其生成的数据。无界流必须持续处理,即必须在摄取事件后立即处理事件。不可能等待所有的输入数据都到达,因为输入是无界的,并且在任何时间点都不会结束。处理无界数据通常要求以特定顺序(例如事件发生的顺序)摄取事件,以便能够推断结果的完整性。

  2. 有界流具有定义的开始和结束。可以在执行任何计算之前,通过摄取所有数据来处理有界流。有界数据集是可以被排序的,因此处理有界流不需要有序摄取。有界流的处理也称为批处理。

Apache Flink擅长处理无界和有界数据集。精确控制时间和状态使Flink的运行时能够在无界流上运行任何类型的应用程序。有界流由算法和数据结构在内部处理,这些算法和数据结构专门针对固定大小的数据集而设计,从而发挥出性能优势。

随处部署应用

Apache Flink是一个分布式系统,需要计算资源才能执行的应用程序。Flink可以与所有常见的集群资源管理器(如Hadoop YARN,Apache Mesos和Kubernetes)集成,但也可以设置为独立的集群运行。

Flink旨在很好地适用于之前列出的每个资源管理器,这是通过特定于资源管理器的部署模式实现的,这些模式允许Flink以其惯用的方式与每个资源管理器进行交互。

部署Flink应用程序时,Flink会根据应用程序配置的并行性自动识别所需资源,并从资源管理器里申请它们。如果发生故障,Flink会通过申请新资源来替换发生故障的容器。所有提交或控制应用程序的通信都是通过REST调用来进行,这简化了Flink在许多环境中的集成。

以任何规模运行应用

Flink旨在以任何规模运行有状态的流应用,应用程序可以并行化为数千个在集群中分布和同时执行的任务。因此,应用程序可以利用几乎无限量的CPU、主内存、磁盘和网络IO。而且,Flink可以轻松维护数据量非常大的应用状态。其异步和增量检查点算法确保对处理的延迟影响最小,同时保证恰好一次状态的一致性。

用户报告了在其生产环境中运行的Flink集群的规模,这样的规模有点令人印象深刻,例如

  • 应用程序每天处理数万亿个事件,
  • 应用程序维护多个TB的状态,以及
  • 应用程序在数千个内核的运行。

内存的性能优势

有状态Flink应用针对本地状态的访问进行了优化。任务状态始终保留在内存中,或者,如果状态大小超过可用内存,则保存在可高效访问的磁盘上的数据结构中。因此,任务通过访问本地(通常是内存中)状态来执行所有计算,从而产生非常低的处理延迟。Flink通过定期并且异步地把本地状态打检查点并持久化到存储设备来保证在出现故障时的恰好一次状态的一致性。

参考资料

[1].https://flink.apache.org/flink-architecture.html