分布式事务学习笔记
1.简述BASE理论的三个思想
基本可用、软状态、最终一致
2.解决分布式事务的思想和模型
全局事务:整个分布式事务
分支事务:分布式事务中包含的每个子系统的事务
最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
强一致思想:各分支事务执行完业务不要提交,等待彼此结果,然后统一提交与回滚
Seata架构
Seata 事务管理中有三个重要的角色:
TC(Transaction Coordinator)-事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚
TM(Transaction Manager)-事务管理者:定义全局事务的范围、开启全局事务、提交或回滚全局事务
RM(Resource Manager)-资源管理器:管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚
Seata提供了四种不同的分布式事务解决方案:
XA模式: 强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
TCC模式: 最终一致的分阶段事务模式,有业务侵入
AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
SAGA模式:长事务模式,有业务侵入
SEATA 每个微服务集成的关键配置信息
seata:
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: ""
group: SEATA_GROUP
application: seata-tc-server
username: nacos
password: nacos
tx-service-group: seata-demo # 事务组名称
service:
vgroup-mapping: # 事务组与cluster的映射关系
seata-demo: SH
nacos服务名称组成包含?
namespace+group+serviceName+cluster
seata 客户端获取tc的cluster名称方式?
以tx-group-service的值为key到vgroupMapping中查找
XA模式原理
XA规范是X/Open组织定义的分布式事务处理(DTP) 标准,XA规范藐视了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对XA规范提供了支持。
都成功的情况:
有失败的情况:
SEATA的XA模式
seata的XA模式做了一些调整,但大体相类似:
RM一阶段的工作
1.注册分支事务到TC
2.执行分支的业务逻辑SQL但不提交
3.报告执行状态到TC
TC二阶段的工作:
TC检测各分支事务的执行状态。如果都成功,通知所有的RM提交事务,如果有失败,通知所有的RM回滚事务
RM二阶段的工作:
接收TC指令,提交或者回滚事务
一阶段:
二阶段:
XA模式的有点是什么?
事务的强一致性,满足ACID的原则
常用数据库都支持,实现简单,并且没有代码入侵。
XA模式的缺点是什么?
因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差。
依赖关系型数据库实现事务
SEATA开启XA模式的配置项:
data-source-proxy-mode: XA
需要在方法上全局事务的入口加上注解:@GlobalTransactional
AT模式原理
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷
RM执行事务后提交
阶段2:回滚或者提交 undo.log
阶段1RM的工作:
注册分支事务
记录undo-log(数据快照)
执行业务sql并提交
报告事务状态
阶段2提交时的RM的工作:
删除undo-log即可
阶段2回滚时的RM的工作:
根据undo-log恢复数据到更新前
AT模式的原理
简述AT模式与XA模式最大的区别是什么?
XA模式一阶段不提交事务,锁定资源,AT模式一阶段直接提交,不锁定资源。
XA模式依赖数据库的机制实现回滚,AT模式则是利用数据快照实现数据回滚
XA强一致性,AT模式最终一致性
AT模式的脏写问题
AT 模式的写隔离
引入了全局锁 ,全局锁:由TC记录当前正在操作执行数据的事务,该事务持有全局锁,具备执行权。
演示时序图:
这样不就与XA模式是一样的么?其实…不是
数据库的锁与TC的锁存在粒度的差别,数据库锁不释放,任何人都无法访问数据。但是TC的锁,只记录操作这样表的全局事务,由SEATA管理的。如果某个事务不是由SEATA管理的,那就仍然可以操作该行记录的其他字段的,但如果仍然操作与SEATA事务中操作的同一字段呢?解决方案如下:
拿更新后的镜像快照值90与当前的数据库中的80的值去比较,如果一样,把数据回滚到更新前的状态,也就是before-image,如果不一样,说明中间有其他事务做了相关的操作,需要后续人为操作。(乐观锁的思想)
AT模型的优点:
一阶段完成直接提交事务,释放数据库的资源,性能比较好。
利用全局锁实现读写隔离
没有代码的侵入,框架自动的完成回滚与提交
AT模式的缺点:
两阶段之间属于软状态,属于最终一致
框架的快照功能会影响性能,但比XA模式会好很多。
实现的时候需要引入相关的表,分为TC以及RM不同的设置,seata-at.sql,其中lock_table导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库
配置项: data-source-proxy-mode: AT
TCC模式原理
TCC模式与AT模式非常类似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据的恢复,需要实现三个方法:
Try: 资源的检测和预留
Confirm: 完成资源操作业务,要求Try成功,Confirm一定能成功
Cancel: 预留资源释放,可以理解为try的反向操作
图解操作:
TCC模式原理的一阶段:
TCC模式原理的二阶段:
TCC模式的每个阶段是做什么?
1.Try:资源检查和预留
2.Confirm: 业务执行和提交
3.Cancel: 预留资源的释放
TCC模式的优点是什么?
一阶段完成直接提交事务,释放数据库资源,性能好。
相比AT模型,无需生成快照,无需使用全局锁,性能最强
不依赖数据库事务,而是依赖补偿机制,可以用于非事务型数据库
TCC的缺点是什么?
有代码侵入,需要人为编写try\confirm\cancel接口
软状态,事务是最终一致的
需要考虑Confirm和Cancel的失败情况,做好幂等处理。
TCC模式需要注意的问题
1.保证confirm以及cancel接口的幂等性
2.允许空回滚
3.拒绝业务悬挂
TCC 模式的实现
1.接口的定义
package cn.itcast.account.service;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
/**
* @author: Runqiang_Jiang
* @Time: 2024/1/28 15:08
*/
@LocalTCC
public interface AccountTccServiceDemo {
/**
* 从用户账户中扣款
*/
@TwoPhaseBusinessAction(name = "deduct",commitMethod = "confirm",rollbackMethod = "cancel")
void deduct(@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "money") int money);
boolean confirm(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
2.接口的实现
package cn.itcast.account.service.impl;
import cn.itcast.account.entity.AccountFreeze;
import cn.itcast.account.entity.AccountFreeze.State;
import cn.itcast.account.mapper.AccountFreezeMapper;
import cn.itcast.account.mapper.AccountMapper;
import cn.itcast.account.service.AccountTccServiceDemo;
import io.seata.core.context.RootContext;
import io.seata.rm.tcc.api.BusinessActionContext;
import javax.swing.RootPaneContainer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author: Runqiang_Jiang
* @Time: 2024/1/28 15:14
*/
@Slf4j
@Service
public class AccountTccServiceDemoImpl implements AccountTccServiceDemo {
@Autowired
private AccountMapper accountMapper;
@Autowired
private AccountFreezeMapper accountFreezeMapper;
@Override
@Transactional
public void deduct(String userId, int money) {
//money数据库已设置为无符号数,所以出现负数就会报错的
//获取事务id
String xid = RootContext.getXID();
//TODO 如何避免业务悬挂?
// 判断freeze中是否有冻结记录,如果有,一定是CANCEL执行过,我要拒绝
AccountFreeze accountFreeze = accountFreezeMapper.selectById(xid);
if(accountFreeze!=null){
//如果有,一定是CANCEL执行过,我要拒绝
return;
}
//1.扣减可用余额
accountMapper.deduct(userId,money);
//2.记录冻结金额,事务的状态
AccountFreeze build = AccountFreeze.builder().userId(userId)
.freezeMoney(money)
.state(State.TRY)
.xid(xid).build();
accountFreezeMapper.insert(build);
}
@Override
public boolean confirm(BusinessActionContext context) {
//1.获取事务id
String xid = context.getXid();
//2.根据id删除冻结记录
int count = accountFreezeMapper.deleteById(xid);
return count==1;
}
@Override
public boolean cancel(BusinessActionContext context) {
//0.查询冻结记录
AccountFreeze accountFreeze = accountFreezeMapper.selectById(context.getXid());
//TODO 空回滚的判断,判断freeze是否为null,为null说明try没有执行,需要空回滚
if(accountFreeze==null){
//证明try没有执行,需要空回滚
String userId = context.getActionContext("userId").toString();
AccountFreeze build = AccountFreeze.builder().userId(userId)
.freezeMoney(0)
.state(State.CANCEL)
.xid(context.getXid()).build();
accountFreezeMapper.insert(build);
return true;
}
//TODO 2.幂等判断
if(accountFreeze.getState()== State.CANCEL){
//已经处理过了一次CANCEL,无需要重复处理
return true;
}
//1.恢复可用余额
accountMapper.refund(accountFreeze.getUserId(), accountFreeze.getFreezeMoney());
//2.将冻结金额清零,状态改为Cancel
accountFreeze.setFreezeMoney(0);
accountFreeze.setState(State.CANCEL);
int count = accountFreezeMapper.updateById(accountFreeze);
return count==1;
}
}
3.业务分析
说明:TCC的使用需要结合具体的业务情景,每个分支是事务采用不同的事务模式(AT\TCC),都是可以实现的,背后依靠的是SEATA框架
Saga模式
Saga模式是SEATA提供的长事务解决方案,也分为两阶段。
一阶段:直接提交本地事务
二阶段:成功则什么都不做,失败则通过编写补偿业务来回滚
Saga模式的优点:
1.事务参与者可以基于事件驱动实现异步调用,吞吐高
2.一阶段直接提交事务,无锁,性能好
3.不用编写TCC的三个阶段,实现简单
缺点:
软状态持续时间不确定,时效差
没有锁,没有事务隔离,会有脏写。
四种分布式事务模式的比较
B站链接
分布式事务