文章目录
一、事务和分布式事务基本概念
1.1 事务是什么?
事务是指数据库中的一组操作,这些操作要么全部成功执行,要么全部回滚,以保持数据的一致性和完整性。事务具有以下四个特性,通常称为ACID特性:
- 原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部失败回滚,没有中间状态。
- 一致性(Consistency):事务的执行使数据库从一个一致性状态转移到另一个一致性状态。事务在开始和结束时,数据库必须满足预定义的一致性规则。
- 隔离性(Isolation):事务的执行应该与其他并发执行的事务相互隔离,每个事务都感觉不到其他事务的存在。
- 持久性(Durability):一旦事务提交,其对数据库的修改应该是永久性的,即使在系统故障的情况下也不应丢失。
1.2 分布式事务是什么?分布式事务问题是怎么出现的?
分布式事务(Distributed Transaction)是指跨越多个分布式系统的事务,其中涉及到多个独立的参与者和资源。分布式事务需要确保多个参与者之间的操作的一致性和原子性。
分布式事务问题的出现是因为在分布式环境中,涉及到多个独立的系统和资源,每个系统可能有自己的本地事务管理机制。由于网络延迟、节点故障、通信失败等原因,导致分布式事务无法像单个系统的事务那样简单地实现ACID特性。常见的分布式事务问题包括:
- 部分失败:在一个分布式事务中,有些参与者执行成功,而其他参与者执行失败,导致事务的部分操作成功,部分操作失败。
- 数据不一致:在一个分布式事务中,数据的一致性无法保证,可能因为参与者之间的数据冲突或者数据同步延迟。
1.3 一般事务的解决方案为什么不能作用于分布式事务?
解决一般事务问题的方法,例如使用数据库的ACID特性、锁机制、回滚和恢复机制等,无法直接应用于分布式事务问题,原因如下:
- 并发控制问题:在分布式环境中,由于多个事务同时执行,并发控制变得更加复杂。传统的锁机制在分布式环境中无法有效地协调多个参与者之间的并发访问。
- 通信故障:分布式事务中的参与者之间通过网络进行通信,网络延迟、断开连接或消息丢失可能导致事务协调失败或超时。
- 参与者故障:在分布式系统中,参与者可能由于硬件故障、软件错误或其他原因而崩溃或无响应。这可能导致事务无法完成或导致数据不一致。
- 同步问题:在分布式环境中,数据的复制和同步可能存在延迟,导致不同参与者之间的数据不一致。
- 跨越多个系统:分布式事务涉及跨越多个独立的系统,每个系统可能具有不同的事务管理机制和数据存储方式,这增加了事务管理的复杂性。
二、分布式事务框架Seata
2.1 初识seata
Seata(Simple Extensible Autonomous Transaction Architecture)是一个开源的分布式事务解决方案,旨在解决分布式事务的一致性和可靠性问题。它提供了一套完整的解决方案,包括事务管理、本地事务协调、分布式事务日志和分布式锁等组件。
Seata通过使用分布式事务日志和分布式锁来保证事务的一致性和可靠性。分布式事务日志记录全局事务的操作日志,并提供了事务的恢复和回滚能力。分布式锁用于保护全局事务在不同参与者之间的并发访问,确保数据的一致性和正确性。
2.2 搭建TC服务
- 下载压缩包
官网地址:http://seata.io/zh-cn/index.html
- 解压至非中文目录
- 修改配置
#打开conf目录中的registry.conf文件
registry {
# 选择注册中心,我这里选择nacos(可以是nacos 、eureka、redis、zk、consul、etcd3、sofa)
type = "nacos"
nacos {
#服务注册到 nacos的自定义服务名称
application = "seata-tc-server"
#nacos的ip+端口
serverAddr = "127.0.0.1:8848"
#当前服务所在的组
group = "DEFAULT_GROUP"
namespace = ""
#集群名称
cluster = "HN"
#账号,默认nacos
username = "nacos"
#密码,默认nacos
password = "nacos"
}
}
config {
# 读取tc服务端的配置文件的方式,这里是从nacos配置中心读取,这样如果tc是集群,可以共享配置
type = "nacos"
# 配置nacos地址等信息
nacos {
serverAddr = "127.0.0.1:8848"
namespace = ""
group = "SEATA_GROUP"
username = "nacos"
password = "nacos"
dataId = "seataServer.properties"
}
}
- 在nacos添加配置
# 数据存储方式,db代表数据库,这里使用的是mysql
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
#如果是mysql8.0,store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.driverClassName=com.mysql.jdbc.Driver
#如果是mysql8.0,store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true&useSSL=false&serverTimezone=PRC
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
#数据库账号
store.db.user=root
#数据库密码
store.db.password=123456
store.db.minConn=5
store.db.maxConn=30
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.queryLimit=100
store.db.lockTable=lock_table
store.db.maxWait=5000
- 进入bin目录,运行其中的seata-server.bat,启动成功后,seata-server就会注册到nacos注册中心
2.3 微服务集成seata
- 添加依赖
<!--seata-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<exclusions>
<!--版本较低,1.3.0,因此排除-->
<exclusion>
<artifactId>seata-spring-boot-starter</artifactId>
<groupId>io.seata</groupId>
</exclusion>
</exclusions>
</dependency>
<!--seata starter 采用1.4.2版本-->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
- 添加配置
seata:
registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
# 参考tc服务自己的registry.conf中的配置
type: nacos
nacos: # tc
server-addr: 127.0.0.1:8848
namespace: ""
group: DEFAULT_GROUP
application: seata-tc-server # tc服务在nacos中的服务名称
username: nacos
password: nacos
tx-service-group: seata-demo # 事务组,根据这个获取tc服务的cluster名称
service:
vgroup-mapping: # 事务组与TC服务cluster的映射关系
seata-demo: SH
2.4 XA模式
2.4.1 原理
- XA模式:
XA模式是一种分布式事务处理的协议,它使用两阶段提交(2PC)来保证事务的一致性和可靠性。
- 准备阶段:事务协调器向参与者发送准备请求,要求它们准备执行事务操作,并将结果记录在事务日志中。
- 提交阶段:如果所有参与者都准备就绪,事务协调器发送提交请求给参与者,要求它们执行事务的提交操作。
- 中断阶段:如果任何一个参与者未能准备就绪或发生错误,事务协调器发送中断请求给参与者,要求它们执行事务的中断操作。
通过两阶段提交,XA模式确保所有参与者要么一起提交事务,要么一起中断事务,从而保证事务的一致性。然而,XA模式也存在一些问题,如阻塞和单点故障的风险,因此在某些情况下可能需要考虑其他分布式事务解决方案。
- seata的XA模式
Seata的XA模式是在XA模式基础上进行了扩展和优化的实现。它引入了Seata Server和Seata Client的概念,通过Seata Server作为事务协调器,将分布式事务的控制逻辑集中管理。Seata的XA模式还提供了更多的功能和工具,如分布式事务日志和分布式锁,以增强分布式事务的可靠性和性能。
2.4.2 优缺点
优点:
- 一致性:XA模式通过两阶段提交协议,确保所有参与者要么一起提交事务,要么一起中断事务,从而保证事务的一致性。
- 可靠性:XA模式提供了强一致性和可靠性的保证,在分布式环境下可以确保事务的正确执行。
- 标准化:XA模式是一种标准的分布式事务处理协议,被广泛支持和应用于各种数据库和资源管理器中。
缺点:
- 性能开销:在XA模式中,需要进行多次网络通信和协调操作,这会引入额外的性能开销,并且可能会导致事务处理的延迟增加。
- 阻塞风险:在准备阶段和提交阶段,所有的参与者都需要等待事务协调器的指令,这可能导致一些参与者在等待期间被阻塞,影响系统的吞吐量和并发性能。
- 单点故障:在XA模式中,事务协调器起着关键的角色,如果事务协调器出现故障,整个分布式事务系统可能无法正常运行。
2.4.3 实现
- 添加配置
seata:
data-source-proxy-mode: XA
-
在需要分布式事务的业务代码上添加注解
@GlobalTransactional
-
重启测试
2.5 AT模式
2.5.1 原理
- AT模式
AT模式是一种分布式事务处理模式,它通过在每个参与者的本地事务中实现事务的原子性和隔离性,来保证分布式事务的一致性。
AT模式的原理如下:
- 事务的发起者开始一个全局事务,并在本地事务管理器中开始一个本地事务。
- 事务的发起者调用其他参与者的服务,将全局事务ID传递给它们。
- 参与者接收到全局事务ID后,在本地事务管理器中开始一个本地事务,并执行操作。
- 当参与者的本地事务执行成功时,将操作结果记录在本地日志中。
- 事务的发起者完成所有参与者的服务调用后,调用各个参与者的提交接口。
- 参与者检查本地事务的日志记录,如果操作都成功,则提交本地事务;否则,回滚本地事务。
通过在每个参与者的本地事务中实现原子性和隔离性,AT模式避免了全局锁和阻塞的问题,从而提高了系统的并发性能。在AT模式中,参与者的本地事务执行成功后即可提交,而不需要等待其他参与者的状态。
- seata的AT模式
Seata的AT模式是在AT模式基础上进行了扩展和优化的实现。Seata引入了Seata Server和Seata Client的概念,通过Seata Server作为事务协调器,集中管理分布式事务的控制逻辑。Seata的AT模式还提供了更多的功能和工具,如分布式事务日志和分布式锁,以增强分布式事务的可靠性和性能。
2.5.2 优缺点
优点:
- 较高的性能:AT模式在每个参与者的本地事务中执行操作,避免了全局锁和阻塞的问题,提高了系统的并发性能。
- 简化的实现:相对于XA模式,AT模式的实现相对简单,不需要涉及全局事务协调器,减少了开发和维护的复杂性。
- 本地事务的独立性:每个参与者在本地事务管理器中管理自己的事务,可以独立控制和优化本地事务的执行。
缺点:
- 弱一致性:AT模式对一致性的要求相对较低,可能会出现数据不一致的情况。在某些场景下,可能需要更高的一致性保证,需要考虑其他分布式事务处理模式。
- 隔离级别限制:由于AT模式依赖于本地事务的隔离性,参与者的隔离级别受限于本地事务管理器支持的隔离级别,可能无法满足某些特定的隔离需求。
- 容错性和恢复性:AT模式在发生故障或错误时,需要考虑如何处理事务的回滚和恢复,以确保数据的一致性和可靠性。
2.5.3 实现
- 添加配置
seata:
data-source-proxy-mode: AT
-
在需要分布式事务的业务代码上添加注解
@GlobalTransactional
-
重启测试
2.6 TCC模式
2.6.1 原理
- TCC模式
TCC模式是一种分布式事务处理模式,用于解决分布式环境下的一致性问题。它通过将事务分解为三个阶段(Try、Confirm、Cancel)来实现事务的可靠性和一致性。
TCC模式的工作原理如下:
- Try阶段(尝试阶段):在这个阶段,参与者(服务)尝试预留或锁定资源,并执行必要的前置检查。如果所有参与者的Try操作都成功,表示资源可用,并进入下一阶段。如果有任何一个参与者的Try操作失败,表示资源不可用或发生冲突,事务将中止。
- Confirm阶段(确认阶段):在这个阶段,参与者进行最终的确认操作,将资源真正提交或应用到系统中。如果所有参与者的Confirm操作都成功,事务完成,提交操作得到确认。如果有任何一个参与者的Confirm操作失败,事务将进入Cancel阶段。
- Cancel阶段(取消阶段):在这个阶段,参与者进行回滚或取消操作,将之前尝试预留或锁定的资源恢复到原始状态。如果所有参与者的Cancel操作都成功,事务被取消,资源释放。如果有任何一个参与者的Cancel操作失败,可能需要进行补偿或人工介入来恢复系统一致性。
TCC模式通过将事务拆分为Try、Confirm和Cancel三个阶段,使得每个参与者可以控制自己的操作和资源,从而实现了分布式事务的可靠性和一致性。它要求参与者实现相应的接口和逻辑,确保Try和Cancel操作是幂等的,以处理重试和故障恢复情况。
- seata的TCC模式
Seata的TCC模式是在TCC模式基础上进行了扩展和优化的实现。Seata引入了Seata Server作为事务协调器,集中管理分布式事务的控制逻辑。Seata的TCC模式还提供了分布式事务日志和分布式锁等功能,以增强事务的可靠性和性能。Seata的TCC模式可以更方便地集成到应用中,并提供了更好的事务管理和监控能力。
2.6.2 优缺点
优点:
- 一阶段完成直接提交事务,释放数据库资源,性能好;
- 相比AT模型,无需生成快照,无需使用全局锁,性能最强;
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务型数据库;
缺点:
- ;有代码侵入,需要人为编写try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致;
- 需要考虑Confirm和Cancel的失败情况,做好幂等处理;
2.5.3 实现
以账户转账为例:
- 定义参与者接口:
public interface AccountService {
boolean tryTransfer(String fromAccount, String toAccount, double amount);
boolean confirmTransfer(String fromAccount, String toAccount, double amount);
boolean cancelTransfer(String fromAccount, String toAccount, double amount);
}
- 实现参与者逻辑:
public class AccountServiceImpl implements AccountService {
@Override
public boolean tryTransfer(String fromAccount, String toAccount, double amount) {
// 执行转账操作,预留转出账户金额,检查账户余额等
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean confirmTransfer(String fromAccount, String toAccount, double amount) {
// 确认转账操作,将预留金额转出
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean cancelTransfer(String fromAccount, String toAccount, double amount) {
// 取消转账操作,将预留金额回滚到账户
// 如果成功,返回 true;如果失败,返回 false
}
}
- 客户端调用:
// 获取Seata全局事务ID
String xid = RootContext.getXID();
// 开启全局事务
TransactionContext context = new TransactionContext();
context.setXid(xid);
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
// 调用参与者的tryTransfer方法
boolean tryResult = accountService.tryTransfer(fromAccount, toAccount, amount);
if (tryResult) {
// 提交全局事务
tx.commit();
} else {
// 回滚全局事务
tx.rollback();
}
} catch (Exception e) {
// 异常时回滚全局事务
tx.rollback();
2.7 Saga模式
2.7.1 原理
- Saga模式
Saga模式是一种用于处理分布式事务的模式,它通过将长时间的、复杂的事务分解为多个小的、可逆的事务片段,以实现事务的一致性和可靠性。
在Saga模式中,每个事务片段称为一个补偿操作。每个补偿操作都与一个正向操作相对应,正向操作是事务的一部分,而补偿操作是用于撤销或修复正向操作的。Saga模式通过按照事务执行的顺序,依次执行正向操作和补偿操作,来确保事务在发生失败或异常时能够进行回滚或恢复。
Saga模式的执行过程如下:
- 执行正向操作:按照事务的逻辑顺序,依次执行正向操作。每个正向操作都会记录事务的执行状态。
- 如果所有的正向操作都成功执行,则事务提交完成。
- 如果某个正向操作失败,将会触发相应的补偿操作。补偿操作会撤销或修复正向操作的影响。
- 执行补偿操作:按照逆序依次执行已经触发的补偿操作。补偿操作应该具备幂等性,以便可以多次执行而不会造成副作用。
- 如果所有的补偿操作都成功执行,则事务回滚完成。
- 如果补偿操作也失败,需要人工介入或其他手段来解决事务的一致性问题。
Saga模式适用于长时间运行的事务或跨多个服务进行的事务,可以降低分布式事务的复杂性。Saga模式的优点在于能够在发生故障或异常时进行局部回滚,而不需要回滚整个事务。然而,Saga模式也存在一些挑战,如补偿操作的实现和管理、事务执行的顺序控制等。
- Seata的Saga模式
Seata的Saga模式通过Seata框架来管理和协调分布式事务,提供了对事务的编排和状态管理的支持。它与Seata的其他特性(如AT模式、TCC模式)结合在一起,构成了Seata全面的分布式事务解决方案。
Seata的Saga模式相对于传统的Saga模式,具有以下特点:
- 集成性:Seata的Saga模式与Seata框架紧密集成,可以与Seata的其他特性一起使用,如分布式事务日志和分布式锁等。
- 强一致性:Seata的Saga模式提供了强一致性的事务支持,确保事务的执行顺序和一致性。
- 可靠性:Seata的Saga模式在补偿操作的执行过程中,支持重试和恢复机制,提高了事务的可靠性和恢复能力。
2.7.2 优缺点
优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高;
- 一阶段直接提交事务,无锁,性能好不用编写TCC中的三个阶段,实现简单;
缺点:
- 软状态持续时间不确定,时效性差;
- 没有锁,没有事务隔离,会有脏写;
2.7.3 实现
使用Seata的Saga模式,需要进行以下步骤:
- 定义参与者接口:
public interface OrderService {
boolean createOrder(String orderId, String userId, String productId, int quantity);
boolean cancelOrder(String orderId);
}
public interface ProductService {
boolean reduceStock(String productId, int quantity);
boolean revertStock(String productId, int quantity);
}
- 实现参与者逻辑:
public class OrderServiceImpl implements OrderService {
@Override
public boolean createOrder(String orderId, String userId, String productId, int quantity) {
// 执行订单创建逻辑,如创建订单记录、扣减用户余额等
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean cancelOrder(String orderId) {
// 执行订单取消逻辑,如回滚订单记录、恢复用户余额等
// 如果成功,返回 true;如果失败,返回 false
}
}
public class ProductServiceImpl implements ProductService {
@Override
public boolean reduceStock(String productId, int quantity) {
// 执行减少库存逻辑,如更新产品库存、记录库存变更日志等
// 如果成功,返回 true;如果失败,返回 false
}
@Override
public boolean revertStock(String productId, int quantity) {
// 执行恢复库存逻辑,如恢复产品库存、删除库存变更日志等
// 如果成功,返回 true;如果失败,返回 false
}
}
- 客户端调用:
// 获取Seata全局事务ID
String xid = RootContext.getXID();
// 开启全局事务
TransactionContext context = new TransactionContext();
context.setXid(xid);
GlobalTransaction tx = GlobalTransactionContext.getCurrentOrCreate();
try {
// 调用参与者的方法
boolean createOrderResult = orderService.createOrder(orderId, userId, productId, quantity);
boolean reduceStockResult = productService.reduceStock(productId, quantity);
if (createOrderResult && reduceStockResult) {
// 提交全局事务
tx.commit();
} else {
// 回滚全局事务
tx.rollback();
}
} catch (Exception e) {
// 异常时回滚全局事务
tx.rollback();
}
2.8 四种模式对比
XA | AT | TCC | SAGA | |
---|---|---|---|---|
一致性 | 强一致 | 弱一致 | 弱一致 | 最终一致 |
隔离性 | 完全隔离 | 基于全局锁隔离 | 基于资源预留隔离 | 无隔离 |
代码侵入 | 无 | 无 | 有,需要编写三个接口 | 有,需要编写状态机和补偿业务 |
性能 | 差 | 好 | 非常好 | 非常好 |
场景 | 对一致性、隔离性有高要求的业务; | 基于关系型数据库的大多数分布式务场景都可以; | 1.对性能要求较高的事务;2.有非关系型数据库要参与的事务; | 1.业务流程长、业务流程多;2.参与者包含其它公司或遗留系统服务,无法提供TCC模式要求的三个接口 |