00:01
到目前为止,我们已经了解了flink当中data streamam API的各种用法啊,从一开始第五章我们就已经介绍了data stream API的基本调用方式,第六章呢,又扩展了窗口的用法,Window API的调用,第七章介绍了底层的处理函数。Process function第八章我们介绍的是多流转换操作,分流合流,嗯,那关于基于一个data stream能够调用的方法,能够调用的API,前面我们基本上就已经全部都讲完了,接下来我们要介绍的呢,是。Think当中非常重要的一个核心概念,我们还要对它进行一个展开的讲解,那就是所谓的状态state。啊,其实这个概念我们并不陌生,在一开始我们就说了弗link对自己的定位是什么呢?就是一个有状态的流式计算处理引擎。哎,所以我们会说关于flink当中的转换处理状态,其实都是非常非常重要的。
01:03
像之前我们介绍的啊,不管是简单的聚合,还是基于一个窗口的聚合,还是在处理函数里边,我们实现一些具体的需求,都会出现状态的概念。啊,那我们在一开始做flink简介的时候,其实就已经介绍过,对于有状态的流处理而言,所谓状态就可以看成事物处理里边数据库里面保存的那些信息啊,只不过现在我们不用把它放在数据库里面了,想要查找相关数据的时候,我们直接在本地去查找,本地去保存就可以了,这个本地保存的数据我们就把它叫做状态,那在flink当中呢,这样的一个东西,很显然我们不能简简单单的就把它看成一个本地保存的变量。那在flink的底层呢,其实给我们提供了非常方便的直接定义状态和处理状态的一整套机制啊,那我们在代码当中,其实之前也已经做过类似的实现啊,直接可以在代码当中自定义状态,然后去进行编程,那这种方式,这就是我们所说的状态编程,所以接下来的第九章我们主要就介绍这部分内容。
02:14
那首先呢,我们还是要先来明确一下flink当中状态的概念,哎,那什么是状态呢?我们可以回忆一下之前所介绍的流处理转换的这个过程啊,比如说这里面我们有一个处理任务,一个task数据输入之后,经过这个task的转换,可以得到一个输出,直接输出出来,诶当前的数据类型可能发生了变化,数据结构都发生了转换,但是呢,诶,整个这个数据的处理过程都是按照顺序一个一个进行处理的。在这个处理的过程当中,我们就会发现啊,有可能我们只跟当前的数据有关,哎,就像之前我们所说的map filter flat map,当前其实就是直接来一个做一个转换处理就行了,我们所要做的转换只基于当前的数据,别的任何的信息都不需要。
03:07
而对于有一些场景呢,啊,比如说之前我们所说的reduce,或者说简单的萨,或者m max,那这种场景在进行转换处理的时候就没那么简单了,比方说我们最简单的这样一个sum操作。如果说我们对于数据要进行一个求和统计的话,那很显然当前来了一个数据,我们并不能根据当前这个数据的内容就直接得到sum结果,我们还需要知道之前所有的数据才能完整的加出一个和来。哎,那所以在这个过程当中,我们需要怎么做呢?哎,那一种处理方式是我们可以把之前的数据全都攒起来啊,用一个列表啊,把它全放在这里,这种方式当然也是可以的,但这种方式有点像批处理嘛,或者说有点像窗口里边的全窗口函数的做法,那更好的方式是什么呢?其实就是诶,我们不需要把所有的数据都保存下来,只要保存一个之前。
04:05
已经来的所有数据的总和,我们把它作为状态保存下来就可以了,哎,所以这个东西这就是我们保存的一个状态。所以现在我们整个的处理流程就其实变成了这样的几部,首先当前的算子任务接收到从上游发过来的数据,然后接下来呢,诶,如果说我想做一个sum计算的话,那应该先从当前任务的对应的状态里边找到之前所有数据已经保存好的那个S和。先把这个状态读取出来,然后接下来呢,根据业务逻辑,我们要做一个求和计算sum,那就把当前数据里边想要求和的那个字段跟之前的sum结果做一个叠加累积,然后把这个计算出来,得到新的和,再保存到状态里面去更新状态,哎呢,得到对应的计算结果呢,我们又可以向下游发送出去,哎,所以我们看到整个的这个过程就涉及到了两部分内容,一个是接收的数据,另外一个是我们当前任务所保存的状态,诶,这就是所谓的有状态的流式计算。
05:22
那根据这样的一个操作流程的不同,我们也就可以把所有的算子分成有状态和没有状态的两种情况,因为我们看到对于这个map filter flat map而言,它的计算转换过程就不需要有任何其他的额外信息嘛,所以它也就不需要设置自己的状态,所以我们有时候会说这些是属于无状态的算子。啊,那对于reduce或者说窗口计算啊,那窗口计算我们知道不管是增量聚合还是全窗口函数,如果是增量聚合的话,那中间我们来一个就叠加一次,中间的那个聚合结果肯定是要保存在状态里的。
06:02
那如果是全装过函数呢?哎,全装过函数,我们之前所有到来的数据,那都得保存在窗口里边了,那我们用什么保存呢?当然也是用一个状态来保存了,所以只要是窗口算子,它也一定是会有对应的状态。另外就是如果我们在代码当中使用了底层的process function,哎,或者我们使用了自定义的复函数类rich function的话,其实也可以实现一些自定义状态的控制。比如说之前我们在实时对账的这个需求里边,就曾经自定义实现了一个Co process方式,在这个底层的处理函数里边,我们自定义了两个值状态value state来保存当前已经到达的支付事件。这就是一个非常典型的自定义状态的使用,我们是在process function里面定义的啊,那另外在第七章的时候,我们曾经讲到过top n有一种实现方法呢,我们实现的是一个K的process function,那么在里边我们想要把之前当前窗口啊,已经输出的每个URL对应的访问频次,我们统计出来的结果都要先保存下来,那这个时候拿什么来保存呢?我们自己也是定义了一个状态,这个是一个列表状态list state啊,所以在之前我们介绍的这个具体的案例当中,也已经实现过对于状态的控制,我们已经做过状态编程,哎,那所以状态这个概念对于我们来讲其实并不陌生。
我来说两句