Seata
Q:为什么用seata
A:在单体任务中, 可以通过简单的锁来保证ACID(原子性,一致性,隔离性,持久性)。 微服务中,无法通过简单锁的方式完成ACID,即便使用redis分布式锁,在多个服务中,AB成功,C失败,此时AB回退也需要自行判断写完。此处可使用类似TCC,2PC,3PC,Seata的方式完成预PC/回退等操作。
架构
Seata分布式事务方案
XA: 强一致分阶段事务,牺牲一定可用性,无业务侵入
执行阶段:
可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚
持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)
完成阶段:
分支提交:执行 XA 分支的 commit
分支回滚:执行 XA 分支的 rollback
人话版本:
由于执行不提交,而且要等其他执行,多个服务的时候性能差。并且依赖底层数据库支持。
TCC: 最终一致分阶段事务,有业务侵入
回顾总览中的描述:一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
• 一阶段 prepare 行为
• 二阶段 commit 或 rollback 行为
根据两阶段行为模式的不同,我们将分支事务划分为 Automatic (Branch) Transaction Mode 和 Manual (Branch) Transaction Mode.
AT 模式基于 支持本地 ACID 事务 的 关系型数据库:
• 一阶段 prepare 行为:在本地事务中,一并提交业务数据更新和相应回滚日志记录。
• 二阶段 commit 行为:马上成功结束,自动 异步批量清理回滚日志。
• 二阶段 rollback 行为:通过回滚日志,自动 生成补偿操作,完成数据回滚。
相应的,TCC 模式,不依赖于底层数据资源的事务支持:
• 一阶段 prepare 行为:调用 自定义 的 prepare 逻辑。
• 二阶段 commit 行为:调用 自定义 的 commit 逻辑。
• 二阶段 rollback 行为:调用 自定义 的 rollback 逻辑。
所谓 TCC 模式,是指支持把 自定义 的分支事务纳入到全局事务的管理中。
AT: 最终一致分阶段事务,无业务侵入,Seata默认使用
前提
基于支持本地 ACID 事务的关系型数据库。
Java 应用,通过 JDBC 访问数据库。
整体机制
两阶段提交协议的演变:
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
二阶段:
提交异步化,非常快速地完成。
回滚通过一阶段的回滚日志进行反向补偿。
写隔离
一阶段本地事务提交前,需要确保先拿到 全局锁 。
拿不到 全局锁 ,不能提交本地事务。
拿 全局锁 的尝试被限制在一定范围内,超出范围将放弃,并回滚本地事务,释放本地锁。
以一个示例来说明:
两个全局事务 tx1 和 tx2,分别对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 - 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交释放本地锁。 tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 - 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 需要重试等待 全局锁 。
人话版本:
阶段一RM工作:
-
注册分支事务
-
记录undo-log(数据快照)
-
执行业务sql并提交
-
报告事务状态
阶段二提交时RM工作:
-
删除undo-log
阶段二回滚时RM工作
-
根据undo-log 回复数据到更新前
AT与XA最大差别在于,XA一阶段不提交事务,锁定资源,AT模式一阶段直接提交不锁定资源。XA依赖数据库回滚,AT使用快照(也就是把修改的地方提前保存)实现回滚。XA强一致,AT最终一致。
但也正因为如此,AT会有并发问题,如下图。
这还是在两个事务都是seata管理下产生的,如果事务2不是seata管理的,那么问题会不可挽回,seata虽然可以判断这个数据被改过,但他无法回滚,也无法继续,只能警告人工介入。