版权声明
部分内容参考CSDN博主「机智兵」的原创文章,原文链接:https://blog.csdn.net/a6470831/article/details/124677896
一、事务ACID原则
原子性:事务中的所有操作,要么全部成功,要么全部失败
一致性:要保证数据库内部完整性约束、声明性约束
隔离性:对同一资源操作的事务不能同时发生
持久性:对数据库做的一切修改将永久保存,不管是否出现故障
原子性(A)
所谓的原子性就是说,在整个事务中的所有操作,要么全部完成,要么全部不做,没有中间状态。对于事务在执行中发生错误,所有的操作都会被回滚,整个事务就像从没被执行过一样。
一致性(C)
事务的执行必须保证系统的一致性,在事务开始之前和事务结束以后,数据库的完整性没有被破坏,就拿转账为例,A有500元,B有500元,如果在一个事务里A成功转给B50元,那么不管发生什么,那么最后A账户和B账户的数据之和必须是1000元。
隔离性(I)
所谓的隔离性就是说,事务与事务之间不会互相影响,一个事务的中间状态不会被其他事务感知。数据库保证隔离性包括四种不同的隔离级别:
Read Uncommitted(读取未提交内容)
Read Committed(读取提交内容)
Repeatable Read(可重读)
Serializable(可串行化)
持久性(D)
所谓的持久性,就是说一旦事务提交了,那么事务对数据所做的变更就完全保存在了数据库中,即使发生停电,系统宕机也是如此。
总结
因为在传统项目中,项目部署基本是单点式:即单个服务器和单个数据库。这种情况下,数据库本身的事务机制就能保证ACID的原则,这样的事务就是本地事务。
概括来讲,单个服务与单个数据库的架构中,产生的事务都是本地事务。
其中原子性和持久性就要靠undo和redo 日志来实现。
二、理论基础
版权声明:本小节为CSDN博主「机智兵」的原创文章,原文链接:https://blog.csdn.net/a6470831/article/details/124677896
1、CAP理论
1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标:
Consistency(一致性)
Availability(可用性)
Partition tolerance (分区容错性)
Eric Brewer 说,分布式系统无法同时满足这三个指标。 这个结论就叫做 CAP 定理。
CAP定理- Consistency (一致性)
Consistency(一致性):用户访问分布式系统中的任意节点,得到的数据必须一致
CAP定理- Availability(可用性)
Availability (可用性):用户访问集群中的任意健康节点,必须能得到响应,而不是超时或拒绝
CAP定理-Partition tolerance(分区)
-
Partition(分区):因为网络故障或其它原因导致分布式系统中的部分节点与其它节点失去连接,形成独立分区。
-
Tolerance(容错):在集群出现分区时,整个系统也要持续对外提供服务
总结
-
分布式系统节点通过网络连接,一定会出现分区问题(P)
-
当分区出现时,系统的一致性(C)和可用性(A)就无法同时满足
-
ES集群出现分区时,故障节点会被剔除集群,数据分片会重新分配到其他节点,保证数据一致。因此是地可用性,高一致性,属于CP
2、BASE理论
BASE理论是对CAP的一种解决思路,包含三个思想:
- Basically Available (基本可用):分布式系统在出现故障时,允许损失部分可用性,即保证核心可用。 Soft
- State(软状态):在一定时间内,允许出现中间状态,比如临时的不一致状态。 Eventually
- Consistent(最终一致性):虽然无法保证强一致性,但是在软状态结束后,最终达到数据一致。
而分布式事务最大的问题是各个子事务的一致性问题,因此可以借鉴CAP定理和BASE理论:
- AP模式:各子事务分别执行和提交,允许出现结果不一致,然后采用弥补措施恢复数据即可,实现最终一致。
- CP模式:各个子事务执行后互相等待,同时提交,同时回滚,达成强一致。但事务等待过程中,处于弱可用状态
3、解决分布式事务的思想和模型
- 全局事务:整个分布式事务
- 分支事务:分布式事务中包含的每个子系统的事务
- 最终一致思想:各分支事务分别执行并提交,如果有不一致的情况,再想办法恢复数据
- 分支事务:各分支事务执行完业务不要提交,等待彼此结果,而后统一提交或回滚
三、分布式事务Seata
1、初识seata
Seata是 2019 年 1 月份蚂蚁金服和阿里巴巴共同开源的分布式事务解决方案。
致力于提供高性能和简单易用的分布式事务服务,为用户打造一站式的分布式解决方案。
官网地址:http://seata.io/,其中的文档、播客中提供了大量的使用说明、源码分析
2、Seata架构
Seata事务管理中有三个重要的角色:
- TC (Transaction Coordinator) - 事务协调者:维护全局和分支事务的状态,协调全局事务提交或回滚。
- TM (Transaction Manager) - 事务管理器:定义全局事务的范围、开始全局事务、提交或回滚全局事务。
- RM (Resource Manager)- 资源管理器 :管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
Seata提供了四种不同的分布式事务解决方案:
- XA模式:强一致性分阶段事务模式,牺牲了一定的可用性,无业务侵入
- TCC模式:最终一致的分阶段事务模式,有业务侵入
- AT模式:最终一致的分阶段事务模式,无业务侵入,也是Seata的默认模式
- SAGA模式:长事务模式,有业务侵入
3、XA模式
概念
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,
XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。
正常情况:
回滚情况:
seata的XA模式
seata的XA模式做了一些调整,但大体相似:
RM一阶段的工作:
- 注册分支事务到TC
- 执行分支业务sql但不提交
- 报告执行状态到TC
TC二阶段的工作:
- TC检测各分支事务执行状态
a.如果都成功,通知所有RM提交事务
b.如果有失败,通知所有RM回滚事务
RM二阶段的工作:
- 接收TC指令,提交或回滚事务
工作模式图
优点
- 事务的强一致性,满足ACID原则
- 常用数据库都支持,实现简单,并且没有代码侵入
缺点
- 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
- 依赖关系型数据库实现事务
代码实现XA模式
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
1)、修改application.yml文件(每个参与事务的微服务),开启XA模式:
seata:
data-source-proxy-mode: XA # 开启数据源代理的XA模式
2)、给发起全局事务的入口方法添加@GlobalTransactional注解,
本例中是OrderServiceImpl中的create方法:
@Override@GlobalTransactional
public Long create(Order order) {
// 创建订单
orderMapper.insert(order);
// 扣余额 ...略
// 扣减库存 ...略
return order.getId();
}
3)、重启服务并测试
4、AT模式
概念
AT模式同样是分阶段提交的事务模型,不过弥补了XA模型中资源锁定周期过长的缺陷。
阶段一RM的工作:
- 注册分支事务
- 记录undo-log(数据快照)
- 执行业务sql并提交
- 报告事务状态
阶段二提交时RM的工作:
- 删除undo-log即可
阶段二回滚时RM的工作:
- 根据undo-log恢复数据到更新前
工作模式图
示例:一个分支业务的sql是这样的,update tb_account set money=monsy - 10 where id = 1;
AT模式和XA模式区别
- XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
- XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
- XA模式强一致;AT模式最终一致。
AT模式的脏写问题
事务1获取到锁,然后执行业务修改为了90,并保存快照100,接着释放锁,等待第二阶段执行;而事务2拿到锁之后,读取到此时是90,执行业务之后是80,提交事务释放锁。假设事务1第二阶段回滚,根据快照恢复数据会变成100,就出现了脏写问题。
AT模式的写隔离(全局锁)
两个事务都是seata全局事务
增加了全局锁,由TC记录当前正在操作某行数据的事务,存在数据库表中
这个前提是两个事务都是用了seata的全局事务;两个事务都去获取全局锁,所以可以达到一致性。
其中一个是非seata全局事务
TC需要记录修改前和修改后的快照,最终释放全局锁时比对,是否中间有别的事务走过操作,如果有记录异常,发送告警,人工接入。
优点
- 一阶段完成直接提交事务,释放数据库资源,性能比较好
- 利用全局锁实现读写隔离
- 没有代码侵入,框架自动完成回滚和提交
缺点
- 两阶段之间属于软状态,属于最终一致
- 框架的快照功能会影响性能,但比XA模式要好很多
5、TCC模式
概念
TCC模式与AT模式非常相似,每阶段都是独立事务,不同的是TCC通过人工编码来实现数据恢复。需要实现三个阶段:
- Try:资源的检测和预留
- Confirm:完成资源操作业务;要求Try成功Confirm一定要能成功
- Cancel:预留资源释放,可以理解为try的反向操作。
当某分支事务的try阶段阻塞时,可能导致全局事务超时而触发二阶段的cancel操作。在未执行try操作时先执行了cancel操作,这时cancel不能做回滚,就是空回滚。
对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或cancel,这就是业务悬挂。应当阻止执行空回滚后的try操作,避免悬挂
举例,一个扣减用户余额的业务。假设账户A原来余额是100,需要余额扣减30元。
通过冻结的方式
工作模式图
TCC的空回滚
当某分支事务的try阶段阻塞时,可能导致全局事务超时,而触发二阶段的cancel操作,在未执行try操作时先执行了cancel操作,这是cancel应该不能做回滚,就是空回滚
TCC的业务悬挂
对于已经空回滚的业务,如果以后继续执行try,就永远不可能confirm或try,这就是业务悬挂。应该组织执行空回滚后的try操作,避免悬挂
业务表增加account_freeze_tbl表
为了实现空回滚,反之业务悬挂,以及幂等性要求。我们必须早数据库记录冻结金额的同事,记录当前事务id和执行状态,为此我们设计了一张表:
DROP TABLE IF EXISTS `account_freeze_tbl`;
CREATE TABLE `account_freeze_tbl` (
`xid` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '事务id',
`user_id` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户id',
`freeze_money` int(11) UNSIGNED NULL DEFAULT 0 COMMENT '冻结金额',
`state` int(1) NULL DEFAULT NULL COMMENT '事务状态,0:try,1:confirm,2:cancel',
PRIMARY KEY (`xid`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 ROW_FORMAT = COMPACT;
声明TCC接口
TCC的try、confirm、cancel方法都需要在接口中基于注解来声明,语法如下:
优点
- 一阶段完成直接提交事务,释放数据库资源,性能比较好
- 相比AT模式,无需要生成快照,无需要使用全局锁,性能最强
- 不依赖数据库事务,而是依赖补偿操作,可以用于非事务性数据库
缺点
- 有代码侵入,需要人为编写Try、Confirm和Cancel接口,太麻烦
- 软状态,事务是最终一致
- 需要考虑Confirm和Cancel的失败情况,做好幂等处理
6、Saga模式
概念
Saga模式是SEATA提供的长事务解决方案。也分为两个阶段:
- 一阶段:直接提交本地事务
- 二阶段:成功则什么都不做;失败则通过编写补偿业务来回滚
优点
- 事务参与者可以基于事件驱动实现异步调用,吞吐高
- 一阶段直接提交事务,无锁,性能好
- 不用编写TCC中的三个阶段,实现简单
缺点
- 软状态持续时间不确定,时效性差
- 没有锁,没有事务隔离,会有脏写