分布式事务是指在分布式环境下,对多个数据资源进行并行操作时,需要保持数据资源之间的一致性和完整性,同时具有ACID的特性。在分布式系统中,由于存在多个数据存储节点、网络延迟、可用性等问题,导致分布式事务的实现相比较于单节点事务要更加复杂和困难。
常见的分布式事务解决方案包括
- 两阶段提交(Two-Phase Commit, 2PC):事务协调者(TC)首先向所有参与者(P)发出“预提交”(prepare)请求,各参与者对消息进行确认操作。如果所有参与者都能正常执行,则协调者向参与者发出“正式提交”请求,各参与者将事务提交到本地数据库,返回确认消息。如果该过程中出现任何一个参与者无法提交,则会进行回滚操作,使得全局事务回滚到最初的状态。
- 补偿事务(TCC):TCC事务将一个事务以尽可能简单的形式分解为三个步骤——试着去执行它,检查是否可以提交,如果不能提交,则回滚已经执行的动作。在TCC事务中,需要实现try、confirm和cancel三个接口,通过调用confirm、cancel接口实现提交和回滚操作。
- Saga事务:Saga是一种支持可撤销和部署在多个实例之间的长事务处理模式。Saga事务将长事务拆分为多个小事务,每个小事务之间不再强制保证强一致性,而是通过异步消息通信的方式进行顺序化处理,从而降低了系统的复杂度和开发成本。
- 消息队列事务:在消息中间件的支持下,将消息的生产者和消费者进行事务性合作。
以上解决方案都有其适用场景和实现细节,需要根据具体的业务需求进行选择。同时,在应用分布式事务时还需要考虑到数据一致性、可靠性、性能、可扩展性等方面的问题。
两阶段提交
两阶段提交(Two-Phase Commit,2PC)是实现分布式事务的一种较为传统的方式,它通过引入一个协调者(Coordinator)来协调多个参与者(Participant)的提交行为,从而保证分布式事务的原子性和一致性。
2PC的过程如下所示:
阶段一:
- 协调者向所有分布式事务参与者发送“canCommit”请求。
- 各参与者接收到请求后,会相应的执行数据准备工作。如果参与者在执行准备工作时发现有不可处理的错误,则向协调者发送“NO”响应。
- 如果所有参与者都成功的执行了准备工作,则向协调者发送“YES”响应。
阶段二:
- 如果协调者在阶段一收到了所有参与者的“YES”响应,则向所有参与者发送“doCommit”请求。参与者收到请求后执行事务提交,并向协调者返回“ACK”表示提交成功。
- 如果协调者在阶段一或二的任一阶段收到了任何一个参与者的“NO”响应或超时,则向所有参与者发送“doAbort”请求,参与者执行事务回滚操作,并向协调者返回“ACK”表示回滚成功。
2PC的具体实现有以下几点需要注意:
- 参与者在接收到“canCommit”请求后,需要记录下此时的当前状态(可以提交或不可提交),以便于在回滚操作时进行信息恢复。
- 协调者在等待参与者响应时要考虑超时情况的处理。如果等待时间过长,则需要对尚未响应的参与者进行处理。
- 2PC模型可能会存在“单点故障”的问题,即如果协调者出现问题,则整个分布式事务都会进入一种无法处理的状态。因此,需要采用数据备份、容灾等措施来降低协调者出现故障的概率。
总之,2PC虽然具有原子性和一致性的特点,但其实现复杂度较高,同时存在一些可能导致性能下降和可用性降低的问题,因此需要根据具体的业务场景进行权衡和选择。
举例说明
以转账业务为例子,介绍使用两阶段提交(2PC)实现分布式事务的过程。
假设有两个账户A和B,我们需要在分布式环境下实现从账户A向账户B转账的业务。具体过程如下:
- 协调者(Coordinator)向账户A和账户B的参与者(Participant)发送“canCommit”请求。
- 账户A和B的参与者接收到请求后进行账户余额扣减和增加操作,并记录下当前状态。如果参与者执行过程中出现异常或者发生网络故障等情况,则向协调者发送“NO”响应。
- 如果A和B的参与者都成功的完成了账户余额变动操作,则向协调者发送“YES”响应。
-
协调者根据收到的参与者响应决定事务提交或回滚操作。
- 如果所有参与者的响应都是“YES”,则协调者向所有参与者发送“doCommit”请求,各参与者执行提交操作并返回“ACK”表示提交成功。
- 如果任意一个参与者的响应为“NO”或协调者等待响应超时,则协调者向所有参与者发送“doAbort”请求,各参与者执行回滚操作并返回“ACK”表示回滚成功。
例如,账户A和B的Participator成功处理账户余额变动操作,向Coordinator发送了“YES”响应。协调者收到两个参与者的“YES”响应后,决定提交事务。协调者向账户A和B的Participator发送“doCommit”请求,各参与者收到请求后执行事务提交操作,返回“ACK”表示提交成功。
如果账户A的余额不足,导致账户A的Participator向协调者发送了“NO”响应,则协调者向所有参与者发送“doAbort”请求,各参与者执行事务回滚操作,并返回“ACK”表示回滚成功。
以上就是使用两阶段提交(2PC)实现分布式事务的过程,可以保证分布式环境下数据的一致性和完整性。
TCC
TCC(Try-Confirm-Cancel)是一种实现分布式事务的较为流行的方式,它将一个分布式事务拆解成“尝试”(Try)、“确认”(Confirm)和“撤销”(Cancel)三个阶段,从而保证了事务的原子性和一致性。
具体来说,TCC实现分布式事务的过程如下:
- Try阶段:进行第一阶段的预处理,检查所有业务参与者的执行条件,检查通过后将所有状态缓存到预提交日志中。
- Confirm阶段:在第二阶段进行业务提交,如果有执行失败,则返回错误,需要重新尝试,直到确认并执行成功,状态提交成功后将对应的缓存删除,释放资源。
- Cancel阶段:在第二阶段,业务需要回滚,则在第三阶段进行回滚处理,取消的同时释放被锁住的资源。
TCC的实现需要从以下几个方面进行考虑:
- 幂等性处理:在Try和Confirm阶段需要保证幂等性,同时需要处理重复请求的情况。
- 可恢复性处理:在整个分布式事务中如果出现异常或者是网络故障等非正常情况,需要通过后台定期的恢复程序对分布式事务进行重试处理,保证分布式事务的正常提交和撤销。
- 长事务和非长事务:TCC不适用于超长事务,因为在尝试期间对资源的占用可能会导致资源的不可用性。 对于较短的事务,则不会受到这个问题的影响。
- 数据库的支持:TCC需要对分布式数据库进行支持,或者使用类似于消息队列的方式进行事务提交。
- 状态并发控制:在TCC模式中,预提交日志的状态会一直存在一段时间,其他构架需要在这段时间内对状态进行异步更新和读取,需要处理并发和锁的问题。
总之,TCC是实现分布式事务的较为常用的方式之一,相对于2PC而言,它的性能和可靠性都更加优秀,实现的复杂度相对2PC要低。
举例说明
假设我们有一个转账业务,需要在分布式环境下完成,使用TCC实现分布式事务的过程如下:
- Try阶段:检查两个账户的余额是否足够,如果余额不足则不能进行转账操作,尝试锁定两个账户并预扣除转账金额,缓存此次转账记录和锁定信息到Try日志中。
- Confirm阶段:完成转账操作,释放锁定资源。如果两个账户的余额都足够,则进行实际的转账操作,记录转账流水和对应的余额变动。如果转账操作执行成功,则删除Try日志中记录的缓存。如果转账操作执行失败,则进行业务回滚,撤销Try日志中记录的缓存。
- Cancel阶段:进行第三阶段处理,将预扣除金额返回给原账户,删除Try日志中记录的缓存并释放锁定的资源,确保已经锁定的账户最终没有被转出。
例如,A账户向B账户转移1000元,使用TCC实现分布式事务的过程如下:
- A账户和B账户的参与者在Try阶段检查A账户余额足够,锁定A账户并预扣除1000元;B账户锁定成功。
- A账户和B账户的参与者在Confirm阶段执行转账操作,记录转账流水和余额变动,释放锁定,最终确认转账操作成功。
- 如果在Try阶段时,A账户余额不足,则进行Cancel阶段处理,将预扣除的1000元退回到A账户中,释放锁定。
以上就是使用TCC实现分布式事务的具体过程了。TCC通过将分布式事务拆分成“尝试”、“确认”和“撤销”三个阶段,以最小的开销保障了分布式事务的可靠性和一致性。
Saga
Saga是一种基于补偿机制的分布式事务解决方案。它通过将整个事务拆解成一系列的局部事务,每个局部事务负责更新自己的本地状态,并通过发送compensating action来完成事务的撤销操作。Saga的核心思想是以“最小承诺”减少分布式事务的范围,从而消除了分布式事务的复杂性,更加适用于微服务架构中的分布式场景。
使用Saga实现分布式事务的过程如下:
- 定义Saga事务:定义Saga事务中的局部事务和compensating action。每个局部事务都会有自己的业务逻辑和状态管理,并且需要实现compensating action以完成事务的撤销操作,保证整个事务的可靠性。
- 执行Saga事务:Saga事务由一个协调器进行控制,它负责调用每个局部事务,并且在每个局部事务完成后检查结果,如果任何局部事务出现了失败,协调器将调用compensating action进行撤销操作。
- Saga事务状态:Saga事务的状态通过每个局部事务的执行结果进行推进,在一系列的局部事务和补偿操作中进行转移。
- 高可用性:Saga事务需要保证高可用性,因此需要使用leader选举或主备模式来保证协调器的高可用性,以防止单点故障导致整个事务出现失败。
举例
例如,下面是一个Saga事务的示例:
- 订单服务提交订单。
- 支付服务执行支付操作,如果支付失败则执行compensating action进行撤销操作。
- 发货服务执行发货操作,如果发货失败则执行compensating action进行撤销操作。
- 物流服务执行更新物流信息操作。
- 审核服务执行订单审核操作,如果审核失败则执行compensating action进行撤销操作。
- 订单服务发送订单确认消息。
- 客户服务完成客户通知。
在这个示例中,如果任何一个局部事务出现失败,Saga事务的协调器将调用compensating action来撤销操作,以保证整个事务的一致性和可靠性。
总之,Saga是一种基于补偿机制的分布式事务解决方案,它提供了一种减少事务范围的方案,从而消除分布式事务中的复杂性,更适合于微服务架构中的分布式场景。使用Saga时需要定义Saga事务、执行Saga事务、Saga事务状态管理和保障高可用性。
消息事务
分布式事务中的消息事务,通常指的是在事务执行过程中所使用的消息队列的事务。实现消息事务的方式主要有两种,分别是本地消息事务和分布式消息事务。
1、本地消息事务:
本地消息事务是在消息发送方和接收方所在的数据库事务中,通过消息队列来实现数据的异步操作。在本地消息事务中,发送方发送消息时,将消息插入到一个本地消息表中,并在当前数据库事务中进行提交。如果后续在事务执行过程中需要回滚,也需要将已经发送的消息删除。接收方在收到消息后,需要在本地数据库事务中进行处理。
2、分布式消息事务:
分布式消息事务需要消息队列支持分布式事务的功能。常见的有基于消息队列内嵌事务的分布式消息事务和基于两阶段提交的分布式消息事务。其中基于消息队列内嵌事务的方式,比如 Kafka 中的事务支持,通过消息队列内嵌事务来实现分布式事务的功能。在使用该方式时,消息的发送方和接收方都需要在同一个事务中进行,如果事务出现问题,则消息会被回滚。
基于两阶段提交的分布式消息事务,则将消息队列作为一个参与者,与数据库事务一起参与分布式事务的处理。在该模式下,在分布式事务提交之前需要进行两个阶段的协作:第一阶段中,记录消息的相关信息并将消息发送到消息队列;第二阶段中,消息队列接收事务协调器发送的消息,确认消息可以发送,并等待最终的确认信息。如果分布式事务提交成功,则消息队列会将消息发送出去;如果分布式事务回滚,则消息队列不会发送消息。
总的来说,实现分布式事务的消息事务需要考虑到消息的发送、接收以及在分布式事务中的协作等方面,采用合适的技术和方案来实现。
本地消息事务
在实现分布式事务时,可以采用本地事务表的形式来实现。具体实现方式如下:
- 在应用中定义本地事务表。可以使用 MySQL 或者其他数据库来存储本地事务表,该表中需要包含以下字段:
- xid:全局唯一标识事务 ID
- branch_id:分支事务 ID
- status:分支事务状态(提交、回滚、未知)
- participant_id:参与者 ID
- create_time:分支事务创建时间
- update_time:分支事务更新时间
- rollback_info:回滚信息
- 在应用中使用使用 Spring Boot 集成分布式事务框架实现分布式事务,比如使用 Seata。
- 实现分布式事务的参与者,需要在本地事务表中记录对应的分支事务。 当前节点的事务参与者需要在本地事务表中插入一条记录,表示当前节点参与了全局事务,并更新状态为“未知”。
- 在分布式事务提交时,需要先提交本地事务,然后根据本地事务的结果,提交或者回滚全局事务。在本地事务提交时,需要将当前节点参与的分支事务状态的状态改为“提交”,并将回滚信息置空。
- 如果本地事务失败,全局事务需要回滚。在回滚时,需要将参与者的分支事务状态修改为“回滚”,并将回滚信息更新到本地事务表中。这时,全局事务协调器将会回滚所有分支事务的状态。
Spring Boot 集成 Seata 可以使用 Seata 官方提供的 Spring Boot Starter,具体使用方法可参考 Seata 的官方文档。
MQ事务
使用消息队列(MQ)实现分布式事务,通常是采用异步确保的方式。具体方式如下:
- 在分布式事务中使用消息队列来进行数据的异步操作,同时将消息队列作为分布式事务的参与者之一。
- 将消息发送方和接收方的事务分离开来,分别作为两个分支事务处理。在本地事务执行完成后,将要发送的消息插入到消息队列中,等待消息队列将消息发送到接收方。
- 当消息发送方完成本地事务后,若事务提交成功,则需要将消息发送到消息队列中,并保证消息可靠性,保证消息发送不会失败。如果发送失败,则需要将消息持久化到本地数据库,并等待后续重发。
- 对于接收方,需要在本地事务中对接收到的消息进行处理。如果处理成功,则需要将消息从消息队列中删除,否则需要将消息更改状态,保证消息重复消费时的正确性。
- 如果在分支事务执行过程中出现失败(比如消息发送失败),需要对分支事务进行回滚,同时需要保证消息队列不会将该消息发送到接收方。在消息队列中实现该功能,常常需要采用阻塞式发送。
需要注意的是,使用消息队列实现分布式事务时,由于涉及到异步操作,因此需要保证消息的可靠性,比如消息持久化、重试机制等。同时,也需要考虑到可能出现消息重复消费的情况,需要在消息队列中进行判重等操作。
具体实现时,可以使用消息中间件(比如 RabbitMQ、Kafka)来实现分布式事务的消息队列。此外,在使用分布式事务时,也可以考虑分布式事务框架,比如 Seata、Hmily 等,它们可以支持分布式事务的协调、管理,提供更便捷的分布式事务实现方式。
Seata
Seata是一种开源的分布式事务解决方案,它提供高效的多数据源、跨服务端的分布式事务管理能力。Seata将每个事务定义为一个全局唯一的XID(X/Open XA标准中的标识符),通过将每个参与方的local transaction纳入全局事务的管理体系中,实现分布式事务的可靠性,保证多个事务操作的原子性、一致性和隔离性。
使用Seata实现分布式事务的过程如下:
- 服务端代码的改造:在所有的服务中,对于需要参与分布式事务的方法进行改造,使用Seata提供的@GlobalTransactional注解标注上事务的类型,自动开启全局事务。
- Seata代理的介入:客户端需要引入Seata代理,Seata代理将介入原有的分布式系统,拦截业务请求并向Seata Server注册事务分支,以获取全局唯一的XID。
- 分布式事务执行:开始执行分布式事务,服务中的每一个分支都会通过与Seata协调器的交互进行状态的更新,并在事务完成时上报状态,根据事务执行的结果通知Seata进行后续的操作。
- Seata Server的管理:包含事务的开始、补偿、提交、回滚、查询等操作,统一管理全局事务的状态。
例如,下面是使用Seata实现转账业务的示例:
- 当用户A向用户B转账时,A服务和B服务需要使用@GlobalTransactional注解对事务进行管理。Seata代理从两个服务中获取XID。
- 然后A服务从A用户账户中扣除金额,B服务增加金额。在扣除和增加金额过程中,Seata将本地事务注册为分布式事务,分配分支ID,通过异步传递分支状态并等待分支状态通知。
- 如果其中一方失败,Seata从上一个通知的状态中检索分支,并向所有未完成任务发送事务回滚指令(暂未实际提交)。事务回滚指令成功发送时则进行回滚(正常状态下这将是通过异步消息来完成)。
- 如果所有分支任务都成功,则Seata回滚底层资源管理器中记录的任何临时写入,并向所有未完成事务发送事务提交指令(正常情况下,这将通过异步消息来完成)。
以上就是使用Seata实现分布式事务的过程,通过Seata对局部事务的承诺和向Seata Server的注册管理,以达到全局事务的控制和管理,实现了分布式事务的可靠性和一致性。
Seata支持三种分布式事务模式,分别是AT、TCC和SAGA,每种模式都有其适用场景,可以根据实际需求选择使用。
1、AT模式
AT(Alter Table)模式利用数据库提供的ACID事务机制,能够减少代码的修改量,最大限度地利用已有的资源,并提供高性能和简便管理。在AT模式下,Seata代理会拦截每一个涉及到数据库的SQL操作,当分支事务进行提交时,Seata会自动将所有修改的数据都写入redo log,并在回滚时将这些操作删除。AT模式适用于业务逻辑简单,对实时性有要求的场景。
2、TCC模式
TCC(Try Confirm Cancel)模式是一种基于业务逻辑实现的分布式事务模式。TCC模式的原理是将一个完整交易拆分成三个阶段——预留资源(try)、执行操作(confirm)和取消操作(cancel),每个阶段对应一个事务操作,通过自定义的业务逻辑实现事务的管理。在TCC模式下,Seata代理会拦截业务请求,同时根据业务逻辑调用try、confirm、cancel的方法。TCC模式适用于业务逻辑复杂,需要定制化事务管理和数据处理的场景。
3、SAGA模式
SAGA(Saga pattern)模式是一种基于补偿机制的分布式事务模式。相比于AT和TCC模式,SAGA模式更加适用于系统与系统之间的分布式事务场景。SAGA模式将复杂的分布式事务拆分成一个个小的本地事务和补偿操作,并通过补偿操作保证整个事务的一致性。在SAGA模式下,Seata会根据应用程序的Saga定义动态地创建和管理micro transactions,从而实现不同服务之间的交互和事务的处理。SAGA模式适用于不同服务之间存在多阶段业务流程,需要根据业务逻辑实现事务管理的场景。
总之,Seata支持三种分布式事务模式,每种模式都适合不同的应用场景和业务需求。在应用中选择合适的事务模式非常重要,可以根据具体业务需求选择AT、TCC和SAGA中的一种或几种来实现分布式事务。
AT
在Spring Boot中使用Seata的AT模式,大概需要以下步骤:
- 引入相关依赖
io.seata
seata-spring-boot-starter
1.4.2
- 配置文件修改
在 application.yml 或 application.properties 中添加以下Seata配置项:
spring:
application:
name: xxx-service # 这里是你的服务名,需要与Seata Server上的注册信息对应
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group # 这里指定Seata服务的事务组名
service:
vgroup:
my_test_tx_group: "default" # 这里指定Seata Server默认的事务组名
group:
my_test_tx_group: # 把这个服务注册到Seata Server的事务组名
default:
annotation-interceptor-order: 1
undo-data-validation: true
mode: AT # 这里指定AT模式
log-store: db
lock:
type: DB
# 记得把下面的DB配置改成你的数据库配置
db:
datasource: druid # javax.sql.DataSource类型,建议使用集成连接池,例如druid、hikari等
...
report-retry-count: 5
- 补充Seata配置文件
在 Seata Server 的 registry.conf
和 file.conf
文件中,需要加入对本服务的配置:
[registry]
# example: file:/registry
type: file
nacos:
serverAddr: localhost:8848
namespace: please_set_your_namespace
cluster: default
client:
...
customizedRegistryClassName: io.seata.registry.FileRegistry
applicationId: my_test_tx_group
txServiceGroup: my_test_tx_group
env: test
# file registry config
file:
name: file.conf
[registry-file]
type: file
serverList: file://registry
[service]
vgroupMapping.my_test_tx_group=default
default.grouplist=file://127.0.0.1:8091
enableDegrade=false
disableGlobalTransaction=false
- 让业务方法支持Seata的分布式事务
在需要使用Seata的分布式事务的方法上加上@GlobalTransactional注解,则此方法即变成了全局事务,如下所示:
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
OrderDAO orderDAO;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
@Override
public Integer createOrder(OrderDTO orderDTO) {
// 业务逻辑
}
}
其中,@GlobalTransactional注解的name属性标注此次全局事务的名称,rollbackFor属性标注回滚的异常类型。
最后,配置好之后运行服务,如果一切正常,你就可以在 Seata Server 的事务列表中看到你的服务加入了一个新的分布式事务了。
通过以上步骤,我们就可以在Spring Boot应用中使用Seata的AT模式实现分布式事务管理。
TCC
在Spring Boot中使用Seata的TCC模式,大概需要以下步骤:
- 引入相关依赖
io.seata
seata-spring-boot-starter
1.4.2
- 配置文件修改
在 application.yml 或 application.properties 中添加以下Seata配置项:
spring:
application:
name: xxx-service # 这里是你的服务名,需要与Seata Server上的注册信息对应
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group # 这里指定Seata服务的事务组名
service:
vgroup:
my_test_tx_group: "default" # 这里指定Seata Server默认的事务组名
group:
my_test_tx_group: # 把这个服务注册到Seata Server的事务组名
default:
annotation-interceptor-order: 1
undo-data-validation: true
mode: TCC # 这里指定TCC模式
log-store: none
lock:
type: db # TCC模式锁的类型需要设为db
db:
...
report-retry-count: 5
- 补充Seata配置文件
在 Seata Server 的 registry.conf
和 file.conf
文件中,需要加入对本服务的配置:
[registry]
# example: file:/registry
type: file
nacos:
serverAddr: localhost:8848
namespace: please_set_your_namespace
cluster: default
client:
...
customizedRegistryClassName: io.seata.registry.FileRegistry
applicationId: my_test_tx_group
txServiceGroup: my_test_tx_group
env: test
# file registry config
file:
name: file.conf
[registry-file]
type: file
serverList: file://registry
[service]
vgroupMapping.my_test_tx_group=default
default.grouplist=file://127.0.0.1:8091
enableDegrade=false
disableGlobalTransaction=false
- 让业务方法支持Seata的分布式事务
在需要使用Seata的分布式事务的方法上加上@TccTransaction注解,接着实现该方法对应的三个方法(try、confirm、cancel),同时这三个方法需要使用@Compensable注解,如下所示:
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
OrderDAO orderDAO;
@TccTransaction(confirmMethod = "confirmCreateOrder", cancelMethod = "cancelCreateOrder")
@Override
public boolean createOrder(OrderDTO orderDTO) {
// TCC try方法,不需要做什么
return true;
}
@Compensable(confirmMethod = "confirmCreateOrder", cancelMethod = "cancelCreateOrder")
public void tryCreateOrder(OrderDTO orderDTO) {
// TCC try方法,预留资源
orderDAO.insert(orderDTO);
}
@Compensable(confirmMethod = "confirmCreateOrder", cancelMethod = "cancelCreateOrder")
public void confirmCreateOrder(OrderDTO orderDTO) {
// TCC confirm方法,提交分布式事务
}
@Compensable(confirmMethod = "confirmCreateOrder", cancelMethod = "cancelCreateOrder")
public void cancelCreateOrder(OrderDTO orderDTO) {
// TCC cancel方法,回滚分布式事务
orderDAO.delete(orderDTO.getId());
}
}
其中,@TccTransaction注解在方法级别上标明当前方法需要支持分布式事务,同时指定了try方法对应的confirm方法和cancel方法。
而@Compensable注解则在子方法级别上标注了当前方法需要纳入分布式事务的管理,同时指定了该方法在confirm和cancel时需要执行的方法。
最后,配置好之后运行服务,如果一切正常,你就可以在 Seata Server 的事务列表中看到你的服务加入了一个新的分布式事务了。
通过以上步骤,我们就可以在Spring Boot应用中使用Seata的TCC模式实现分布式事务管理。
Saga
在 Spring Boot 中使用 Seata 的 Saga 模式,需要以下步骤:
1、引入相关依赖
io.seata
seata-spring-boot-starter
1.4.2
2、配置文件修改
在 application.yml 或 application.properties 中添加以下 Seata 配置项:
# Seata 配置
spring:
application:
name: xxx-service # 这里是你的服务名,需要与 Seata Server 上的注册信息对应
cloud:
alibaba:
seata:
tx-service-group: my_test_tx_group # 这里指定 Seata 服务的事务组名,需要与 Seata Server 上的设置一致
service:
vgroup:
my_test_tx_group: "default" # 这里指定 Seata Server 默认的事务组名
group:
my_test_tx_group: # 把这个服务注册到 Seata Server 上的事务组名
default:
mode: SAGA # 指定 saga 模式
health-check: false
report-retry-count: 5
max-commit-retry-timeout: 300
max-rollback-retry-timeout: 30
3、配置 Seata Server 的 endpoint
在 Seata Server 的配置文件 registry.conf
中,需要为每个要加入 Saga 模式的服务,配置其 endpoint。这里以两个服务为例:
[registry]
# example: file:/registry
type: file
nacos:
serverAddr: localhost:8848
namespace: please_set_your_namespace
cluster: default
client:
...
customizedRegistryClassName: io.seata.registry.FileRegistry
applicationId: my_test_tx_group
txServiceGroup: my_test_tx_group
env: test
# file registry config
file:
name: file.conf
[registry-file]
type: file
serverList: file://registry
[service]
vgroupMapping.my_test_tx_group=default
default.grouplist=file://127.0.0.1:8091,file://127.0.0.1:8092
enableDegrade=false
disableGlobalTransaction=false
[saga]
store.mode=db # saga 模式需要指定 store.mode 为 db
store.db.driver-class-name=com.mysql.cj.jdbc.Driver # 使用 MySQL 数据库存储 Saga 状态
store.db.url=jdbc:mysql://localhost:3306/seata_saga?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false
store.db.username=root
store.db.password=123456
store.db.min-conn=5
store.db.max-conn=30
store.db.global-table-name=seata_global_table # 全局事务表名,默认值为global_table
store.db.branch-table-name=seata_branch_table # 分支事务表名,默认值为branch_table
store.file.path=/seata/saga # 使用文件存储 Saga 状态
其中,store.mode
需要指定为 db
,store.db
指定数据库相关信息。
4、编写 Saga 并添加 @SagaTransactional 注解
在需要实现 Saga 模式的业务操作方法上添加 @SagaTransactional
注解,并编写与之对应的 Saga 对象及其步骤执行逻辑。
下面是一个示例:
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDAO orderDAO;
/**
* 创建订单
*/
@SagaTransactional
@Override
public void createOrder(OrderDTO orderDTO) {
// 扣减商品库存
ProductReduceStockDTO productReduceStockDTO = new ProductReduceStockDTO(orderDTO.getProductId(), orderDTO.getAmount());
productClient.reduceStock(productReduceStockDTO);
// 下单
orderDAO.insert(orderDTO);
// 发送支付事务
SagaRequest sagaRequest = new SagaRequest();
sagaRequest.setServiceName("account-service");
sagaRequest.setMethodName("pay");
sagaRequest.setPayloads(new Object[]{orderDTO.getUserId(), orderDTO.getTotalAmount()});
SagaDefinition sagaDefinition = new SagaDefinition();
sagaDefinition.setSagaName("create-order-saga");
sagaDefinition.setPayload(orderDTO);
sagaDefinition.setSteps(Arrays.asList(
new SagaStep("product-service", "reduceStock", productReduceStockDTO),
new SagaStep("order-service", "insert", orderDTO),
new SagaStep(sagaRequest)
));
SagaContext start = SagaContext.start();
SagaManager.executeSaga(sagaDefinition, start);
}
@CompensableConfirm
public void createOrderConfirm(OrderDTO orderDTO) {
// do nothing
}
@CompensableCancel
public void createOrderCancel(OrderDTO orderDTO) {
orderDAO.delete(orderDTO.getId());
}
}
其中,@SagaTransactional
注解在方法上表示这是一个 Saga 分布式事务操作。
SagaDefinition
表示一个 Saga 分布式事务的定义,包含了该 Saga 的名称、开始时的 payload(启动该 Saga 执行的参数)、以及一系列的步骤,每个步骤都是一个 SagaStep
对象。
@CompensableConfirm
和 @CompensableCancel
注解分别表示执行确认和取消操作。这些方法的实现应当和原始操作相反,以确保分布式事务的完整性。
5、运行服务
配置好 Seata Server 和相关配置信息之后,就可以启动应用并运行测试了。如果一切正常,你就可以在 Seata Server 的 Saga 实例中看到对应的实例,并可以查看各个步骤的执行结果。
通过以上步骤,我们就可以在 Spring Boot 应用中使用 Seata 的 Saga 模式实现分布式事务管理。
需要注意哪些方面
在实现分布式事务时,需要注意以下几个方面:
- 数据库的一致性:分布式事务涉及到多个数据库,需要确保各个数据库操作的一致性。这可以通过在事务开始时对数据库进行锁定,保证在这个事务结束前其他事务无法访问受锁定的数据,从而保证了数据的一致性。
- 事务的可靠性:分布式事务需要确保事务的可靠性,即在各个节点发生故障或者网络错误时,能够保证事务的正确执行。这可以通过使用分布式事务协议或者保证事务的幂等性来实现。
- 事务的性能:分布式事务相对于本地事务,会带来一些额外的性能开销。因此,在实现分布式事务时需要考虑性能问题。例如可以通过分片、异步操作等方式来优化性能。
- 基础设施的支持:分布式事务需要底层基础设施的支持,例如分布式锁、消息队列、分布式计算等。因此,在实现分布式事务时需要考虑基础设施的支持,并选择合适的技术栈。
- 代码的可维护性:分布式事务涉及到多个节点的操作,因此代码的可维护性非常重要。在实现分布式事务时需要考虑代码的可维护性,例如可以通过模块化、统一接口等方式来提高代码的可维护性。
面试常问
以下是一些关于分布式事务的常见面试考点:
- 什么是分布式事务?
分布式事务是指跨越多个计算机或者系统的一种事务,并且在多个数据库、文件系统或其他系统资源上跨越多个事务管理器或协调器。分布式事务需要保证数据在分布式环境下的一致性。
- 分布式事务的实现方式有哪些?
分布式事务的实现方式有以下几种方式:
- 2PC (Two-Phase Commit):通过协调者来管理事务的提交和回滚,实现多个数据库、进程之间的一致性。
- 3PC (Three-Phase Commit):2PC 的改进版,解决了 2PC 中的阻塞问题。
- TCC (Try-Confirm-Cancel):在分布式环境中,将一个分布式事务拆分成多个本地事务,允许服务自行处理事务的确认或取消。
- Saga:将一个分布式事务拆分成多个子事务,每个子事务都有自己的本地事务管理器来处理自己的事务状态,在子事务之间传递消息来保证最终一致性。
- 本地消息表:用于处理事务间的关系,通过本地消息表维护各个事务的状态,在事务提交时才向外部系统发出消息。
- 什么是 2PC?
2PC (Two-Phase Commit) 是一种分布式事务的实现方式。在 2PC 中,协调者将会在两个阶段来提交事务:prepare 和 commit。在 prepare 阶段,协调者会向所有参与者通知分布式事务的准备状态,并要求各个参与者在本地准备好事务。在 commit 阶段,如果所有参与者在 prepare 阶段都反馈了已准备好事务的状态,协调者会通知所有参与者提交事务,否则协调者会请求所有参与者回滚事务。
- 2PC 的优点和缺点是什么?
2PC 的优点是:
- 支持强一致性,所有节点的状态相同。
- 协调者负责整个事务的管理,相对简单。
- 标准化实现,易于开发调试。
2PC 的缺点是:
- 阻塞性问题,要等待所有参与者反馈结果后才能完成事务。
- 单点故障,协调者出现问题会导致整个事务无法继续进行。
- 吞吐量和可扩展性存在瓶颈,由于协调者负责整个事务的处理,因此无法扩展请求的吞吐量。
- 可靠消息最终一致性框架的实现方式有哪些?
可靠消息最终一致性框架的实现方式有以下几种方式:
- TCC:Try-Confirm-Cancel 的缩写。尝试先执行需要远程执行的事务;如果成功,则进行执行确认,否则则进行执行取消,从而实现事务的回滚操作。
- Saga:将一个分布式事务拆分成多个子事务,每个子事务都有自己的本地事务管理器来处理自己的事务状态,在子事务之间传递消息来保证最终一致性。
- 本地消息表:用于处理事务间的关系,通过本地消息表维护各个事务的状态,在事务提交时才向外部系统发出消息。
本文由mdnice多平台发布
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net