00:00
我们已经了解了弗link当中状态的概念和分类,那接下来呢,我们就来分门别类的进行详细介绍。在我们前面介绍到的两种状态案件,分区状态kid state和算子状态operator state里边使用最多的当然应该就是kid state了啊,因为我们知道在实际应用过程当中,一般所要去进行的统计计算往往都是聚合操作哎,或者说基于一个窗口去进行一个窗口的统计,所以在这种情况下,我们都要先去做一个K办,然后接下来定义的状态当然就都是k state了啊。那所以我们之前所介绍到的按键分区流k stream上的聚合操作,或者说开窗口进行的计算,这些算子里边所包含的状态,所持有的状态都是这里的k state啊,所以接下来我们的介绍也是以k state为主。这是我们在应用当中。
01:00
用到最多的状态类型啊,那当然了,像之前我们已经介绍过的聚合算子,还有窗口算子,这里边的状态呢,弗林格已经给我们包装的很好了,我们也不用去过多的关心状态到底是怎么使用的,比如说之前我们在介绍窗口聚合函数的时候,增量聚合函数aggregate function这里,哎,我们知道它里边需要先去创建一个accumulator,一个中间计算结果的累加器,这个累加器其实就是我们创建出来的一种状态啊。那所以当时我们看到aggregate function里边三个类型参数,那input output,这是输入输出数据类型,中间这个ACC就是所谓的累加器类型,这就是我们的中间状态类型。另外或者说我们之前介绍聚合函数的时候,讲到了reduce啊,那我们知道reduce function式里边,这里边的参数是有两个的,我们当时的介绍是说这里定义的是一个规约的规则,哎,那就是来了一组数据,我们要定义这一组数据看成一个列表,两两进行规约,两两进行规约,那我们知道执行到后边的时候,其实第一个参数就已经变成了之前规约出来的一个聚合结果,哎,所以我们也可以把前面的这一个参数T啊,直接看成当前的一个聚合状态,哎,所以前面这就是一个状态,后边才是我们新到的当前新来的那个数据。
02:31
把它们做一个规约,就得到了我们新的状态。这是关于flink已经给我们提供的接口API里边我们能够感受到的状态的概念啊,那除了这些之外呢,前面其实我们也已经接触到啊,像在process方式里边,我们就曾经自己定义过这样的状态,呃,比方说像之前这个top n,我们其实呢,就是先做了KBY,然后调用了点process function法去自定义了一个k process function,所以在k process function里边,我们基于当前的运行之上下文去get一个对应的状态,那么获取出来的也就是当前的k state,因为我们是基于KY之后的k stream去进行的状态声明嘛。
03:18
这里我们能够使用k state的一个关键就是需要去获取当前的运行之上下文啊,在这个里边才有对应的get state get list state这样的一些方法,才能获取到状态的控制句柄,哎,那所以在什么地方可以使用调用get run time contact方法呢?诶,这就是之前我们说的啊,在process function里面当然是可以的,但是呢,诶,不仅仅是process function能调,其实本质上来讲这个方法它是。我们说reach function abstract function里边的一个方法。Get runtime context,所以只要我们实现了一个复函数类,哎,那即使不是process function,即使我们是reach map function reach filter function,同样可以获取到当前的运行时上下文,然后去自定义状态使用k state啊,这就是关于k state的基本的一个使用的过程啊,那关于k state呢,前面我们也提到了它的最重要的一个特点,或者说它的主要概念就在于数据对于状态的访问是以K作为作用范围的隔离的哈,什么意思?就是说我们当前如果说来了所有的数据,我们可以看之前的这张图,如果数据按照不同的并行子任务读取进来,然后传递到当前的一个并行子任务上的时候啊,那可能我们经过K外之后,现在接收到的数据就有不同的K,诶,那如果说我们在这个算子上定义一个状态的话,定一个k set的话,所有的数据访问到的是同一个状。
04:54
态吗?不是的,是按照K来进行划分的,比如说我们现在就以数据的这个颜色作为K进行一个划分的话,那我们就看到了浅色的数据来了之后,访问到的就是浅色的这一份状态啊,那如果是深色的数据来了之后,访问到的就是深色的这一份状态。
05:13
同样,当我们这个数据进行处理计算之后,如果引起了状态的更新的话,这个更新变化也只能更新自己对应的那一份状态,哎,所以当前的这个访问和维护状态的权限就完全局限在当前的K范围内了。哦,那对应的底层上我们就知道啊,就应该是用这样的一张类似于哈希表的结构,一个K一个value。哎,那比方说我们现在要做一个sum聚合统计的话,嗯,比方说我们现在的K就是ABC。这样的一些字母,那我们这里边进行sum这个状态保存的时候,如果ABC都对应的分到了我们第一个分区子任务的话,那么可能就是A对应着一个sum的结果。
06:06
萨一。B对应着一个结果SUM2 C对应着一个结果SUM3。他们就是完全分开的,这样的话,这就跟我们想要实现的需求完全一致了,我们本来就是要根据分开的K,各自统计各自的嘛,哎,所以这样的话,所有的需求就能顺理成章的按照我们的处理流程完成了。这里另外还需要说明一点的就是,哎,之前我们说在分布式的流处理系统当中,我们需要考虑状态,除了按照K的作用范围进行分割之外,还得考虑,哎,那假如说我们当前如果掉电怎么办呢?发生故障想要进行恢复怎么办呢?哎,那当前所有的状态,我们还应该能够把它做一个持久化的存盘保存啊,那这个机制呢,这就是之前我们说过的检查点机制,Checkpoint有时候就简写成为CK。
07:02
关于检查点,我们会放到后面再详细讲解,那另外还有一个问题,就是我们说现在是分布式的病情任务,哎,我们每一个任务里边呢,又都有很多个状态,假如说我们当前的这个并行都发生了调整,这个状态又应该怎么样重组呢?哎,其实我们发现啊,对于kid state而言,这个重组的过程非常的简单,比如说我们现在两个变成三个了。那怎么办呢?啊,那非常简单了,因为你现在每一个并行子任务里边保存的状态都不是一份嘛,都是按照K划分开的,相当于是保存在一个k value6这样一个哈希表里的,所以现在呢,我就按照不同的K把所有的数据都提出来,然后我们合在一起重新做一个调整,平均分成三份不就行了吗?哎,所以这种重组就会非常非常的简单,那当然了,实际应用的时候,我们的P可能非常非常的多,所以在弗Li的底层呢,它是用到了一种叫做建组的形式,就是所谓的key group。
08:09
这样的一种形式,哎,那么也就是说把很多个K对应的状态合并成一组,每一组k set,那么就会对应着一个并行的子任务。这样的话呢,当并行度发生变化的时候,我们其实就是调整建组里边k state的组合形式,那这样的话做一个平均分配就能保证调整并行度之后各个子任务负载还是相同的了啊,这就是关于k set的基本概念和它的特点。
我来说两句