1 详解分布式事务
在分布式系统中,由于数据分布在多个节点上,因此需要确保跨多个节点的操作能够保持一致性,即保证事务的 ACID 特性(原子性、一致性、隔离性、持久性)。
2 分布式事务的几种解决方案
2.1 2PC
2.1.1 XA
XA 是一种用于分布式事务处理的标准接口。它定义了一组协议,使得应用程序能够在多个独立的事务性资源(例如数据库、消息队列等)上执行分布式事务。XA 接口定义了两阶段提交(Two-Phase Commit)协议,这是一种经典的分布式事务协议,用于确保跨多个资源管理器(例如数据库)的事务的原子性和一致性。
在 XA 协议中,事务管理器(Transaction Manager)负责协调各个资源管理器(Resource Manager)的事务,以确保整个事务的一致性。XA 接口定义了一系列操作,包括开始事务、准备提交、提交事务、回滚事务等,以及在分布式事务中发生故障时的恢复机制。
通过使用 XA 接口,应用程序可以在分布式环境中实现跨多个资源的原子性操作,从而确保数据的一致性和可靠性。
2.1.2 AT
AT是Automatic Transaction的缩写,意为自动事务。在分布式系统中,AT模式是一种实现分布式事务的方式之一。
在AT模式中,分布式事务的管理和控制由事务发起方(一般是应用服务器)来完成,而不是交由全局事务协调器(如XA协议中的协调器)来处理。具体来说,AT模式下的分布式事务一般分为以下几个阶段:
事务发起:事务的发起方发起一个分布式事务,并在本地数据库中开始一个事务。
本地事务操作:在事务的执行过程中,各个参与者(微服务或数据库)执行本地的事务操作,包括读取、写入数据等。
一阶段提交:在事务的一阶段结束时,事务发起方会向各个参与者发送提交请求。参与者在接收到提交请求后,会执行一阶段提交,并在本地提交或回滚事务。这一阶段的目的是确保所有参与者都可以提交事务,并且在下一阶段进行最终提交之前保持一致。
最终提交:在所有参与者都成功执行了一阶段提交后,事务发起方会发出最终提交请求。参与者在接收到最终提交请求后,会确认并最终提交事务,从而完成分布式事务的执行。
AT模式相对于XA协议来说实现机制更加简单,因为它不需要全局事务协调器来管理分布式事务的执行。然而,AT模式也可能存在一些问题,例如在一些异常情况下可能会导致事务的不一致性,因此在选择分布式事务模式时需要根据具体的业务场景和需求来进行权衡。
AT如何保证数据安全
本地锁和全局锁:在AT模式下,全局锁和本地锁通常是用来确保事务的一致性和隔离性的关键机制。本地锁用于在事务的一阶段中保护参与者的本地数据,而全局锁用于在事务的提交阶段确保全局事务状态的正确性。
本地锁一般指的是第一阶段数据库层面的行级锁等等,由RM自行进行管理,本地锁的存在使得我们分布式事务中队数据库的操作在本地保证数据安全。
全局锁是整个分布式事务逻辑的锁,由TC进行管理,每一次分布式事务都会分配一个全局锁,它的存在使得多个相同的分布式事务逻辑不会同时执行,从而保证分布式系统中数据安全。
2.1.3 TCC
TCC(Try-Confirm-Cancel)是一种分布式事务处理模式,旨在解决分布式环境下的事务一致性问题。与传统的ACID事务模型(原子性、一致性、隔离性、持久性)不同,TCC模式强调了更加灵活的事务处理方式,特别适用于微服务架构和分布式系统中的复杂场景。
TCC模式由以下三个阶段组成:
尝试(Try):在尝试阶段,事务参与者会尝试执行事务,预留必要的资源和状态,并检查执行事务的前提条件。尝试阶段不会对数据进行实际修改,而是做一些预处理的准备工作。
确认(Confirm):在确认阶段,事务参与者会确认事务执行,将之前预留的资源和状态转换为实际的业务数据。确认阶段会提交事务,将事务操作的结果永久保存。
取消(Cancel):在取消阶段,事务参与者会撤销或回滚之前尝试阶段所做的操作,释放之前预留的资源和状态。取消阶段用于处理尝试阶段的任何失败或异常情况。
TCC模式的优点包括:
灵活性:TCC模式允许开发者根据业务逻辑定义事务处理的各个阶段,从而提供更灵活的事务控制。
可扩展性:TCC模式适用于复杂的分布式系统和微服务架构,可以轻松实现跨服务的事务处理。
高可用性:TCC模式在处理分布式事务时可以更好地应对系统故障和异常情况,提供更高的可用性。
然而,TCC模式也有一些缺点,例如实现复杂度较高,需要开发者自行处理事务的一致性和异常情况等。因此,在选择事务处理模式时,需要根据具体的业务需求和系统架构来进行评估和选择。
TCC如何保证数据安全
TCC模式中的每个阶段(尝试、确认、取消)都是事务性的操作。在尝试阶段,事务参与者会预留资源和状态,但并不会对数据进行实际修改,直到确认阶段才会将操作结果永久提交。如果事务执行失败或被取消,参与者会在取消阶段将之前的操作回滚,以确保数据的安全性。
TCC设计的初衷是提供强一致性,但由于多个分布式事务执行无阻塞、二阶段失败、网络延迟等,使得它不得不降级为最终一致性。
2.2 3PC
三阶段提交(Three-Phase Commit,3PC)是一种用于分布式系统中实现事务的一致性和可靠性的协议。它是两阶段提交(Two-Phase Commit,2PC)协议的改进版本,在解决了2PC协议的部分缺点的基础上,进一步提高了系统的可用性和容错性。
三阶段提交协议由以下三个阶段组成:
CanCommit(准备阶段):在这个阶段,事务协调者(Coordinator)向所有参与者(Participants)发出CanCommit请求,询问它们是否可以执行提交操作。如果所有参与者都确认可以提交,则事务协调者会进入PreCommit阶段;如果有任何一个参与者拒绝提交,则事务协调者会进入Abort阶段。
PreCommit(预提交阶段):在这个阶段,事务协调者向所有参与者发送PreCommit请求,要求它们执行事务的预提交操作,并在准备好之后等待最终的确认。如果所有参与者都成功预提交,则事务协调者会进入DoCommit阶段;如果有任何一个参与者在此阶段失败,则事务协调者会进入Abort阶段。
DoCommit(执行提交阶段):在这个阶段,事务协调者向所有参与者发送DoCommit请求,要求它们执行事务的最终提交操作。如果所有参与者都成功执行提交,则事务协调者会发送全局的事务已提交的通知;如果有任何一个参与者在此阶段失败,则事务协调者会发送全局的事务已中止的通知。
三阶段提交协议相对于两阶段提交协议的优点包括:
减少阻塞时间:三阶段提交协议中的准备阶段和预提交阶段可以并行执行,从而减少了整个协议的阻塞时间。
减少潜在的阻塞点:三阶段提交协议中的预提交阶段将阻塞点从准备阶段移动到了预提交阶段,减少了潜在的阻塞点。
增加容错性:三阶段提交协议引入了预提交阶段,可以在事务提交之前检测到参与者的故障,并在预提交阶段将其排除,从而提高了系统的容错性。
然而,三阶段提交协议也并不是没有缺点的,例如在网络分区或参与者故障时可能导致协议无法完成,从而影响系统的可用性。因此,在选择分布式事务协议时,需要根据具体的业务需求和系统架构来进行评估和选择。
2.3 Saga
Saga是一种分布式事务处理模式,旨在解决分布式系统中的事务一致性问题。Saga模式将长时间运行的事务分解为一系列的短事务(称为子事务),并通过一系列的补偿操作来实现事务的一致性。
Saga模式由以下几个关键要素组成:
分解为子事务:长时间运行的事务被分解为一系列的短事务,每个短事务都是原始事务的一部分,负责执行一部分操作。
顺序执行:Saga模式要求子事务按照预定义的顺序依次执行,确保事务的正确执行顺序和一致性。
补偿操作:每个子事务都关联有一个补偿操作,用于撤销或者回滚之前的操作。如果某个子事务执行失败,后续的子事务将会执行相应的补偿操作,以确保事务的一致性。
事务状态管理:Saga模式需要跟踪和管理事务的状态,包括事务的开始、执行、完成、失败等状态,以确保事务的正确执行和一致性。
Saga模式的优点包括:
可扩展性:Saga模式适用于大规模和复杂的分布式系统,可以轻松实现跨服务和跨组织的事务处理。
容错性:Saga模式通过补偿操作可以有效处理事务中的失败和异常情况,提高了系统的容错性。
降低锁竞争:Saga模式将长时间运行的事务分解为短事务,减少了事务的锁竞争,提高了系统的并发性能。
然而,Saga模式也存在一些挑战,例如事务状态的管理和一致性的维护较为复杂,需要开发者自行处理事务的异常和补偿操作等。因此,在选择事务处理模式时,需要根据具体的业务需求和系统架构来进行评估和选择。
Saga的优势
适用于业务流程长且多,支持包含外部接口或非关系型数据库,Saga为最终一致性的。
2.4 MQ分布式事务
2.4.1 MQ事务消息
RocketMQ的事务消息是一种在分布式消息系统中提供事务性消息处理的机制。它允许发送方在发送消息时标记为事务性消息,然后通过执行事务性的本地业务逻辑来确认或回滚这些消息。
发送半消息:应用程序向RocketMQBroker发送一条半消息,该消息在Broker端的事务消息日志中被标记为prepared”状态。
执行本地事务:RocketMQ会通知应用程序执行本地事务。如果本地事务执行成功,应用程序通知RocketMQBroker提交该事务消息。
提交事务消息:RocketMQ收到提交消息以后,会将该消息的状态从“prepared”改为“committed”,并使该消息可以被消费者消费。
回滚事务消息:如果本地事务执行失败,应用程序通知RocketMQBroker回滚该事务消息,RocketMQ将该消息的状从“prepared”改为“rollback”,并将该消息从事务消息日志中删除,从而保证该消息不会被消费者消费。
若收到半消息就没后续了怎么办?
如果半消息接受成功后,规定时间内为接受COMMIT和ROLLBACK,MQ会发送检查消息,如果没收到结果会将该消息标记为UNKNOW状态,若是过期时间内还是未收到结果,则会删除该事务消息。
为什么要先发送半消息?
主要是因为:本地事务执行完成之后再发送消息可能会发消息失败。 一旦发送消息失败了,那么本地事务提交了,但是消息没成功,那么监听者就收不到消息,那么就产生数据不一致了。 那如果用事务消息。先提交一个半消息,然后执行本地事务,再发送一个commit的半消息。如果后面这个commit半消息失败了,MQ是可以基于第一个半消息不断反查来推进状态的。这样只要本地事务提交成功,最终MQ也会成功。如果本地事务rolllback,那么MQ的消息也会rollback。保证了一致性。
2.4.2 可靠消息最终一致性
这个方案的主要思想是将分布式事务拆分为本地事务和消息事务两个部分,本地事务在本地数据库中进行提交或回滚,而消息事务则将消息写入消息中间件中,以实现消息的可靠投递和顺序性。 一般来说的做法是,在发送消息之前,先创建一条本地消息,并且保证写本地业务数据的操作和,写本地消息记录的操作在同一个事务中。这样就能确保只要业务操作成功,本地消息一定可以写成功。然后再基于本地消息,调用MQ发送远程消息。消息发出去之后,等待消费者消费,在消费者端,接收到消息之后,做业务处理,处理成功后再修改本地消息表的状态。
优缺点
优点:
可靠性高:基于本地消息表实现分布式事务,可以将本地消息的持久化和本地业务逻辑操作,放到一个事务中执行进行原子性的提交,从而保证了消息的可靠性
可扩展性好:基于本地消息表实现分布式事务,可以将消息的发送和本地事务的执行分开处理,从而提高了系统的可扩展性。
适用范围广:基于本地消息表实现分布式事务,可以适用于多种不同的业务场景,可以满足不同业务场景下的需求。
缺点:
实现复杂度高:基于本地消息表实现分布式事务,需要设计复杂的事务协议和消息发送机制,并且需要进行相应的异常处理和补偿操作,因此实现复杂度较高。
系统性能受限:基于本地消息表实现分布式事务,需要将消息写入本地消息表,并且需要定时扫描本地消息表进行消息发送,因此对系统性能有一定影响。
会带来消息堆积扫表慢、集中式扫表会影响正常业务、定时扫表存在延迟问题等问题。
2.4.3 最大努力通知
所谓最大努力通知,换句话说就是开不保证100%通知到。这种分布式事务的方案,通常也是借助异步消息进行通知的。
发送者将消息发送给消息队列,接收者从消息队列中消费消息。在这个过程中,如果出现了网络通信故障或者消息队列发生了故障,就有可能导致消息传递失败,即消息被去失。因此,最大努力通知无法保证每个接收者都能成功接收到消息,但是可以尽最大努力去通知。