00:00
我们已经了解了flink当中的基本转换算子,前面我们介绍了map filter和flat map啊,那整体来看的话,我们发现这些基本转换算子真的是在做一个转换操作,也就是说我们是基于当前的数据来一个就把它转换成另外一种形式,比如说map的话,那是一对一的转换,而如果是filter的话,有可能是一对一,也有可能是一对零,当然如果要是flat map的话,那就有可能是一对多了。那在实际的应用过程当中呢?很显然,我们对于数据的分析和处理不仅仅只是这样,每一个数据的一个转换操作,往往还要对大量的数据进行统计或者整合,然后提炼出更加有用的信息。比如说像之前我们在第二章当中介绍过的workout就是这样一个例子,我们当时在代码当中会发现,我们需要把当前所有的单词拆分出来,然后要统计每一个词出现的频率啊,所以在这个过程当中,我们后边的代码出现了。
01:06
分组聚合这样一个操作,哎,那对于flink而言,其实更多的操作我们都是要进行聚合操作的,这就是所谓的obligation,其实对于聚合操作我们并不陌生啊,在非常经典的大数据处理里边,我们都知道map reduce啊,所谓的reduce其实就是一个聚合操作啊,那在flink当中呢,如果说我们想要对数据进行聚合,首先先要进行一个按键分区的操作,这就是所谓的K败。啊,这主要就是因为在flink的data stream API当中,是没有办法直接调用方法去进行聚合的。我们在聚合之前必须先要做K败。这是为什么呢?呃,主要是因为我们当前在进行大数据的并行处理的时候,面对海量数据我们要聚合,肯定是需要进行分区操作啊,这就像在Spark当中,我们进行group by key一样,进行一个分区分组,这样才能够提高效率。
02:07
在flink当中,这个方法是基于data stream去调用一个K方法。所以我们可以认为KY就是聚合之前必须要用到的一个算子,那这个算子的主要作用就是要指定一个键K,然后我们就可以将一条流从逻辑上去划分成不同的分区。这里的分区我们就可以对应着并行处理的子任务啊,我们就可以认为每一个分区上的聚合操作都会放在一个单独的任务槽上去进行处理。我们可以看一下这张图,可以一目了然的知道K所做的操作。比如说我们这里所有的数据来了之后,经过一个KBY方法调用之后,那就会根据我们当前指定的K把数据从逻辑上分成不同的分支。比如说我们当前就是以每一个数据里边,假如有一个字段就表示当前这个方块的颜色,那么我们以颜色作为当前的K,当前我们看到浅色的方块就都会划分到上边这个分区来,那下边呢,呃,偏深一点的,或者说很深的颜色都会分配到下边这个分区来。
03:17
这里我们需要注意的是K的底层计算啊,因为我们直观上去想的话,既然它是要对数据进行分组分区,那我们自然想到是不是每一个K就对应着一个分区呢?呃,通过实际操作肯定我们发现不是这样的啊,因为我们在实际运行的时候,当前的资源,也就是slot数量是有限的,我们指定的并行度也是有限的。而数据里边的K的种类,很显然这个我们是没有办法预料的,很有可能我们数据里边啊,这个K是五花八门,比方说在实际应用过程当中,像我们之前所说的用户点击的这个E的事件。很可能我们就是以当前每一个用户的user。
04:00
用户名称作为当前的K啊,那么在这个过程当中,所有的用户他都是对应着一个不同的键值,难道说都要给他做一个分区吗?我们没有那么多资源,所以在实际的底层计算的过程当中,是要计算一个K的哈希值。然后呢,我们根据当前的分区数量做一个取模运算啊,那所以这里我们可以看到,如果这个K是po类的话,那必须还要去重写它的哈扣的方法,所以我们这里并不是每一个K都会对应分配到一个分区里面去,有可能出现什么情况呢?那就是两个不同的K分配到了同一个分区,因为它要做曲和运算嘛,我们当前只有系统只有两个分区啊,那么当前肯定会有不同的K放到一个分区来。我们这里可以确定的是同一个分区的数据,比方说我们当前K相同的这些数据,它一定都会分配到同一个分区里面去,而不同K的数据呢,有可能分配到不同分区,也有可能分配到同一个分区。
05:06
这就是K的一个基本概念,那在代码当中如果要调用的话,也非常简单,我们说就是基于一个data stream直接去调用KBY方法啊,这里我们可以在代码当中来做一个简单的测试。所以我们新建一个。Object啊,那接下来我们主要是测试这个聚合操作,Flink当中做聚合操作呢,必须先做K啊,所以我们就放在一起来做讲解,那就是transfer aggregation a test。接下来我们还是先把密方法写在这里。首先还是要创建当前的执行环境,Stream execution environment,创建出来get execution environment,然后当前得到的变量我们叫做env。同样还是上边把这个下划线引入,方便后边做影视转换,为了方便后边做测试,我们还是把全局的并行度先设置成一。
06:02
接下来我们还是借鉴之前的这个例子,直接把数据源copy过来。我们还是指定这样的几条数据,呃,像这个不同用户Mary Bob Alice的一些点击数据,我们还可以追加一些数据啊,比方说我们后边如果想要根据不同用户去进行分组的话,那比方说我们可以把这个Mary的数据。再来复制一条。后面还有一个Mary的点击事件。这里可能是点击了某一个商品的详情页。读入了data STEM之后,接下来那其实就是直接去调用一个KBY方法就可以进行分组了,我们可以看到这个KBY里边啊,它是有不同的传参形式的,首先可以直接传一个int类型的每一个字段的对应的索引号,而且我们看到后面有星号,也就是说当前这种方法可以给多个参数,参数是可变的。呃,不过呢,我们看到这种方式已经要被弃用了,那现在更加推荐的方法是什么呢?就是我们所说的在K里边直接传入一个key select,也就是我们所说的键选择器。
07:11
那键选择器又是一个什么样的东西呢?呃,下面我们可以看的非常的明显,所谓的键选择器最简单的实现就是直接传入一个拉的表达式给一个音频函数,放在这里就表示我们当前提取键的操作。当然了,对于这一个k select而言,在下边我们可以看到啊,它本身也是flink当中给我们提供的一个接口,这个接口里边呢,有唯一的抽象方法,就叫做get key。通过这个名字我们也可以看得出来,其实就是把当前的数据作为参数传入,然后呢,经过一系列的转换提取的方法,得到一个我们想要的键,然后进行返回,这就是我们当前分组的依据啊,所以在代码里边啊,我们可以用这种方式进行一个实现,比如说我们直接去new一个my key select啊,那当然了,下面我们需要对这一个my key select进行一个实现。
08:08
My key select。它其实是key select的。K select接口的一个具体的实现。我们把k select引入。当然了,这里我们发现它后面需要跟着有泛型,当前的key select,它的泛型是什么呢?一个是in,另外一个是K,这个其实看的很明显,一个就是当前输入数据的数据类型,另外一个就是当前提取出来键的类型啊,那对于我们当前而言,输入的数据当然是event类型了。那提取出来的键呢?假如说我们以当前的用户名user作为一个键的字段的话,那么对应的类型当然就是string了。里边必须要实现一个get key方法。这个get方法其实就是要返回我们当前想要提取的user嘛,所以直接把当前的in.user作为返回就可以了。
09:02
所以整体来看的话,我们当前所做的这个操作就是传入了一个提取键的操作,然后给T,这个方法得到的当前的数据流就变成了以user进行逻辑分流的一个数据流。那我们可以点进去看一下KY之后,得到的这个结果其实不再是data stream,而是变成了一个叫做KSTEM这样的一个数据类型,这个k stream在有些资料里边会翻译成键控流啊,或者叫按键分组流,按键分区流,它的特点呢,主要就是在原先data stream的基础上,指定了当前分组的K到底是什么。我们看到它后边同样这个泛型有两个对应的泛型参数啊,那我们可以看到一个是T,当然就是当前的数据流里边的数据元素的类型了,另外一个K就是提取出来的键的类型。另外我们看到它的这个类型声明的时候,这里它其实是继承自data stream啊,所以本质上来讲,我们K外之后得到的k stream也是一个data stream,接下来基于它的各种转换调用的还是data stream API啊,这就是我们所说的按键分区啊K的这个操作。
10:15
除了前面我们介绍的直接实现key select接口之外,前面我们还说了,另外可以直接在这里传入一个拉表达式,这种方式其实在实际的应用过程当中可能会更加的常见一点啊,因为更加方便啊。比如说我们这里想要使用user作为当前的这个按键分区的key的话,那么我们就可以直接写一个拉姆达表达式,提取当前的user就可以了。啊,我们也知道在SC当中可以用一个下划线,把后边这一部分做一个简写,所以实际上应用的时候,只要这么简单的一句话,就可以告诉我们当前分组的策略。当然了,我们也可以。按照其他的字段来进行分组,比方说我们用URL来做一个分组,这样也是可以的。
11:03
这就是关于K的具体操作。
我来说两句