前言
不知不觉距离上一篇的更新已经有几个月了,后台也有很多小伙伴催更,奈何事情比较多,期望我能坚持下去~
书接上话,前两篇我们聊了CAP的最佳打开方式,也讲透了分布式系统一致性的问题。最近几篇我们聊一聊分布式事务。
文章结尾附小米大数据平台运维工程师JD,有意者请与我联系~
二将军问题
我们先来看个故事,相信会有助于你对分布式事务的理解。二将军问题是一个计算机领域一个经典的问题。
「故事背景」黑白两军交战之际,两股白军将黑军被围困在山谷之中;山谷两侧任意一股白军都比山谷中的黑军人数少,因此单独一只白军向黑军发起进攻,必败无疑,但是两股白军人数总和却比黑军人数多,若能同时向黑军发起进攻,则可大胜。而山谷左右侧的白军相互通信(告知进攻时间)必然会穿过山谷,但是有被黑军俘虏的风险,如果你是山谷左侧的白军统帅,你会如何抉择呢?
「战况分析」
-
「情形1」:送信失败,如下图所示,左侧白军穿过山谷的时候被黑军俘虏了,右侧白军仍旧不知道左侧白军的进攻信息。
送信失败
-
「情形2」:送信成功,回信失败。如下图所示,左侧白军穿过山谷将信息成功通知给右侧白军,但是右侧白军携带"收到命令"的信息,穿过山谷向左侧白军通知的时候被黑军俘虏了,此时左侧白军不知道右侧白军是否接收到进攻命令。
送信成功,回信失败
-
「情形3」:送信成功,回信成功,如下图所示,白军胜利毫无疑问。
送信成功,回信成功
「战役复盘」
-
面对不稳定的信息传输通道(山谷中的黑军),要完成两股白军的通信是有难度的,送信回信的过程就好比我们系统中Send和Ack 的过程,并且只要送信或者回信任意一次失败,整个信息传递就标志着失败,无法完成进攻,这像不像CAP的最佳打开方式 中提到的原子性,事务中的每个操作都成功,事务才会提交。
-
如果真的发生了情形1和情形2,如果你是左侧白军的统帅或者右侧白军的负责人,你该怎么办呢?是不是应该等等,看看有没有回信,如果长时间没有回信就认为这个送信的人被俘虏了,再派1个人重复送信的过程,这就是我们在系统中常见的超时重试。当然
重试一定是有限制
的,如果真的无限重试。那么左右侧白军人数有可能会清零(资源耗尽),战争自然失败(系统自然崩溃) -
如果左侧白军派遣多名士兵同时发起送信的动作,那么对于右侧白军来说不管收到多少次信息,都只进攻一次。类比咱们系统来说,对于不管是一次请求还是多次请求,结果都是一样的,这就是
系统幂等性
-
这个二军问题,像不像TCP链接的握手的过程呢?问题来了,
为什么是三次握手,而不是两次握手?
文章留言回复下,如果你不知道可要补课啦
本地事务
本地事务实际上是指传统的事务,也就是大学课程里数据库原理中的提到事务。在CAP的最佳打开方式的文章中,我们也可以看到,ACID就是本地事务的最好诠释,原子性、一致性、隔离性、持久性,详细解释不再赘述,可以参看CAP篇中的含义。
当时我是用的午休去超市买苹果来举例,我们再简单重温下本地事务。当我在超市选好苹果去结账的时候,超市系统突然崩了无法结账,虽然选好了苹果,但也没啥用,虽然这次买苹果的事务自然而然也就失败了。但是对于超市来说,所有数据都不曾有任何变化,这就是本地事务。
分布式事务
我们沿着本地事务的思路继续深入,这次超市系统崩了的原因是超市发展很快,数据量的不断增长,导致后端单数据库性能已经达到了瓶颈,于是开始进行分库(分到多个物理实例)原来在单库中会操作多个表,由于不涉及网络传输,直接通过RDBMS的undo log、redo log以及锁机制就可以满足本地事务的所有需求了,但是分库后,意味着出现了跨库、跨网络的的事务场景。
举个例子,在分库前,优惠券逻辑使用的表A和支付逻辑相关的表B在同一个库中的,此时超市有苹果促销的活动,使用优惠券和支付逻辑是在同一个事务中,因此在操作支付时,如果支付失败,本地事务会强制将使用优惠券的操作也会失败。而由于分库拆分成两个独立实例,也就导致在跨网络的情况下,无法将单机事务在数据资源层面完美支持,因此介于此种情况,自然而然有了分布式事务的应用场景。
我们来明确下什么是分布式事务,通常分布式事务中存在两种角色
-
资源管理器(Resource Manager, RM)即事务参与者
-
事务管理器(Transaction Manager, TM)即事务协调者
在分布式事务的模型中,RM对应着资源,一个DB、一个被依赖服务都是一个RM;而TM 是一个全局事务管理器,一个TM会管理多个RM
,就类似上面我们的超市促销支付的逻辑去协调多方数据库资源,从而协调各个本地事务的进度,使其共同提交或回滚,最终达成一种全局的 ACID 特性
。
两种事务的区别
实际上与分布式系统CAP类似,分布式事务中涉及到的所有参与者会分布在同一个网络环境中,参与者会通过相互通信达到分布式系统的一致性,但因为网络环境的不确定性,网络不可避免的会出现超时、失败的情况,进而可能导致数据出现不一致的情况。而本地事务并不需要多次rpc,自然也就也就不存在分布式一致性问题,分布式一致性的核心点在于数据的分布式操作,导致本地事务无法保证数据的原子性。
举个实际的🌰,我们工作中经常会有独立负责一个项目的情况,自己会了解项目的所有逻辑,不需要和其他人交互。此时如果项目参与的人数增加到多个,成立了一个项目组,要想顺利的继续完成项目,就会导致你经常需要和项目组的其他人进行交流,保持目标/进度的一致,此时这个项目组就是分布式系统,每一个项目组成员就是分布式系统中的节点。由于沟通的不确定性,当某个模块废弃时,除了自己本地修改外,还需要通知所有人修改。这时如果沟通的不到位,就会出现不一致的情况。因此在分布式事务中会出现一个全局协调者的角色(TM),协调所有资源(RM)步调,保证全局一致。
分布式事务的场景
-
「资源主导」
上面超市买苹果的例子仅仅是举了一个由于分库而引发的分布式事务场景,我们针对这个场景向分布式系统上的延伸,如下图所示,当单点系统进行分布式化之后,实际上是由于资源的多点分布导致分布式事务场景的产生。
资源主导
-
「服务依赖主导」
下图展示的服务依赖主导,毕竟业务架构通常是比较复杂的,只需要一个服务访问一份数据资源的服务还是比较少的,随着SOA服务化理念的加深,特别是微服务的大规模应用,导致服务依赖非常复杂,因此这种分布式事务的场景完全由服务依赖所主导
服务依赖主导
-
「混合主导」
即数据资源分布+服务依赖主导,这里需要注意的是,不是说传统数据库和分布式系统就是泾渭分明的,在一个成熟的业务架构下,一会存在DB和分布式系统混用的情况。如下所示
分布式事务的分类
分布式事务从实现方案的类型上分为「刚性事务」和「柔性事务」。
刚性事务通常并不需要业务进行改造,实现的是强一致性,强一致性的同步阻塞导致服务并发上不去,比较适合短的事务。而柔性事务通常业务会参与进行改造,事务基本都是异步的,因此实现的是最终一致性,高并发,比较适合长事务。
换个角度看,结合CAP中的内容,刚性事务对应着分布式系统中遵循ACID的CA系统;柔性事务对应着分布式系统中遵循BASE的CP系统。
「往期原创推荐」
「往期最佳实践」