以Flink为例,消除流处理常见的六大谬见

大数据杂谈 浏览次数: 2016-12-02 14:33

我们在思考流处理问题上花了很多时间,更酷的是,我们也花了很多时间帮助其他人认识流处理,以及如何在他们的组织里应用流处理来解决数据问题。 我们首先要'...

我们在思考流处理问题上花了很多时间,更酷的是,我们也花了很多时间帮助其他人认识流处理,以及如何在他们的组织里应用流处理来解决数据问题。

我们首先要做的是纠正人们对流处理(作为一个快速变化的领域,这里有很多误见值得我们思考)的错误认识。

在这篇文章里,我们选出了其中的六个作为例子。因为我们对Apache Flink比较熟悉,所以我们会基于Flink来讲解这些例子。

谬见1:没有不使用批处理的流(Lambda架构)

谬见2:延迟和吞吐量:只能选择一个

谬见3:微批次意味着更好的吞吐量

谬见4:Exactly once?完全不可能

谬见5:流只能被应用在“实时”场景里

谬见6:不管怎么样,流仍然很复杂

谬见1:没有不使用批处理的流(Lambda架构)

“Lambda架构”在Apache Storm的早期阶段和其它流处理项目里是一个很有用的设计模式。这个架构包含了一个“快速流层”和一个“批次层”。

之所以使用两个单独的层,是因为Lambda架构里的流处理只能计算出大致的结果(也就是说,如果中间出现了错误,那么计算结果就不可信),而且只能处理相对少量的事件。

算Storm的早期版本存在这样的问题,但现今的很多开源流处理框架都具有容错能力,它们可以在出现故障的前提下生成准确的计算结果,而且具有高吞吐的计算能力。所以没有必要再为了分别得到“快”和“准确”的结果而维护多层架构。现今的流处理器(比如Flink)可以同时帮你得到两种结果。

好在人们不再更多地讨论Lambda架构,说明流处理正在走向成熟。

谬见2:延迟和吞吐量:只能选择一个

早期的开源流处理框架要么是“高吞吐”的,要么是“低延迟”的,而“海量且快速”一直未能成为开源流处理框架的代名词。

不过Flink(可能还有其它的框架)就同时提供了高吞吐和低延迟。这里有一个基准测试结果的样例。

让我们从底层来剖析这个例子,特别是从硬件层,并结合具有网络瓶颈的流处理管道(很多使用Flink的管道都有这个瓶颈)。在硬件层不应该存在需要作出权衡的条件,所以网络才是影响吞吐量和延迟的主要因素。

一个设计良好的软件系统应该会充分利用网络的上限而不会引入瓶颈问题。不过对Flink来说,总是有可优化的空间,可以让它更接近硬件所能提供的效能。使用一个包含10个节点的集群,Flink现在每秒可以处理千万级别的事件量,如果扩展到1000个节点,它的延迟可以降低到几十毫秒。在我们看来,这种水平已经比很多现有的方案高出很多。

谬见3:微批次意味着更好的吞吐量

我们可以从另一个角度来讨论性能,不过先让我们来澄清两个容易混淆的概念:

微批次

微批次建立在传统批次之上,是处理数据的一个执行或编程模型。“通过这项技术,进程或任务可以把一个流当作一系列小型的批次或数据块”。

缓冲

缓冲技术用于对网络、磁盘、缓存的访问进行优化。Wikipedia完美地把它定义为“物理内存里的一块用于临时储存移动数据的区域“。

那么第3个缪见就是说,使用微批次的数据处理框架能够比每次处理一个事件的框架达到更高的吞吐量,因为微批次在网络上传输的效率更高。

这个缪见忽略了一个事实,流框架不会依赖任何编程模型层面的批次,它们只会在物理层面使用缓冲。

Flink确实也会对数据进行缓冲,也就是说它会通过网络发送一组处理过的记录,而不是每次发送一条记录。从性能方面说,不对数据进行缓冲是不可取的,因为通过网络逐个发送记录不会带来任何性能上的好处。所以我们得承认在物理层面根本不存在类似一次一条记录这样的情况。

不过缓冲只能作为对性能的优化,所以缓冲:

对用户是不可见的

不应该对系统造成任何影响

不应该出现人为的边界

不应该限制系统功能

所以对Flink的用户来说,他们开发的程序能够单独地处理每个记录,那是因为Flink为了提升性能隐藏了使用缓冲的细节。

事实上,在任务调度里使用微批次会带来额外的开销,而如果这样做是为了降低延迟,那么这种开销会只增不减!流处理器知道该如何利用缓冲的优势而不会带来任务调度方面的开销。

谬见4:Exactly once?完全不可能

这个缪见包含了几个方面的内容:

从根本上说,Exactly once是不可能的

从端到端的Exactly once是不可能的

Exactly once从来都不是真实世界的需求

Exactly once以牺牲性能为代价

让我们退一步讲,我们并不介意“Exactly once”这种观点的存在。“Exactly once”原先指的是“一次性传递”,而现在这个词被随意用在流处理里,让这个词变得令人困惑,失去了它原本的意义。不过相关的概念还是很重要的,我们不打算跳过去。

为了尽量准确,我们把“一次性状态”和“一次性传递”视为两种不同的概念。因为之前人们对这两个词的使用方式导致了它们的混淆。Apache Storm使用“at least once”来描述传递(Storm不支持状态),而Apache Samza使用“at least once”来描述应用状态。

一次性状态是指应用程序在经历了故障以后恍如没有发生过故障一样。例如,假设我们在维护一个计数器应用程序,在发生了一次故障之后,它既不能多计数也不能少计数。在这里使用“Exactly once”这个词是因为应用程序状态认为每个消息只被处理了一次。

一次性传递是指接收端(应用程序之外的系统)在故障发生后会收到处理过的事件,恍如没有发生过故障一样。

流处理框在任何情况下都不保证一次性传递,但可以做到一次性状态。Flink可以做到一次性状态,而且不会对性能造成显著影响。Flink还能在与Flink检查点相关的数据槽上做到一次性传递。

Flink检查点就是应用程序状态的快照,Flink会为应用程序定时异步地生成快照。这就是Flink在发生故障时仍然能保证一次性状态的原因:Flink定时记录(快照)输入流的读取位置和每个操作数的相关状态。如果发生故障,Flink会回滚到之前的状态,并重新开始计算。所以说,尽管记录被重新处理,但从结果来看,记录好像只被处理过一次。

那么端到端的一次性处理呢?通过恰当的方式让检查点兼具事务协调机制是可能的,换句话说,就是让源操作和目标操作参与到检查点里来。在框架内部,结果是一次性的,从端到端来看,也是一次性的,或者说“接近一次性”。例如,在使用Flink和Kafka作为数据源并发生数据槽(HDFS)滚动时,从Kafka到HDFS就是端到端的一次性处理。类似地,在把Kafka作为Flink的源并且把Cassandra作为Flink的槽时,如果针对Cassandra的更新是幂等时,那么就可以实现端到端的一次性处理。

值得一提的是,利用Flink的保存点,检查点可以兼具状态版本机制。使用保存点,在保持状态一致性的同时还可以“随着时间移动”。这样可以让代码的更新、维护、迁移、调试和各种模拟测试变得简单。

网友点评
猜你喜欢