00:00
了解了flink里边的时间语义,主要有事件时间和处理时间两种时间语义,在实际使用的过程当中,我们往往更关心的是事件时间啊,这里还需要说明的一点就是在早期的版本里边。默认是处理时间,因为大家知道系统时间嘛,这个是最简单的啊,考虑到在实际应用的时候,往往还是要这个事件时间的,呃,就是以前的版本呢,我们还需要全局去去设置,就是在你要使用事件时间的话,那怎么用呢?在这儿啊NV。来看that stream timeistic,你要去做这样的一个操作,去设置当前的时间特性,时间语义,但是大家看现在这一个已经被被弃用了,就现在没有必要再去设置这样的时间语义了,默认就是出现时间。啊,那假如说这个我想用这个处理时间又怎么办呢?想要用处理时间也不需要去全局的设置时间语义,只需要在用到它的地方,你去定义对应的处理时间语义的窗口就可以了,啊所以现在的做法默认一点,12版本开始之后就是默认就是事件时间语义。
01:09
啊,那接下来我们就来说一说water mark这个概念啊,前面我们已经提到了,就是在这一个flink里边常见的是事件时间,但是我们怎么样去考虑一个窗口已经到点了呢?呃,比方说大家看到这个八点到九点这个窗口啊,我们想要统计所有的数据,那假如说正常来讲,我们的这个规则肯定就是说,诶,八点钟到了,那然后八点来的数据收集起来,对吧,08:05的数据肯定也是收集起来,十分的数据收集起来,只要在这个。一个小时之内的数据啊,全在这个窗口内,08:59:59的数据收集起来,然后接下来诶,到九点了,是不是直接要关啊,啊这个这个窗口就直接应该关闭了,然后输出所有数据的统计结果,这是我们能想到的窗口的基本应用。这里的最关键的一个问题就在于,你这个窗口关闭的九点钟到底以谁为标准呢?
02:06
之前我们说的,如果说是处理时间的话,非常简单,看系统时间到了九点就就关嘛,跟数据就没关系,对吧?啊,到点直接关了,里边有多少数据算多少啊,这个就有点儿像我们干什么呢?我们去赶班车的时候,赶班车的时候就是到点发车,正常来讲就是如果要是九点钟发的一班车,你上来多少人我都不管,司机那儿是有一个表的,只要到了九点钟,哎,我直接就开走了。因为一般情况下,我们在这个实际使用的时候是事件,事件就是因为你这个到点就发车呢,有可能就本来他应该是上这班车赶着要去某个目的地,他本来自己本身的那个时间戳啊,是八点到九点范围内的,但是呢,他有滞后啊,他赶的这个过程当中可能有一点耽搁,没赶上你这边就不等他直接发车走了。那这个就很悲剧,我们的数据是不是就收集少了呀,那大家自然想到了,那如果他真的要有这个,本来应该是。
03:03
发生在八点到九点范围内,结果来迟了的数据,那他会怎么办呢?那他就只能扔到下一个窗口了,就把它当成九点到十点的数据计算了,这种当然是不对的啊,那我们想要的就是说你属于哪个窗口,你是在什么时间发生的,就应该属于哪个窗口。啊,那所以我们要用这个事件时间,如果说这个事件时间的话,用赶车的例子,可能大家就会觉得有有点不太合适,因为人身上本身是没有这个时间戳这样一个标签的,那我们干脆换一个场景啊,我们来考虑这个。工厂生产出来的这个产品吧,工厂里面生产出来的产品都上面有一个生产日期,有这个生产的时间,就相当于给它盖了一个戳,然后呢,生产出来之后,我们要让这个产品去赶班车运到这个,呃,另外一个地方的仓库。相当于我们现在是做的这样一个操作啊,本身我这个班车应该是按照八点到九点生产出来的商品都应该上这班车,然后发车发送到对应的仓库里面去,但是现在呢,因为生产出来这个产品下生产线到运到这个车上,这是要一点过程的呀。
04:16
要有一个滞后,那假如说你用这个处理时间,那肯定你不等的话,那数据就就丢了嘛,或者说你就只能赶下一班车了,数据就错了,那怎么办呢?呃,那我们就不要直接看司机的那个表了,那以什么作为表呢?以产品上面自带的那个生产的时间戳为标准,诶,也就是说我看到了08:10:00的这个,呃,带着这个生产时间戳的产品到了这个车上,我看到有一个这个数据来了,我就知道哦,现在到08:10了。然后如果有一个08:59:59的这个商品,带着这个戳的商品来了,司机哦,就知道了,好,现在到08:59:59了。那什么时候发车呢?就是假如说这个时候来了一个九点整的数据,哎,那司机就知道了,好,现在所有的数据都到齐了啊,已经到九点钟了,我现在可以发车了。
05:11
相当于每个车上的司机还是得有一个自己的表的,只不过这个表呢,他不会自己走,他是按照当前我看到的。数据看到的这个产品上面的时间戳来不停的往前波动的。所以大家会发现了,就是在事件时间语义下,相当于flink自己给我们构建了一个逻辑时钟,为什么叫逻辑时钟呢?啊,就是它不是真正意义上的一个表,它是靠着数据驱动去推动它不停的往前走的,是你见到什么样的呃,时间戳,这个时间戳如果更新了的话,我就把这个时针往前拨动一下。那么前面有一个算子,可能他要做这样一个操作,是一个窗口的操作,那大家就会想到了,如果说下游我还有一个一个算子。
06:03
要接收这个数据,想要做对应的一些操作,假如说这里边也跟时间有关的话,诶大家就会想到了,你这个窗口是有问题的呀,你看八点这个数据来了之后。停在这儿了,它不会往下走,对不对,因为它窗口是要等到等到九点钟才会计算,才会有数据输出啊,所以八点之后停在这儿,8.05到这儿,停在这儿,只有我们前面这个窗口接收到这个数据的,这这这里的这个算子啊,这个任务知道当前的时间进展到08:10 08:15 08:59了,后面的任务他是不知道的。因为他接收不到数据啊。所以大家就想到了,你这个不能简简单单的就单纯的以这个数据作为标准来判断时钟啊,我们应该单独的有一个标志去指明当前这个系统里边,数据流里边时钟到底是怎么样前进的,而且呢,我需要把这个标志从前边的。
07:01
算子任务要传递到后边的算子任务里面去,即使我当前的这个数据不输出,没有任何输出结果,我也得把那个时钟的标志传递到下游,这样的话,下游任务就不用依赖它的这个数据里面的标签了,对吧,我我只要是,呃,这里有一个时钟的推移,你这里来了一个08:05啊,那我这里相当于就有一个时钟的标志,我就告诉下游现在是08:05了,尽管我没有任何的这个窗口的计算结果输出,但是你应该知道时间变化。这样的话,我们下游所有的任务都可以执行时间相关的操作,而不会等到九点钟才去做的。你如果要等这么久的话,这不就变成标准的批处理了吗?后续就都成批处理了。所以为了解决逻辑时钟在分布式系统前后发生的任务之间,它推移指明这个逻辑时钟前进的这个特点,在弗link里边我们给出了一个水位线这样的标记来指明当前逻辑时钟的进展。
08:05
这种东西在flink里边是有专门的一个类去实现的啊,这个类就叫做water mark,有些地方把这个water mark叫做水印啊,我个人认为还是叫水位线,可能会更加形象一点,因为本身我们认为这个就是数据流嘛,就像在水里边走一样啊,那这里边水里边可能流动的时候就有一个。随着数据一起流动,一起漂浮着的一个标记啊,这个标记我们就把它叫成水位线啊,这个还是挺直观的。在具体的实现上呢,水位线可以看成一个特殊的数据记录啊,它其实就是插入在数据流里边的一个标记点啊,那怎么样去插入呢?当然是要跟当前的某一个数据有关。它主要是用来指示当前事件时间,就即使是数据被拦住了,不往下发送了这个标记标签,这个当前的这个水位线也可以往下去发送,所以这样的话就起到指示事件时间进展的功能。
09:05
啊,那大家可以看到我们这里有一个非常简单的示例图啊,当前怎么样去发出水位线呢?大家看这里边的这个不是具体的数据啊,或者说这里边这个数据就是一个时间戳啊,我们这里假如说这个就是一个秒数吧,啊,我们就把这个当成秒数了,真正意义上的这个水位线的话,它其实里边的那个值应该是一个好秒数啊,我们这里把它当成秒,两秒钟来了一个数据,那在后边当前的这个任务当然就知道。两秒钟的数据,两秒钟的那个产品已经下线了,已经生产出来了,当然事件时间就已经进展到两秒了啊,所以我可以插入一个两秒钟的水位线,然后呢,五秒钟的数据来了之后,哎,那么我就在后面插入一个五秒钟的水位线,接收到这个水位线的任务,即使没有收到对应的这个数据啊,呃,就是被被拦住了,我也可以知道现在事件时间进展到五秒了。啊,所以大家看到整个流里边的效果就变成了这样一个过程,就成了数据带着水位线一起朝下游流动。
10:06
每来一个数据,就可以更新一下当前的水位线,插入一个新的水位线,表示事件时间更新了。这就是呃,水位线的基本定义。
我来说两句