状态(State)与一致性模型
接下来我们转向另一个在流处理中十分重要的点:状态(state)。状态在数据处理中是无处不在的。为了产生一个结果,函数一般会聚合某个时间段内(或是一定数量的)events的状态信息(例如计算聚合值,或是发现一个模式),有状态的 operators使用流的输入事件以及内部状态,计算出它们的输出。例如,一个滚动聚合operator输出当前它读入的所有的events的总和。Operator 持有当前sum的值作为它的内部状态,并在每次读入新值时对它做更新。类似,考虑一个operator在检测到“高温”后,若是在接下来的10分钟内检测到“烟”,则发出警报。此时operator需要将“高温”事件存储在它的状态信息中,直到看见“烟”事件,或是到达10分钟的过期时间。
由于流处理器可能会处理无限的数据,所以需要注意的是,不要让内部state无限地增长下去。为了限制state 大小,一般来说,operators会维护一些到目前为止看到的所有events的概要(synopsis)、或是总结(summary)信息。这个摘要信息可以是 计数、sum、 一个样本、亦或是用户自定义的一个数据结构(用于存储某些需要的属性)。
对于支持有状态的operators,会有以下挑战:
1. 状态管理
系统需要高效地管理state,并确保它可以被(合理地)并行更新
2. 状态分区
在并行处理时,场景较为复杂,因为输出的结果依赖于state以及持续输入的events。不过在大部分场景中,我们可以通过key将state分区,并单独的管理每个state。例如处理的输入流为一组传感器的events,这时可以使用分区的operator state 独立地维护每个传感器的状态信息。
3. 状态恢复
最大的挑战为:在发生错误时,有状态的operator需要确保state可以被恢复,并且仍能输出正确的结果。
接下来我们详细地讨论一下任务失败以及result guarantees。
任务失败
在流处理工作中,opratror state是非常重要的信息,需要对此有容错能力。如果在一次failure中,state丢失,则即使最终作业恢复了,输出的结果可能仍是不对的。流处理工作一般会持续运行较长时间,所以state可能会在几天(甚至几月)才会被收集一次。如果重新去处理所有的输入,以生成丢失的state,会是一个费时费算力的过程。
在实际场景中,可以经常见到有上百个并行任务(task)的流作业。而在长期运行的流作业中,每个任务在任何时间都有可能失败。如何确保这些故障可以被透明地处理,并让流工作继续运行?事实上,我们需要流处理器不仅可以在任务失败时仍能继续处理,还需要保证结果以及operator state的正确性。接下来我们会对此讨论具体细节。
什么是一个任务失败
对于输入流里的每一个event来说,一个任务是一个处理步骤,包含以下几步:(1)接收到event,将它存储在本地缓存;(2)可能会更新内部state;并(3)生成一个输出条目。而故障可能在以上任意一步发生。如果故障发生在第一步,event是否会丢失?如果在已经更新了state后发生故障,state的信息在任务恢复后是否再次被更新?而在这些场景下,最终输出的结果是否仍是准确的?
流系统通过提供结果保障(result guarantees)定义任务在故障发生时的行为。下面我们会介绍主流流处理器提供的保障(guarantees),以及为了达到这些guarantees,系统实现的一些机制。
至多一次(AT-MOST-ONCE)
当一个任务失败时,最简单的做法是不做任何事。至多一次确保每个event仅被处理一次 。也就是说,失败了就失败了。events可以简单的被丢弃,并且不做任何事去确保结果的正确性。这种guarantee也被称为“没有保障“(no guarantee),因为一个丢弃所有events的系统也可以提供这种保障。没有保障这点可能听起来是一个比较差的想法,但是如果你关注的是尽量少的延迟,并且系统并不要求很高的准确性,则这也是一个可接受的选择。
至少一次(AT-LEAST-ONCE)
在大部分真实应用中,我们会希望events不应该丢失。这种guarantee被称为至少一次,意思是:所有的events均会被处理,并且有些可能会被处理不止一次。如果应用的准确性仅取决于所有事件的完整性,则重复的处理可能也是能被接受的。例如,如果场景是判断某个特定的event是否在流中出现,则可以使用此guarantee。但是如果是判断此特定event在流中出现的次数,则使用此gurantee会返回错误的结果。
为了确保at-least-once结果的准确性,我们需要有一种方式replay events(从数据源或是缓冲区)。持久化的 event log会将events写入到可靠存储,所以在task失败时,这些events可以被replay。另一种达到同样效果的方法是使用 记录确认(record acknowledgments)。此方法会在缓存中存储每个event,直到某个event被管道中所有的task 确认处理后,此event才会被丢弃。
精确一次(EXACTLY-ONCE)
精确一次是最严格的guarantee,并且很难达成。精确一次意思是:不仅没有event丢失,而且在更新state时,每个event仅被应用一次。本质上说,exactly-once guarantee 表示应用会提供准确的结果,就像故障没有发生一样。
提供exactly-once guarantee需要at-least-once guarantees,所以数据replay的机制也是必须的。此外,流处理器还需要确保internal state的一致性。也就是说,在从错误恢复后,它应该能够知道,一个event是否已经被state使用了。事务更新(transactional updates)是一种实现它的方式,但是它会引发大量性能开销的问题。在Flink中,它用了一个轻量级的快照机制(snapshotting)达成exactly-once result guarantee。会在之后的章节进一步讨论。
端到端精确一次(END-TO-END EXACTLY-ONCE)
到目前为止我们所见到的各种guarantees,都表示的是:由流处理器(stream processor)管理的application state。在真实的流应用中,除了流处理器外,都至少会有一个source和一个sink。End-to-end guarantees表示的是在整个数据处理pipeline中的结果准确性。每个组件提供了它自己的guarantee,所以整个pipeline的 end-to-end guarantee 是每个组件中最弱的那个guarantee。需要注意的是,有时候使用较弱的guarantees可能也能获取更强guarantee的语义。一个常见的案例是:当一个task求最大值或最小值的时,使用at-least-once guarantess即可达到exactly-once语义的效果。
总结
到现在为止,我们独立于Flink介绍了流处理中的一些概念,在之后的章节中,我们会介绍Flink是如何实现的这些概念,以及如何使用DataStream API 去编写应用程序并使用介绍到的这些功能。
References:
Vasiliki Kalavri, Fabian Hueske. Stream Processing With Apache Flink. 2019
原文地址:https://www.cnblogs.com/zackstang/p/10850023.html