00:00
那是不是说只要开启了检查点,那发生了故障之后,从检查点进行恢复结果就不会有任何的问题呢?当然没有那么简单。在实际应用当中,我们要保证是从用户的角度看,最终消费到的数据都应该是正确的,也就是说看到的数据都是处理了,都被处理了,而且只处理了一次。那如果说我们只是flink内部开启了检查点机制的话,那假如我们的数据源那边不重放数据,很显然后边我们的数据就会丢掉了,最终看到的结果就不对。哎,那或者还有就是说,假如我们前面的数据可以重放,但是呢,经过flink处理之后,得到的计算结果已经让已经写入到外部系统了,已经被用户消费掉了,那这个时候如果说我们在回滚,再重新做计算,再重新输出的话,那用户那边就会消费两次了。
01:01
诶,所以如果说我们想要保证真正意义上整个系统。完整链条的结果一致性是正确的话,那就相当于我们要不仅考虑flink内部数据的转换处理是对的,而且还要考虑从外部读取数据以及最终写入外部的持久化系统。所有的流程从头到尾都得是正确的。哎,那所以完整的处理流程呢,那就应该包含了数据源还有。中间的flink流处理器流处理的每一个任务,还有最终写入输出到外部系统三个环节,就是我们所说的端到端的状态一致性。那端到端的一致性级别取决于哪一个环节呢?那这自然就是木桶理论了。如果说我们三个环节里边任何一个环节有可能丢掉数据的话,那很明显我们最终就只能达到MOS。
02:06
那如果说所有的环节都能够做到不丢数据,而且数据只处理一次的话,很显然我们当前端到端就能够做到exactly one啊,所以整整个端到端的一致性级别取决于所有组件中一致性最弱的那个环节。那一般来说啊,能否达到at least once最至少一次,那其实主要是看数据源,因为数据源那边如果能够重放数据的话,诶,我们知道在内部我们只要开启了,那就肯定没有问题了,所以这样的话,至少能保证数据不丢,那就是at。而如果说我们想要达到完整的端到端one的话,诶,那就是每个环节都得有对应的保证。那接下来我们就要看所谓的端到端exactly ones,首先最简单的当然就是流处理器内部的保证,那就是开启checkpoint,这是我们最先能够想到的,那另外呢,S端还要去达到的要求,那S端呢,其实没有任何关于。
03:17
重复处理的限制,因为我们只是去消费数据嘛,对于我们最终用户看到的数据其实是没有什么影响的,所以S端只要能够保证能够重设数据的读取位置。或或者说就是像我们在卡夫卡里做的那样,能够重设,重新提交偏移量就可以了啊,那所以这样的话就至少保证整个环节是at least once,至少数据不丢。然后另外还有一个就是think端了。就是在最后我们得到的处数据处理结果要写入到外部系统的时候,也是每条数据都能够处理完了之后写入,而且只写入一次,诶这个过程其实就有一些困难了,因为前边我们的数据可以保证不丢重放数据,但是后边的处理结果呢,它是有可能重复写入的,因为我们在两次checkpoint进行保存的中间是有一段时间间隔的。
04:21
在这一段时间间隔的过程当中,我们继续会处理接下来的数据,这些数据处理的结果很显然是会正常写入到外部系统中的,哎,那如果说在中间的这个过程当中直接发生故障,那我们恢复的话是会恢复到上一个checkpoint保存的状态,接下来所有数据还要重放,那当然得到的结果就会重复的写入到外部系统当中了。那所以这个过程当中就相当于是把同一个数据写了两次嘛,我们其实只保证了端到端到at least once至少一次的一致性语义。
05:01
但如果说想要实现端到端的one这个最强级别的保证的话,诶,那我们就是。重点是要对think端。对于外部存储系统和。Flink的think连接器。都需要有额外的要求,那能够保证一致性的写入方式主要有这样的两种,那。这就是这里列出的一种是逆等性的写入,另外一种就是事物性的写入。那接下来我们就可以对于这两种机制做一个具体的介绍。首先是幂等性的写入,那所谓的密等操作,前面我们也我们也提到了,就是说同一个操作它可以执行多次,但是呢,结果只会有一次更改,也就是说一次操作之后,后面再来执行重复操作就不起作用了。这个我们其实非常的熟悉,在数学里边非常典型的一个例子就是针对E的X这样一个指数函数去做求导操作,哎,这里边小括号N指的是求N阶导啊,那我们知道本身E的X次求导还是自身,所以你不管求多少次导,得到的还是自身。
06:18
没有用啊,这里边就相当于对它进行求导,这个操作就是一个密的操作。而在计算机领域啊,数据处理领域,最典型的当然就是哈希表了。哈希曼,我们知道如果是相同的一个建制队的话。黑白的话。那么我们写入到哈希表的时候,是要基于当前K的哈希值去寻找它写入的位置,那所以如果当前是同一个key value,那当然它写入的位置是一样的,而而同一个位置呢,它的value又是一样的,那当然你重新写入一遍,跟之前还是一样啊,那所以后边的重复插入操作其实是没有任何的效果的。
07:02
那这就相当于说我们其实没有解决。让这个数据只写入一次的这样的一个,呃,这样的一个要求,没有做到这样的一个要求,而是说即使你重复的去写,随便去写吧,重复写入也没关系,最后的结果不会变啊,所以这种方式呢,它的主要限制是在于。对应我们写入的这个外部存储系统,它就必须是这样的一种密等写入的操作,比如说我们的数据刚好就是这样的k value,然后我们把它写入到k value的数据库里。比如说写到。啊,或者说我们按照PY的这个存储写入到其他的一些数据库啊,写入到MYSQL都是可以的,如果是这样的操作的话,那就相当于呃,我我们这个根据K去做了一个更新操作。重复的写入没有任何影响。所以这种方式其实是对外部存储系统。
08:02
要求会比较高,而对于我们当前写入到外部系统的这个sink算子呢,呃,其实没有什么要求,就是正常的写就行了,来了结果就写,来了结果就写。这里需要注意的是,对于这种密等写入操作啊,在遇到故障进行恢复的时候呢,中间是有可能会出现短暂的不一致的啊,那比如说我们就把这个写入的外部系统想象成是一个这里面有一个k value这样的一张表,我们要把它写进来。那如果说在这个过程当中,我们想要连续的写入数据的话,比方说。当前我们做这个写入的时候,是一连串逐步上升的。数据十,15。20、25、30。这样的一组一组数据依次写入,那如果我们在外部有应用啊,有消费者去。
09:00
呃,看观测当前这个redis里边存放的这个值的话,那就相当于会看到是一个逐步上升的。画出来的话,就是这样的一个动态的图表啊,那就是不停的上升,十,15 20 25 30。但是假如说如果中间出现故障呢,如果说我们在十这条数据处理完的时候,当前做了一个checkpoint。后边继续处理,15,二十二十五在遇到25的时候,当前发生了故障,还没有来得及进行检查点的保存,哎,那我们就知道,当然接下来。就会回退到十的这个状态,当前的对应存储的这个直径又变成了十。接下来又是十五二十,25 30,所以外边的用户他消费这个数据的时候,观测到的是什么样子呢?那是先是十十五二十,然后上升,接下来呢,突然又回退,突然跌落到了十。
10:05
然后接下来又是15 20 25 30,这样接下来才追上了之前故障之前的那个状态,然后继续去上升了。所以如果我们消费这个数据,画出对应的图表的话,就会有不一致的情况。当然了,最终的结果是正确的,就是如果说我们只是想读取当前的这个值的话,这是没有问题的,但是这个过程当中出现了短暂的不一致。啊,所以最终我们会发现这个密等写入啊,在有一些场景下还是有问题的,不能完美的解决伊萨once我们想要的这个目标。那怎么样可以解决这样的问题呢?那还有另外一种写入方式,就是所谓的事物写入。啊,那既然是事物性写入,我们自然就是要构建一个事物了。什么叫事物呢?可以简单的回顾一下,所谓的事物,那指的就是应用程序当中一系列严密的操作按照顺序发生的,那所有这些事物呢?所有这些操作呢,它构建成了一个事物,那就要求所有操作必须全部成功完成,如果要是里边某一个操作没有完成的话,诶,那当前事务里边的所有操作都会被撤销,都会被回滚。
11:25
啊,这就是我们所所说的这个事物,它是具有原子性的一个事物当中的一系列操作,要不全部成功,要不就全部回滚,一个都不做。就是最典型的当然就是我们比较熟悉的银行转账这个操作了,我们知道对于计算机系统而言,银行转账当然非常简单了,其实就是数据库里边的。一加一减两个操作啊,那当然最先应该是减去我们当前要转账的这个账户里边,比方说要转100块钱,那就是这个账户查出来之后,先减100,然后把要转给的那个账户查询出来,然后再加100。
12:05
做两次查询和更新的操作,哎,那这样的话我们就做完了,但是这一减一加两次操作呢,它必须捆绑在一起。要不全部发生转账成功,要不全部不发生转账失败,因为如果我们不捆绑在一起的话,就有可能出现我刚刚这边做了减掉100的这个操作,接下来掉电或者发生故障了。那就会发现当前账户净少了100,但是转账没有转过去,当然这个就不对了,所以我们的要求就是他应该要构建一个事物,要不都成功,要不都失败。那我们现在如果说要在容错机制里边,整个这个端到端要保证状态一致性的exists,保证精确一次的话,那它的实现思思想可以借用事物性写入的这个思想。简单来说就应该是我们构建一个事物,它跟checkpoint检查点是完全对应的。
13:08
因为我们不是说如果要写入到外部系统,但是这个时候没有做完检查点的话,诶,那相当于当前写入的有些数据之后重放就会就会重复写入吗?诶所以呢,我们自然就想到了,我就不要着急把当前的数据。直接入到外部系统。而是等到做完的时候才把当前。这个checkpoint里边包含的那些数据对应的结果写入到外部系统,那具体来说的话,那就是我们首先先构建一个事物,然后接下来就要就要看这个bar瑞尔了。我们知道这个最终的think任务。也是要在接收到barrier的时候才会去把当前的状态去做checkpoint的保存,那这个时候如果说来了数据已经处理完了,Think系统已经think任务已经处理完了,是直接输出到外部系统吗?这个时候显然不能。
14:12
我们需要构建一个事物,把它放在当前这个事物里面。而这个事物什么时候真正提交呢?那就是需要等到当前的checkpoint真正的完成了的时候,然后再把所有这个事物里边的结果提交到外部系统当中。这样的话。如果说发生故障要回滚的话,那就。如果中间这些数据。在在写入的过程当中发生故障了,那我们就知道这个事件直接撤销,所有的数据就不会写入到外部系统,那如果当前这个检查点已经正常保存进去的话,所有数据也写入到外部系统的话,那之后回滚,回滚到的就是当前所有数据都处理完的那个状态,那当然所有数据也都已经写入进去了,这样的话,每个数据。
15:08
都会被写入,而且都只被写入一次。这就实现了我们真正意义上的。
我来说两句