当前位置: 首页 > 科技观察

蚂蚁技术专家:一篇文章带你学习分布式事务

时间:2023-03-22 16:34:43 科技观察

Ant技术专家:一文带你了解分布式事务在近几年越来越火的微服务架构中,几乎可以说是必然。本文将介绍分布式事务的方方面面。1.事务1.1什么是事务数据库事务(缩写:transaction、Transaction)是指数据库执行过程中的一个逻辑单元,由有限的数据库操作序列组成。事务具有以下四个特性,习惯上称为ACID特性:原子性:事务作为一个整体执行,包含在其中的对数据库的操作要么全部执行,要么都不执行。一致性:事务应该确保数据库的状态从一种一致的状态变为另一种一致的状态。一致的状态意味着数据库中的数据应该满足完整性约束。此外,一致性还有一层语义,即无法观察到事务的中间状态(这一层语义也被称为原子性)。隔离性:当多个事务并发执行时,一个事务的执行不应该影响其他事务的执行,就好像数据库只在执行这一个操作。持久性(Durability):提交事务对数据库的修改应该永久保存在数据库中。在交易结束时,这个操作是不可逆的。1.2本地事务最初,事务仅限于对单个数据库资源的访问控制:架构服务化后,事务的概念扩展到服务。如果将单个服务操作看作一个事务,则整个服务操作只能涉及单个数据库资源:这种基于单个服务和单个数据库资源访问的事务称为本地事务(LocalTransaction)。2.分布式事务应用架构本地事务主要局限于单个会话,不涉及多个数据库资源。然而,在基于SOA(Service-OrientedArchitecture,面向服务的架构)的分布式应用环境中,越来越多的应用要求访问多个数据库资源,并且多个服务可以合并到同一个事务中。事业应运而生。最早的分布式事务应用架构非常简单,不涉及服务间的访问调用,只是服务内操作涉及访问多个数据库资源。当一个服务操作访问不同的数据库资源,并希望它们的访问具有事务特性时,就需要使用分布式事务来协调所有的事务参与者。对于上面介绍的分布式事务应用架构,虽然一个服务操作会访问多个数据库资源,但毕竟整个事务还是被控制在一个服务内。如果一个服务操作需要调用另一个服务,那么这个事务就需要跨越多个服务。在这种情况下,当从某个服务开始的事务调用另一个服务时,需要通过某种机制流向另一个服务,从而将被调用服务访问的资源自动添加到事务中。下图反映了这样一个跨越多个服务的分布式事务:如果将以上两种场景(一个服务可以调用多个数据库资源,或者调用其他服务)组合扩展,整个分布式事务的参与者将形成一个树形拓扑如下图所示。在跨服务的分布式事务中,事务的发起者和提交者是同一个人,可以是整个调用的客户端,也可以是客户端最终调用的服务。与基于单一数据库资源访问的本地事务相比,分布式事务的应用架构更加复杂。在不同的分布式应用架构下,实现一个分布式事务需要考虑的问题并不完全相同,比如多资源的协调、事务的跨服务传播等,实现机制也复杂多变.虽然要考虑的工程细节如此之多,但分布式事务的核心还是它的ACID特性。因此,要想了解分布式事务,首先要了解它是如何实现事务性的ACID特性的。下面将从最常见的两种分布式事务模型入手,重点分析分布式事务的基本共性点,即如何保证分布式事务的ACID特性。3、常见分布式事务模型ACID实现分析3.1X/OpenXA协议最早的分布式事务模型是X/Open国际联盟提出的X/Open分布式事务处理(DTP)模型,常简称为X/OpenXA协议,简称XA协议。DTP模型包括一个全局事务管理器(TM,TransactionManager)和多个资源管理器(RM,ResourceManager)。全局事务管理器,负责管理全局事务状态和参与资源,与资源一起提交或回滚;资源管理器负责具体的资源操作。XA协议描述了TM和RM之间的接口,允许在同一个分布式事务中访问多个资源。基于DTP模型的分布式事务流程大致如下:1.应用程序(AP,Application)向TM申请启动一个全局事务。2、对于要操作的RM,AP会先向TM注册(TM负责记录AP操作过哪些RM,即分支交易),TM通知对应的RM开始子交易通过XA接口函数实现分布式事务,然后AP就可以对这个RM管理的资源进行操作。3、AP完成对所有RM的操作后,AP根据执行状态通知TM提交或回滚全局事务,TM通过XA接口函数通知各RM完成操作。TM会先要求每个RM做pre-submission。所有RM成功返回后,他们将要求每个RM进行正式提交。XA协议要求一旦RM预提交成功,后面的正式提交也必须成功;如果有任何RM预提交失败,则TM通知每个RM回滚。4.所有RM提交或回滚完成后,全局事务结束。3.1.1原子性XA协议使用2PC(TwoPhaseCommit,两阶段提交)原子提交协议来保证分布式事务的原子性。两阶段提交是指将提交过程分为两个阶段,准备阶段(投票阶段)和提交阶段(执行阶段):准备阶段:TM向每个RM发送准备消息。如果RM的本地事务操作执行成功,则返回success;如果RM的本地事务操作失败,则返回失败。在提交阶段,如果TM收到所有RM回复的成功消息,则向每个RM发送提交消息;否则,它发送回滚消息;RM根据TM指令执行commit或rollback本地事务操作,释放事务处理中使用的所有资源。锁定资源。3.1.2隔离XA协议没有描述如何实现分布式事务的隔离,但是XA协议要求DTP模型中的每个RM都实现本地事务,即基于XA协议的分布式事务的隔离isolation由每个RM本地事务的隔离来保证。当一个分布式事务的所有子事务都隔离了,那么这个分布式事务自然就实现了隔离。以MySQL为例,MySQL采用2PL(Two-PhaseLocking,两阶段锁)机制来控制本地事务的并发,保证隔离性。2PL类似于2PC。它还将加锁操作分为加锁和解锁两个阶段,并保证两个阶段完全不相交。在加锁阶段,只加锁,不释放锁。解锁阶段只释放锁,不加锁。如上图所示,在一个本地事务中,每次执行更新操作之前,都会先获取对应的锁资源。只有成功获取到锁资源才会执行操作,一旦获取到锁资源,就会一直持有锁资源,直到本次事务执行结束。通过这种2PL机制,MySQL可以保证在本地事务执行过程中,其他并发事务不能操作相同的资源,从而实现事务隔离。3.1.3一致性如前所述,一致性有两层语义。一是确保事务执行完成后,数据库从一种一致状态变为另一种一致状态。另一层语义是无法观察到事务执行过程中的中间状态。前面一层语义的实现非常简单,通过原子性、隔离性、RM自身一致性的实现来保证。至于后一层的语义,我们先看看单个RM上的本地事务是如何实现的。仍然以MySQL为例,MySQL通过MVCC(MultiVersionConcurrencyControl,多版本并发控制)机制为每个一致状态生成一个快照(Snapshot),每个事务看到每个Snapshot对应的一致状态,这样它还确保不会观察到本地事务的中间状态。Snapshot虽然是在单RM上实现的,但是在分布式应用架构下会遇到哪些问题呢?如上图所示,在RM1的本地子事务提交和RM2的本地子事务提交之间,只能读取到RM1上的子事务执行的内容,不能读取RM2上的子事务。也就是说,虽然单个RM上的本地事务是一致的,但是从全局来看,观察的是一个全局事务执行过程的中间状态,破坏了全局一致性。XA协议没有定义如何实现全局快照。例如MySQL官方文档中推荐使用序列化隔离级别来保证分布式事务的一致性:“与非分布式事务一样,如果你的应用对读现象敏感,SERIALIZABLE可能是首选。REPEATABLEREAD可能不是足以进行分布式交易。”(对于分布式事务,可重复读隔离级别不足以保证事务一致性,如果你的程序有全局一致读需求,可以考虑序列化隔离级别。)当然,由于序列化隔离级别性能不佳,很多分布式数据库实现了分布式MVCC机制,提供全局一致读。一个基本的思路是用一个集中的或者逻辑上单调递增的东西来控制全局快照的生成,每次事务或者SQL执行都获取一次,从而实现不同隔离级别下的一致性。例如,Google的Spanner使用TrueTime来控制对全局Snapshot的访问。3.1.4小结XA协议通常在数据库资源层实现,直接作用于资源管理器。因此,基于XA协议的分布式事务产品,无论是分布式数据库还是分布式事务框架,对业务几乎没有侵入性,就像使用普通数据库一样。XA协议严格保证事务的ACID特性,能够满足所有业务领域的功能需求。然而,这也是一把双刃剑。由于隔离的互斥要求,在事务执行过程中所有资源都被锁定,只适用于具有一定执行时间的短事务。同时整个事务周期都是独占数据,对于热点数据的并发性能可能会很低。实现分布式MVCC或乐观锁(optimisticlocking)后,性能可能会有所提升。同时,为了保证一致性,要求所有RM同样可信可靠,要求故障恢复机制可靠、快速。在网络故障隔离的情况下,业务基本不可用。3.2TCC模型与XA等传统模型相比,TCC(Try-Confirm-Cancel)分布式事务模型的特点是不依赖于资源管理器(RM)来支持分布式事务,而是通过业务逻辑的分解。实现分布式事务。TCC模型认为,对于业务系统中特定的业务逻辑,当它对外提供服务时,必须接受一些不确定性,即调用业务逻辑的前期操作只是一个临时操作,主业务调用它的服务保留后续取消的权利。如果主业务服务认为应该回滚全局事务,会请求取消之前的临时操作,对应副业务服务的取消操作。而当主业务服务认为应该提交全局事务时,会放弃之前临时操作的取消权,对应从业务服务的确认操作。每一次初步操作最终都会被确认或取消。因此,对于一个具体的业务服务,TCC分布式事务模型需要业务系统提供三块业务逻辑:初步运行Try:完成所有的业务检查,并预留必要的业务资源。确认操作Confirm:真正执行的业务逻辑,不做任何业务检查,只使用Try阶段预留的业务资源。所以只要Try操作成功,Confirm就一定成功。另外,Confirm操作需要幂等,保证一个分布式事务只能成功一次。取消操作Cancel:释放Try阶段预留的业务资源。同样,取消操作也需要满足幂等性。TCC分布式事务模型包括三个部分:主业务服务:主业务服务是整个业务活动的发起者,服务的编排者负责发起和完成整个业务活动。Slave业务服务:Slave业务服务是整个业务活动的参与者,负责提供TCC业务操作,实现预备操作(Try)、确认操作(Confirm)、取消操作(Cancel)三个接口,调用由主营业务服务。业务活动管理器:业务活动管理器对整个业务活动进行管理和控制,包括记录和维护TCC全局事务的事务状态和各个从属业务服务的子事务状态,并在时从业务服务调用所有的Confirm操作业务活动提交,业务活动取消时调用所有从业务服务的Cancel操作。一个完整的TCC分布式事务流程如下:主业务服务先启动本地事务;主业务服务向业务活动管理器申请启动分布式事务的主业务活动;活动管理器注册从业务活动,然后调用从业务服务的Try接口;当从业务服务的所有Try接口调用成功后,主业务服务提交本地事务;如果调用失败,主业务服务回滚本地事务;如果主业务服务提交本地事务时,TCC模型分别调用所有从业务服务的Confirm接口;如果主业务服务回滚本地事务,则分别调用Cancel接口;从业务服务的所有Confirm或Cancel操作完成后,全局事务结束。3.2.1原子性TCC模型也使用2PC原子提交协议来保证事务的原子性。Try操作对应2PC的第一阶段准备(Prepare);Confirm对应2PC的第二阶段提交(Commit),Cancel对应2PC的第二阶段回滚(Rollback)。可以说TCC就是应用层的2PC。3.2.2隔离性TCC分布式事务模型只提供了两阶段原子提交协议来保证分布式事务的原子性。事务的隔离是由业务逻辑实现的。隔离的本质是控制并发,防止并发事务操作同一资源造成结果混乱。例如,在金融行业管理用户资金时,当用户发起交易时,一般会先查看用户的资金。如果资金充足,则扣除相应的交易金额,追加卖家资金完成交易。如果没有事务隔离,用户同时发起两个事务。检查两笔交易认为资金充足,但实际上只够支付一笔交易。结果,两笔交易都成功支付,导致资金损失。可以发现,并发控制是业务逻辑正确执行的保证,但是两级锁等并发访问控制技术要求数据库资源锁一直持有到整个事务执行结束,尤其是在分布式事务中建筑学。到分布式事务第二阶段结束,也就是说分布式事务会延长资源锁的持有时间,导致并发性能进一步下降。因此,TCC模型的隔离思想是通过初始阶段后的业务改造,从底层数据库资源层面的加锁过渡到上层业务层面的加锁,从而释放底层数据库锁资源,放松分布式事务.提高业务并发性能的锁协议。仍以上面的例子为例:第一阶段:检查用户资金,如果资金充足,则冻结用户当前交易资金,这笔资金被业务隔离,不允许除此交易以外的其他并发交易使用。第二阶段:扣除第一阶段预冻结的用户资金,增加卖家资金,完成交易。采用业务锁定方式隔离用户冻结资金,第一阶段后直接释放底层资源锁,用户与卖家之间的其他交易可以立即并发执行,无需等待整个分布式事务结束,且更高并发交易能力。3.2.3一致性我们来看一下TCC分布式事务模型下的一致性实现。类似于XA协议实现一致顶层的语义,通过原子性保证事务的原子提交,通过业务隔离控制事务的并发访问,从而实现分布式事务的一致性状态转换.至于二级语义:无法观察交易的中间状态。看看在SOA分布式应用环境下有没有必要。仍以会计服务为例。转账业务(用户A?用户B),由事务服务和记账服务组成分布式事务,事务服务为主业务服务,记账服务为副业务服务,记账服务的Try操作预冻结用户A的资金;Commit操作扣除用户A的预冻结资金,增加用户B的可用资金;Cancel操作解冻用户A之前冻结的资金,记账服务完成Try阶段后,可以提交交易主业务,然后TCC框架调用AccountingCommit阶段。在accountcommit阶段完成之前,用户A可以查看到自己的余额已经被扣除,但是此时用户B的可用资金并没有增加。从制度上看,确实存在问题和不确定性。在第一阶段执行结束和第二阶段执行结束之间,有一段延迟时间,在此期间似乎没有用户有权使用该资产。然而,从用户的角度来看,这个时间间隔可能无关紧要或者根本不存在。尤其是当这个时间间隔只有几秒的时候,对于专门沟通资产转移的用户来说,这个过程是隐蔽的或者说是确实可以接受的,保证了结果的最终一致性。当然,对于这样的系统,如果你真的需要检查系统的一致状态,你可以使用额外的方法来实现。一般来说,服务之间的一致性比服务内部的一致性更容易弱化,这也是为什么XA等直接在资源层面实现通用分布式事务的模型都注重一致性的保证。在层级,服务之间的功能划分和逻辑的解耦,更容易弱化一致性。这就是SOA架构下BASE理论的终极一致性思想。BASE理论指的是BA(BasicAvailability,基本业务可用性);S(Softstate,柔韧状态);E(Eventualconsistency,最终一致性)。该理论认为,对于可用性、性能和降级服务的需求,可以适当降低一致性要求,即“基本可用,最终一致”。在业界,严格遵循ACID的事务一般称为刚性事务;基于BASE思想的交易称为灵活交易。灵活事务并没有完全摒弃ACID,只是放宽了一致性要求:严格遵循事务后的一致性,事务中的一致性可以适当放宽;3.2.4总结TCC分布式事务模型的业务实现特点决定了它可以实现跨DB、跨服务的资源管理,通过TCC模型将不同DB、不同业务操作的访问协调成一个原子操作,解决分布式应用中的事务问题架构场景。TCC模型通过2PC原子提交协议保证分布式事务的原子性,将资源层的隔离性提升到业务层,交给业务逻辑实现。对于资源层,TCC的每个操作都是使用单个本地事务。当操作完成后,本地事务结束,避免了2PC和2PL下资源层占用资源带来的低性能问题。同时,TCC模型还可以根据业务需求进行一些定制化的功能,比如异步事务实现削峰填谷。但业务接入TCC模型需要将业务逻辑拆分成两个阶段,实现Try、Confirm、Cancel三个接口,定制化程度高,开发成本高。4.小结本文首先介绍了分布式事务的典型架构场景。分布式事务最初是为了解决单服务多数据库资源的场景而诞生的。随着技术的发展,特别是SOA分布式应用架构和微服务时代的到来,服务成为了基本的业务单元。因此,需要跨服务的分布式事务。然后,从两种常用的分布式事务模型XA和TCC入手,介绍其实现机制,重点分析各模型如何实现分布式事务的ACID特性。