这个问题其实咱们大部分时候是没有考虑过的,大多数,咱们是把流式处理和实时计算放在一块儿来讲的。咱们先来了解下,什么是数据流。数据库
流式处理就是指实时地处理一个或多个事件流。它是一种编程范式。其余编程领域,主要有3种编程范式:编程
流的定义不依赖某个框架,只要储蓄从一个无边界数据集中读取数据,并对它们进行处理生成结果,就是进行流式处理。重点是:整个过程必须是持续的。设计模式
上述咱们已经说过了,数据流都是有序的。某一时刻的数据是肯定的。时间是流式处理中很是重要的概念。大部分流式应用的操做都是基于时间窗口的。缓存
流式系统通常包含如下几个时间概念(熟悉Flink的同窗应该会很熟悉):网络
若是流式处理是来一个事件就处理一个事件,那么流式处理就很简单。但若是操做中包含了多个事件,流式处理就有意思了。例如:咱们想在流式处理中统计北京用户的订单数量、消费金额等等。此时,就不能光处理单个事件了,咱们须要获取更多的事件。事件与事件之间的信息就称之为状态。例如简单的,求某个类型的订单数等。并发
这些状态通常就保存在流式处理程序本地变量(本地内存)中,例如:使用HashMap来保存计数。但这种作法是很不可靠的,流式处理处理的是无界数据集,一旦应用程序出现异常,就会出现状态丢失,这是咱们说不能接受的。因此,每一种流式计算框架都会很当心地持久化状态。若是应用程序重启,须要将这些数据恢复。负载均衡
流式处理通常包含两种状态:框架
大部分针对流的操做都是基于时间窗口的。例如:计算一周内销量最好的产品。两个流的合并也是基于时间窗口的。流式系统会合并发生在相同时间段上的事件。窗口是有类型的。如下几点是咱们设计窗口须要考虑的:性能
下面这张图,说明了滚动窗口与滑动窗口的区别。线程
滚动窗口:假设窗口的大小为5分钟,这里肯定的3个时间窗口
滑动窗口:假设每分钟滑动一次,那么这个时候会有5个时间窗口,计算结果会发生重叠
这是流式处理最基本的模式。这种模式也叫:map或filter模式。常常被用来过滤无用的事件或者用于转换事件。
这种模式,应用程序读取流中的数据,修改数据,而后把事件生成到另外一个流上。这一类应用程序无需在程序内部维护状态,每个事件都是独立处理的。这种错误恢复和进行负载均衡都很容易。由于无需进行状态恢复操做。
大部分流式处理应用关系如何聚合数据。特别是:基于时间窗口进行聚合。例如:找到天天最低、最高的交易价格。要实现这种操做,就须要维护流的状态。例如:咱们须要将最小值、最大值保存下来,用它们与每个新值对比。这类操做,能够经过本地状态来实现。例如:每个分组都维护本身分组的状态。
一旦流式处理中包含了本地状态,就须要解决如下问题。
有些时候,咱们要经过全部可用的数据来得到结果。例如:要发布天天的“前10支”股票,这10支股票须要从天天的交易股票中挑选出来。若是仅仅在单个实例上处理是不够的,由于10支股票分布在多个实例上。
此种,咱们分为多个阶段来处理。
一、计算每支股票当天的涨跌。这个计算能够在每一个实例上执行
二、将结果写入到单个分区
三、再用一个实例找出当天的前10支股票
这一类操做就与MapReduce很像了。
有时候,流式处理须要将外部数据和流集成在一日。例如:外部数据中保存了一些规则、或者将完整完整地用户信息拉取到流中。
这种case最大的问题,外部查找会带来严重的延迟,通常在 5-15 ms之间,这在不少状况下是不可行的。并且,外部系统也没法承受这种额外的负载——流式处理系统每秒能够处理10-50W个事件,而数据库正常状况下每秒只能处理1W个事件,因此须要伸缩性更强的解决方案。
为了获取更好的性能和更强的伸缩性,须要将外部数据库的信息缓存到流式处理应用中。但考虑如下问题:
如何保证缓存里的数据是最新的?
若是刷新太频繁,仍然会对数据库形成很大压力,缓存也就无用了。
若是刷新不及时,那么流式处理中所用的数据就会过期。
若是可以捕捉数据库的变动事件,并造成事件流,流式处理做业就能够监听事件流,并及时更新缓存。捕捉数据库的变动事件并造成数据流,这个过程称为CDC(Change Data Capture)。例如:咱们能够经过Canal来捕获MySQL数据库的变化、能够经过ogg来捕获Oracle数据库的变化
有时候须要链接两个真实的事件流。要链接两个流,就是链接全部的历史事件(将两个妞中具备相同键、发生在相同时间窗口内的事件匹配起来),这种流和流的链接称为:基于时间窗口的链接(windowed-join)。链接两个流,一般包含一个滑动时间窗口。
无论对于流式处理、仍是传统的ETL系统,处理乱序事件都是一个挑战。物联网领域常常发生乱序事件:一个移动设备断开Wifi链接几个小时,在从新连上WiFi后,将几个小时堆积的事件一并发出去。要让流式处理应用处理好这些场景,须要作到几下:
该重要模式是从新处理事件:
第一种状况,须要Kafka将事件流长时间地保存在可伸缩的数据存储中
第二种状况,须要应用程序回到输入流的起始位置开始处理,同时重置本地状态,还要清理以前的输出流。这种方式处理起来比较困难。建议仍是使用第一种方案。
参考文献:
《Kafka全文指南》