什么是事务?
事务就是数据库执行中的一系列操作,这些操作要么都做,要么一个都不做;不同事务之间要互不影响;事务完成后要永久生效;数据库只能从一个状态转到另一个状态,没有中间态,以此来保证程序状态的前后一致性。所以说,事务具有原子性,隔离性,持久性,一致性。
什么是分布式事务?
由于系统规模的不断扩大,出现了分布式系统,甚至出现了数据库分库。这时候就出现了分布式事务问题。这是因为在分布式环境中,涉及的数据可能存储在不同的物理位置或不同的数据库实例中,这就导致了传统事务管理无法直接适用。
例如,在一个电商系统中,订单创建(Order Service)和服务支付(Payment Service)可能是由两个独立的服务负责,并且它们各自拥有自己的数据库。如果用户下单并支付这一过程需要作为一个整体来看待,那么这两个操作就必须作为同一个事务处理,否则就可能出现订单创建成功但支付失败的情况。
本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
目前的分布式事务方案
由于分布式事务涉及到分布式系统,分布式事务往往比较复杂。以下是分布式事务的常见模型。
两阶段提交
两阶段提交协议(Two-Phase Commit, 2PC) 是一种经典的分布式事务协议,旨在保证多个服务或数据库之间的事务一致性。
在 2PC 中有两个核心角色:
- 协调者(Coordinator):负责发起事务并决定最终是提交还是回滚。又称事务管理器
- 参与者(Participants):实际执行事务操作的节点,比如数据库或服务。
整个过程分为两个阶段:
-
准备阶段(Prepare Phase)
步骤 操作描述 1 协调者向所有参与者发送 prepare 请求。 2 每个参与者收到请求后,会尝试执行本地事务操作,但不会真正提交。 3 参与者将事务状态写入日志,并回复协调者结果:
- Yes 表示准备好提交;
- No 表示无法提交。 -
提交阶段(Commit Phase)
根据第一阶段的响应,协调者做出如下两种决策:
所有参与者都返回 Yes ✅
步骤 操作描述 1 协调者发送 commit 命令给所有参与者。 2 参与者正式提交本地事务,并释放资源。 3 各参与者完成提交后,向协调者确认提交成功。 任一参与者返回 No ❌
步骤 操作描述 1 协调者发送 rollback 命令给所有参与者。 2 参与者撤销之前所做的操作(通过日志回滚)。 3 各参与者完成回滚后,向协调者确认回滚成功。
优缺点
优点 | 缺点 |
---|---|
实现简单,逻辑清晰 | 单点故障风险大(协调者挂掉则整个事务卡住) |
能够保证强一致性 | 存在同步阻塞,如某个数据库系统本地事务执行较慢,连累其他系统 |
适用于小规模集群 | 不支持部分提交,要么全提交,要么全回滚 |
该方式属于CAP
下的CP
模式。由于同步阻塞问题无法实现完全的可用性。
三阶段提交
三阶段提交(3PC)是对两阶段提交(2PC)的一种改进,旨在解决2PC在协调者故障或网络延迟时可能导致的阻塞问题。它通过将原来的“准备阶段”进一步拆分为两个阶段,从而减少系统因等待响应而卡住的可能性。
整个过程分为三个阶段:
-
CanCommit
协调者向所有参与者发送
canCommit
请求,询问它们是否可以执行事务。参与者收到请求后,检查自身状态,判断是否具备执行能力,并回复Yes
或No
。通过该阶段,可以快速探测参与者是否准备好,避免不必要的资源锁定。 -
PreCommit
类似于二阶段的准备阶段
-
DoCommit
类似于二阶段的提交阶段
由于三阶段支持超时,所以相比于二阶段,可用性更好些。
TCC模式
见后文
Saga 模式
见后文
Seata实现的分布式事务
Seata 是一个基于著名的分布式事务的框架,支持多种事务模式:
- AT 模式(Auto Transaction):自动代理数据库事务,开发者无感知,对数据库无特殊要求。(2PC)
- XA 模式:基于 XA 协议的强一致性事务,要求数据库支持 XA,开发者无感知。(2PC)
- TCC 模式(Try-Confirm-Cancel):通过业务逻辑实现补偿机制。
- Saga 模式:长周期事务处理模式。
其中AT与XA模式使用简单,TCC与Saga较为复杂
使用
Java
AT
得益于Java的注解,使用AT模式,只需要使用@GlobalTransaction
,即可使用分布式事务,开发者无感知,使用 AT 模式可以最小程度减少业务改造成本。例如:
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@GlobalTransactional
public void createOrder() {
// 调用库存服务扣减库存
inventoryService.reduceStock();
// 调用支付服务扣款
paymentService.deductMoney();
}
}
XA
XA 同样作为开发者无感知的模式。同样只需要使用@GlobalTransaction
,即可使用分布式事务。XA 模式使用起来与 AT 模式基本一致,用法上的唯一区别在于数据源代理的替换:使用 DataSourceProxyXA
来替代 DataSourceProxy
。
public class DataSourceProxy {
@Bean("dataSourceProxy")
public DataSource dataSource(DruidDataSource druidDataSource) {
// DataSourceProxyXA XA
return new DataSourceProxyXA(druidDataSource);
// DataSourceProxy AT
// return new DataSourceProxy(druidDataSource);
}
}
TCC 与 Saga
见后文
GO
AT
GO不同于Java,没有注解。其使用依赖于seata-go的api 首先创建数据源
// sql2.SeataATMySQLDriver
sqlDB, err := sql.Open(sql2.SeataATMySQLDriver, "root:root@tcp(127.0.0.1:3306)/seata_go?multiStatements=true&interpolateParams=true")
gormDB, err = gorm.Open(mysql.New(mysql.Config{
Conn: sqlDB,
}), &gorm.Config{})
使用时,将执行事务的方法作为参数传递
import (
"context"
"database/sql"
"time"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"seata.apache.org/seata-go/pkg/client"
sql2 "seata.apache.org/seata-go/pkg/datasource/sql"
"seata.apache.org/seata-go/pkg/tm"
)
func main() {
client.InitPath("./conf/seatago.yml")
// insert
tm.WithGlobalTx(context.Background(), &tm.GtxConfig{
Name: "ATGlobalTx",
Timeout: time.Second * 30,
}, insertData)
}
func insertData(ctx context.Context) error {
data := OrderTblModel{
Id: 1,
UserId: "NO-100003",
CommodityCode: "C100001",
Count: 101,
Money: 11,
Descs: "insert desc",
}
return gormDB.WithContext(ctx).Table("order_tbl").Create(&data).Error
}
XA
XA
模式的使用类似于AT
,唯一的区别为创建数据源的方式
// sql2.SeataXAMySQLDriver
sqlDB, err := sql.Open(sql2.SeataXAMySQLDriver, "root:root@tcp(127.0.0.1:3306)/seata_go?multiStatements=true&interpolateParams=true")
gormDB, err = gorm.Open(mysql.New(mysql.Config{
Conn: sqlDB,
}), &gorm.Config{})
TCC 与 Saga
见后文
附录
分布式理论
一致性
强一致性
无论何时,数据一直保持一致,每个节点中的数据始终是一致的
弱一致性
简而言之,就是允许节点之间出现一定程度的数据不一致
最终一致性
不要求数据始终保持一致,允许一段时间的数据不一致。但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化。最终,节点间的数据会达到一致状态。
CAP理论
与分布式系统相关的理论中,包括CAP
理论
C:一致性,分布式系统中各节点数据保持一致
A:可用性,分布式系统中部分节点故障,其他节点依然可以维持系统运行
P:分区容错性,不同节点之间得通信可能存在延迟或断联。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,分布式系统必须可以容忍这种现象发生并作出处理。
在CAP
理论中,三者是不可得兼的,只能得其二。需要在三种特性中做出权衡:
组合 | 特点 |
---|---|
CA | 一致 + 可用,不考虑分区容错(单机) |
CP | 一致 + 容错,牺牲可用性 |
AP | 可用 + 容错,牺牲一致性 |
BASE理论
除了CAP
理论,还有BASE
理论,该理论是对CAP
中AP
组合的延伸。该理论核心思想是即便无法做到强一致性,但应该采用适合的方式保证最终一致性。
BA:Basically Available 基本可用,分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用 \
S:Soft State 软状态,允许系统存在中间状态,而该中间状态不会影响系统整体可用性 \
E:Eventual Consistency 最终一致性,系统中的所有数据副本经过一定时间后,最终能够达到一致的状态