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

如何解决分布式事务?说清楚一次!

时间:2023-03-14 17:20:50 科技观察

DistributedTransactionBasicTransaction事务是指一个操作单元,这个操作单元中的所有操作最终都必须保持一致的行为,要么全部操作成功,要么全部操作取消。简而言之,事务提供了一种“什么都不做或什么都做”的机制。本地事务本地事务其实可以认为是数据库提供的一种事务机制。说到数据库事务,不得不说数据库事务有四大特点:A:原子性(Atomicity),一个事务中的所有操作要么完成,要么不完成C:一致性(Consistency),在一个数据库中交易执行前后必须处于一致的状态。我:隔离。在并发环境中,当不同的事务同时对同一个数据进行操作时,事务之间不会相互影响。D:Durability,意思是只要事务成功结束,它对数据库所做的更新就必须永久保存。当一个数据库事务实现时,一个事务中涉及的所有操作都会被纳入一个不可分割的执行单元。所有的操作要么成功要么失败,只要任何一个操作失败,整个事务就会回滚。分布式事务分布式事务是指事务的参与者、支持事务的服务器、资源服务器和事务管理器分别位于不同分布式系统的不同节点上。简单的说,一个大的操作是由不同的小操作组合而成的。这些小操作分布在不同的服务器上,属于不同的应用。分布式事务需要确保这些小操作要么全部成功,要么全部失败。本质上,分布式事务是为了保证不同数据库中数据的一致性。分布式事务场景单体系统访问多个数据库一个服务需要调用多个数据库实例完成数据的增删改查多个微服务访问同一个数据库多个服务需要调用一个数据库实例完成数据的增删改查操作多个微服务访问多个数据库多个服务需要调用一个数据库实例来完成数据的增删改查操作分布式事务解决方案全局事务全局事务基于DTP模型实现。DTP是X/Open组织提出的一种分布式事务模型——X/Open分布式事务处理参考模型。它规定要实现分布式事务,需要三个角色:AP:Application应用系统(微服务)TM:TransactionManager事务管理器(全局事务管理)RM:ResourceManager资源管理器(数据库)整个事务分为两个阶段:Phase1:在投票阶段,所有参与者预提交交易的执行,并将成功与否反馈给协调者。Phase2:执行阶段,协调者根据所有参与者的反馈通知所有参与者,并统一执行commit或rollback。优点:提高了数据一致性的概率,实现成本低。缺点:单点问题:事务协调器宕机和同步阻塞:延迟提交时间,延长资源阻塞时间数据不一致:第二阶段提交时commit结果仍然存在不明情况可能导致数据不一致。基于消息服务的可靠解决方案是使用消息中间件来保证上下游应用数据操作的一致性。假设有两个系统A和B,可以分别处理任务A和任务B。这时有一个业务流程需要在同一个事务中处理任务A和任务B。你可以使用消息中间件来实现这个分布式事务。第一步:消息由系统A传递给中间件。系统A在处理任务A之前,首先向消息中间件发送消息。消息中间件收到消息后,将消息持久化,但不进行投递。持久化成功后,回复确认响应给A。系统A收到确认响应后,可以开始处理任务A。任务A处理完成后,向消息中间件发送Commit或Rollback请求。请求发送后,系统A的事务处理结束,如果消息中间件收到Commit,则将消息投递给系统B;如果收到回滚,则直接丢弃该消息。但如果消息中间件无法接收到Commit和Rollback命令,就必须依赖“超时查询机制”。超时查询机制A系统除了要实现正常的业务流程外,还需要提供一个事务查询接口供消息中间件调用。当消息中间件收到释放消息后,开始计数。若超时后仍未收到确认指令,则主动调用系统A提供的交易查询接口,查询系统当前状态。接口会返回三个结果,中间件会根据三个结果做出不同的反应:提交:将消息投递给系统B回滚:直接丢弃消息处理:继续等待第二步:消息由中间件投递B系统消息中间件向下游系统投递消息后,进入阻塞等待状态,下游系统立即处理任务,任务处理完成后返回响应给消息中间件。如果消息中间件收到确认响应,则认为交易完成。如果消息中间件等待确认响应超时,则会重新投递,直到下游消费者返回成功的消费响应。对于基于可靠消息服务的分布式事务,上半场使用异步,关注性能;下半年使用同步并专注于开发成本。BestEffortNotificationBesteffortnotification,也叫定期校对,其实是对第二种方案的进一步优化。它引入了本地消息表来记录错误消息,然后增加了对失败消息的定期校对功能,进一步保证消息会被下游系统消费。第一步:消息由系统A投递到中间件处理业务的同一个事务中,并在本地消息表中写入一条记录,以备专用消息发送者不断发送本地消息表中的消息给消息中间件。如果失败,重试第二步:消息由中间件投递给系统B。消息中间件收到消息后,负责将消息同步投递给对应的下游系统,并触发下游系统的任务执行.消息中间件反馈确认响应,消息中间件可以删除消息,从而完成交易。对于发送失败的消息,使用重试机制进行重试。对于重试失败的,将错误消息表写入消息中间件,需要提供失败消息的查询接口,下游系统会定时查询失败消息并消费。这种方式的优缺点:优点:非常经典的实现,实现了最终一致性。缺点:消息表会和业务系统耦合。如果没有打包的解决方案,就会有很多杂事要处理。TCC事务TCC是TryConfirmCancel,是一种补偿分布式事务。TCC实现分布式事务分为三个步骤:Try:尝试要执行的业务。该流程不执行业务,但完成所有业务的一致性校验,并预留执行所需的所有资源。Confirm:Confirm执行业务确认执行业务操作,不做任何业务检查,只使用Try阶段预留的业务资源。Cancel:取消要执行的业务取消Try阶段预留的业务资源。TCC两阶段提交与XA两阶段提交的区别在于:XA是资源层面的分布式事务,具有强一致性。在两阶段提交的整个过程中,一直持有资源锁。TCC是业务层面的分布式事务,具有最终一致性,不会一直持有资源锁。TCC事务优缺点:优点:数据库层二阶段提交交由应用层实现,避免了数据库层2PC性能低的问题。缺点:TCC的Try、Confirm、Cancel操作功能需要业务提供,开发成本高。SAGASAga是一种补偿协议。Saga模式下,一个分布式事务有多个参与者,每个参与者都是一个抵消的补偿服务。用户需要根据业务场景进行正向操作和反向回滚操作。补偿协议:在Saga模式下,一个分布式事务有多个参与者,每个参与者都是一个前向补偿服务。上图中T1~Tn为前向调用,C1~Cn为补偿调用,前向调用和补偿调用一一对应。假设有n个callee服务,T1是调用服务一,那么T2是调用服务器二,T3是调用服务器三。如果此时返回失败,则需要回滚。此时会调用T2对应的补偿C2,调用T1对应的补偿C1,使分布式事务回到初始状态。Saga的转发服务和补偿服务都需要业务开发者来实现,属于业务入侵。Saga模式下的分布式事务通常由事件驱动,在参与者之间异步执行。Saga模式是一种长期的事务解决方案。Saga模式使用场景Saga模式适用于业务流程较长,需要保证事务最终一致性的业务系统。Saga模式下,第一阶段会提交本地事务,在无锁和长进程的情况下可以保证性能。交易参与者可能是其他公司的服务或遗留系统,无法改造并提供TCC需要的接口,可以使用Saga模式。Saga模式优缺点优点:本地数据库事务一次性提交,无锁,高性能;参与者可以使用事务驱动的异步执行,高吞吐补偿服务是正向服务的“逆向”,简单易懂,易于实现;缺点:Saga模式不能保证隔离,因为第一阶段已经提交了本地数据库事务,没有进行“reserve”动作。SEATA2019年1月,阿里巴巴中间件团队推出了开源项目Fescar(Fast&EaS??yCommitAndRollback),其愿景是让分布式事务的使用像使用本地一样简单高效,解决分布式事务中的所有难点。后来更名为Seata,意思是:SimpleExtensibleAutonomousTransactionArchitecture,是一套分布式事务解决方案。Seata的设计目标是对业务无侵入,所以它从无业务侵入的2PC方案出发,在传统2PC的基础上演进。它将分布式事务理解为包含多个分支事务的全局事务。全局事务的职责是协调其管辖下的分支事务达成共识,要么成功一起提交,要么失败一起回滚。此外,通常分支事务本身是关系数据库的本地事务。Seata主要由三个重要组件组成:TC:TransactionCoordinator事务协调器管理全局分支事务的状态,用于全局事务的提交和回滚。TM:TransactionManager事务管理器,用于开启、提交或回滚全局事务。RM:ResourceManager资源管理器,用于对分支事务进行资源管理,向TC注册分支事务,报告分支事务的状态,接受来自TC的命令,提交或回滚分支事务。Seata的执行流程如下:服务A的TM向TC申请开启一个全局事务,TC会创建一个全局事务并返回一个唯一的XID。服务A的RM向TC注册一个分支事务,并合并到XID对应的全局事务中JurisdictionA服务执行分支事务,操作数据库。A服务开始远程调用B服务。此时XID会在微服务调用链上传播B服务的RM向TC注册分支事务,并包含在XID对应的全局事务中管辖B服务执行分支事务,全局事务调用链处理到数据库中。TM根据是否有异常向TC发起全局事务的提交或回滚。TC协调其管辖下的所有分支事务,并决定是否回滚Seata实现2PC与传统2PC的区别:在架构层面,传统2PC方案的RM其实是在数据库层,RM本质上是数据库本身,是通过XA协议实现的,而Seata的RM是以jar包的形式作为中间件层部署在应用端。在两阶段提交方面,传统的2PC直到Phase2完成后才释放事务资源的锁,而不管第二阶段的决议是commit还是rollback。Seata的做法是在Phase1提交本地事务,节省了Phase2持有锁的时间,提高了整体效率。