简单来说就俩阶段
一阶段:
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。只保留一个全局锁在Seata服务端.
二阶段:
如果提交: 提交异步化,非常快速地完成。
如果回滚: 回滚通过XID 和 Branch ID查询出在一阶段的回滚日志然后进行反向补偿。
详细执行步骤:
一阶段
- 先解析sql语句,得到表名,条件,sql类型,等信息
- 得到前镜像:根据解析得到的条件信息,生成查询语句,定位数据。
- 执行业务 SQL
- 查询后镜像:根据前镜像的结果,通过 主键 定位数据。
- 插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志
- 插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记录,插入到 UNDO_LOG 表中。
- 提交前,向 TC 注册分支:申请一个主键等于目标数据主键值的全局锁 。
本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交. - 将本地事务提交的结果上报给 TC。
二阶段-提交
- 收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成功的结果给 TC。
- 异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。
二阶段-回滚
- 收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作。
- 通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。
- 数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数据被当前全局事务之外的动作做了修改。这种情况,需要根据配置策略来做处理,详细的说明在另外的文档中介绍。
- 根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句
- 提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。
其中涉及到知识点解释:
全局事务
包括了两个阶段,哪怕第二阶段不是事务,只有二阶段执行完毕,才代表全局事务的结束,比如:一阶段结束,其实一阶段的本地事务就已经结束了,如果二阶段是提交,那么Seata只会将回滚日志删除.如果二阶段是回滚,那么Seata会新建一个回滚的本地事务执行回滚日志.
本地事务
就是Java事务,每个阶段都可以认为是一个本地事务,对比着全局事务来理解.
全局锁
简单来说就是为了能够保证其他事务在当前事务还未释放全局锁时无法修改同一数值,实现了写的隔离.整个过程 全局锁 在当前全局事务结束前一直是被全局事物持有的,所以不会发生 脏写 的问题。
分支注册流程
每个资源都有一个全局唯一的资源 XID,并且在初始化时用该 XID 向 TC 注册资源,当前事务会根据这个XID创建出一个创建了一个GlobalSession然后进行持久化存储在服务端,默认是采用File的方式存储(当然也可以选择db的方式等),随后在第一阶段结束提交前会向GlobalSession注册分支(注册一个以目标数值的键值为键的全局锁),如果全局锁已存在,则进行不停重试,直到注册成功.
上下文如何传递
Seata 的事务上下文由 RootContext 来管理。
应用开启一个全局事务后,RootContext 会自动绑定该事务的 XID,事务结束(提交或回滚完成),RootContext 会自动解绑 XID。
逆向sql的生成条件
seata是根据主键生成逆向sql的,故而要生成逆向sql必须让seata能够从正向sql中拿到主键
官网流程解释
http://seata.io/zh-cn/docs/dev/mode/at-mode.html
相比于传统XA(2PC)的优点
其实这些也都是XA协议(传统2PC)的缺点
- 速度更快:二阶段是异步提交,无需再像XA协议那样需要等所有的操作都执行完才进行提交.所谓分支事务的二阶段异步提交,其实就是异步删除undoLog。因为一阶段的时候已经提交了本地事务,所以二阶段就非常地快速。
- 高可用性:他将分布式事物中的协调者独立部署,可以实现高可用(可以开启多个seata服务),
- 不会发生脏读:因为整个过程 全局锁 在当前全局事务结束前一直是被自己持有的,所以不会发生 脏写 的问题。
Seata模式的选用
这里讲的不错http://www.360doc.com/content/19/0830/20/99071_858122841.shtml
核心就是这张图