===================
最终一致性,一言以蔽之,过程松,结果紧。不管中间过程如何,结果必须符合业务需求,满足数据一致性的要求。
虽然,在实现中,有各种花样百出的方案,但是本质的思想都是一样的。我们现在就来忽略那些乱花迷眼的过程,仔细探讨下最终一致性的本质。
何事居穷道不穷,乱时还与净时同
===================
在我刚入行不久的时候,能力有限,菜鸟一个,只能做一些小的功能模块。我印象最深的就是订单模块。
用户下单,订单模块收到下单请求后,执行对应的订单业务逻辑。最终,会把订单插入到订单表,并返回下单结果给用户。用户结算后,订单模块就会去根据支付情况去更新订单状态。
就这点事儿,对我这个技术渣渣来说,开始也着实费了一番手脚,不过最终也成了熟手,维护起这个模块来也驾轻就熟了。
这种简单的小日子过了一阵子后,新任务来了!
产品经理告诉我,数据审计部门想要我维护的这个订单模块在订单完成后,能及时分发一份订单数据给他们。他们提供了一个接口,让我直接传数据给他们。
两个问题出现了:
问题 1:用户等待时间变长
最简单的实现就是我更新完订单数据后,再顺序去调用数据审计部门给的接口,把订单数据传过去。
但是,从用户结算成功到更新订单状态这一系列的流程是同步的,假设这一系列流程所花费的时间是 n 毫秒。这就意味着,用户需要等待至少 n 毫秒。如果再加上传给数据审计部门的操作时间,假设为 m 毫秒,则整个用户就需要等待就 n+m 毫秒。
整个功能用户等待时间成本上升,体验下降。如下图:
问题 2:部分成功,部分失败
引入新的接口后,某些时候调用这个接口可能会失败,比如网络问题啊,验证问题啊,接口服务失败啊,很多原因。那么问题来了,新接口失败的时候怎么处理?
如果订单更新成功,传给数据审计部门的时候失败了,这种情况会让订单模块的后续处理变得很尴尬。
首先你不可能返回给客户端说你这次结算失败了,请求就没失败,你凭什么说人家失败了?其次,你又不能说这次业务上就是成功的,因为数据审计其实还挺重要的,它是业务逻辑的重要组成部分。
真是进退两难。
这两个问题的解决方案其中之一就是最终一致性。
我们以前谈到过 CAP,知道如果牺牲一定的一致性就可以保证分区容错性和可用性。而最终一致性则是不能保证同时让所有的数据当时都符合业务需求,但是我们能保证任何时候服务在内部出现问题的时候都是可对外服务的。
四哥我平时喜欢玩游戏,那我们就用一个淘宝买 Switch 的例子,来解释最终一致性:
如果你想在淘宝同时买一个 Switch 的数字版游戏和一台 Switch,那么你付完钱后,你就可以立刻得到数字版的游戏,但是,对于那台购买的 Switch,你就要等几天,等到快递投递到家才可以拿到。
来梳理下这个例子的细节:
-
首先淘宝上肯定得有个对顾客售卖 Switch 和数字游戏的商家去接受我们下的订单,并给你一个单号。
-
你得到了一个数字版游戏,但是没拿到 Switch。
-
你不知道这个商家背后 Switch 是怎么给你准备的,是不是中间他没货了还得跑别的商家串货,又或者没货等了两天才发给你(延迟发货可以给出别的理由,不再赘述)。这些不重要,重要的是你明确对方接单了他就要完成这笔单子。
-
你下单成功之后,你就有了保障,你最终会拿到你的 Switch,只是你可能不太肯定什么时候收到。
过了几天,你终于收到货了,恩,恭喜你成功入坑 Switch。
上面的例子就是我们说的最终一致性。但是,这里有个非常非常重要的东西还没有凸显出来,即到底是什么样的原因在驱使我们使用最终一致性?
答案就是数据的分发。
纸上得来终觉浅,绝知此事要躬行
===================
为什么我们会出现需要最终一致性的情况呢?
因为我们需要把数据分发到不同的地方上去,而由于分发数据到不同的地方,就会导致,可能中间分发过程中出现分发成功或者失败的不一致情况,就需要最终一致性这种思路来处理这些情况。
恩,分发数据……OK,你想到了吧?