共计 4903 个字符,预计需要花费 13 分钟才能阅读完成。
作者 | 陈健斌(funkye)github id: a364176773
起源 | 阿里巴巴云原生公众号
Seata 是一款开源的分布式事务解决方案,star 高达 18100+,社区活跃度极高,致力于在微服务架构下提供高性能和简略易用的分布式事务服务,本文将分析 Seata-AT 的实现原理,让用户对 AT 模式有更深刻的意识。
Seata 事务模式是什么?
1. Seata 对事务的定义
Seata 定义了全局事务的框架。
全局事务定义为若干分支事务的整体协调:
- TM 向 TC 申请发动(Begin)、提交(Commit)、回滚(Rollback)全局事务。
- TM 把代表全局事务的 XID 绑定到分支事务上。
- RM 向 TC 注册,把分支事务关联到 XID 代表的全局事务中。
- RM 把分支事务的执行后果上报给 TC。(可选)
- TC 发送分支提交(Branch Commit)或分支回滚(Branch Rollback)命令给 RM。
Seata 的全局事务处理过程,分为两个阶段:
执行阶段 >:执行分支事务,并保障执行后果满足是可回滚的(Rollbackable)和长久化的(Durable)。
实现阶段>:依据执行阶段后果造成的决定,利用通过 TM 收回的全局提交或回滚的申请给 TC,> TC 命令 RM 驱动 分支事务 进行 Commit 或 Rollback。Seata 的所谓事务模式是指:运行在 Seata 全局事务框架下的分支事务的行为模式。> > 精确地讲 >,应该叫作 > 分支事务模式>。
不同的事务模式区别在于分支事务应用不同的形式达到全局事务两个阶段的指标。> > 即,答复以下两个问题:
执行阶段 >:如何执行并保障执行后果满足是可回滚的(Rollbackable)和长久化的(Durable)。
实现阶段>:收到 TC 的命令后,做到事务的回滚 / 提交。
2. 其它二阶段事务如何在 Seata 事务框架下运行
1)TCC 事务模式
首先来看下 TCC 事务如何交融在 Seata 事务框架中:
能够发现,其实跟 Seata 的事务框架图长得十分像,而区别为 RM 负责管理就是一阶段的 try 执行和二阶段的 confirm/cancel,一样是由 TM 进行事务的 Begin(发动),RM 被 TM 调用后执行一阶段的 Try 办法,期待调用链路走完的时候,TM 向 TC 告知二阶段决定,此时 TC 对 RM 驱动二阶段执行(下发告诉,RM 执行 confirm/cancel)。
2)XA 事务模式
如图所示,XA 模式其实就是 Seata 底层利用了 XA 接口,在一阶段二阶段时主动解决。如一阶段时,XA 的 RM 通过代理用户数据源,创立 XAConnection,进行开启 XA 事务 (XA start) 和 XA-prepare(此时 XA 的任何操作都会被长久化,即使宕机也能复原),在二阶段时,TC 告诉 RM 进行 XA 分支的 Commit/Rollback 操作。
AT 模式是什么?
首先来看一个例子。
1. 一阶段
业务 sql:update product set name = ‘GTS’ where name = ‘TXC’。
一阶段的执行过程对用户是无感知的,用户侧的业务 sql 放弃不变,而 AT 模式下一阶段具体产生了什么?接下来,简略说下。
- 解析 sql 并查问失去前镜像:select id, name, since from product where name = ‘TXC’。
- 执行业务 sql。
- 查问执行后的数据作为后镜像:select id, name, since from product where id = 1。
2. 二阶段
提交:仅需把事务相干信息删除即可(实践上不删除也没问题)。
回滚:取出前镜像进行回滚。
通过上述简略的例子,其实能够发现,AT 模式就是主动弥补式事务,那 AT 具体都做了哪些呢?下文将会讲述。
AT 如何保障分布式事务一致性?
先来看这个图:
可能很多人刚看到上图会有疑难,其实这个就是无侵入式 AT 模式的做法示意图。首先用户还是从接口进入,达到事务发起方,此时对业务开发者来说,这个发起方入口就是一个业务接口罢了,一样地执行业务 sql,一样地 return 响应信息给客户端并没有什么扭转。而背地就是用户的 sql 被 Seata 代理所托管,Seata-AT 模式能感知到用户的所有 sql,并对之进行操作,来保障一致性。
Seata-AT 是怎么做到无侵入的呢?
如图所示,利用启动时 Seata 会主动把用户的 DataSource 代理,对 JDBC 操作相熟的用户其实对 DataSource 还是比拟相熟的,拿到了 DataSource,就等于把握了数据源连贯,也就能在背地做些“小动作”,此时对用户来讲也是无感知无入侵。
之后业务有申请进来,执行业务 sql 时,Seata 会解析用户的 sql,提取出表元数据,生成前镜像,再通过执行业务 sql,保留执行 sql 后的后镜像(至于后镜像的介绍之后会讲到),生成行锁之后在注册分支时携带到 Seata-Server,也就是 TC 端。
到此为止,在 Client 端的一阶段操作就曾经实现了,无感知、无入侵。此时如果思考下,会发现这里其实有一个行锁,这个行锁是干什么用的呢?这就是要接着讲到 Seata-AT 是如何保障分布式下的事务隔离性,这里间接拿官网的示例来说。
1. 写隔离
- 一阶段本地事务提交前,须要确保先拿到 全局锁。
- 拿不到 全局锁,不能提交本地事务。
- 拿 全局锁 的尝试被限度在肯定范畴内,超出范围将放弃,并回滚本地事务,开释本地锁。
以一个示例来阐明:
两个全局事务 tx1 和 tx2,别离对 a 表的 m 字段进行更新操作,m 的初始值 1000。
tx1 先开始,开启本地事务,拿到本地锁,更新操作 m = 1000 – 100 = 900。本地事务提交前,先拿到该记录的 全局锁 ,本地提交开释本地锁。tx2 后开始,开启本地事务,拿到本地锁,更新操作 m = 900 – 100 = 800。本地事务提交前,尝试拿该记录的 全局锁 ,tx1 全局提交前,该记录的全局锁被 tx1 持有,tx2 须要重试期待 全局锁 。
tx1 二阶段全局提交,开释 全局锁 。tx2 拿到 全局锁 提交本地事务。
如果 tx1 的二阶段全局回滚,则 tx1 须要从新获取该数据的本地锁,进行反向弥补的更新操作,实现分支的回滚。
此时如果 tx2 仍在期待该数据的 全局锁 ,同时持有本地锁,则 tx1 的分支回滚会失败。分支的回滚会始终重试,直到 tx2 的 全局锁 等锁超时,放弃 全局锁 并回滚本地事务开释本地锁,tx1 的分支回滚最终胜利。
因为整个过程 全局锁 在 tx1 完结前始终是被 tx1 持有的,所以不会产生 脏写 的问题。
这个时候隔离性想必大家曾经比拟明确了,此时一阶段的大部分操作置信大家也比拟明确了,接下来咱们持续往下一阶段解析。
2. AT 模式二阶段解决
由上图可见,在二阶段提交时,TC 仅是下发一个告诉:把之前一阶段做记录的 undoLog 删除,并把相干事务信息如:行锁删除,之后让因为在竞争锁被阻塞的事务顺利进行。
而二阶段是回滚时,则要多做一些解决。
首先在 Client 端收到 TC 告知的二阶段是回滚时,会去查到对应的事务的 undolog,取出后镜像,比照以后的数据(因为 SeataAT 是从业务利用层面进行爱护分布式事务,如果此时在数据库层面间接批改了库内信息,这个时候 SeataAT 的行锁不起隔离性作用),如果呈现了在全局事务以外的数据批改,此时断定为脏写,而 Seata 因为无奈感知这个脏写如何产生,此时只能打印日志和触发异样告诉,告知用户须要人工染指(标准批改数据入口可防止脏写)。
而如果没有产生脏写就比较简单了,拿出前镜像,众所皆知事务是须要有原子性的,要么一起产生,要么都不产生,此时前镜像记录了产生之前的数据,进行回滚后,就达到了相似本地事务那样的原子性成果。回滚后,再把事务相干信息,如 undolog,行锁进行删除。二阶段回滚算是告一段落了。
既然介绍完了 AT 模式的一阶段及二阶段的原理思维形式,那么 AT 在 Seata 的分布式事务框架下是怎么样的呢?
能够看到,AT 与其它事务模式在 Seata 事务框架中,会多出一个 undolog 的表(绝对其它模式的入侵点),然而除此之外,对业务来说,简直是零入侵性,这也就是为什么 AT 模式在 Seata 中受众宽泛的起因。
3. AT 模式与 Seata 反对的其它二阶段模式区别
首先应该明确,目前为止,不存在有任何一种分布式事务的能够满足所有场景。
无论 AT 模式、TCC 模式还是 Saga 模式,这些模式的提出,实质上都源自 XA 标准对某些场景需要的无奈满足。
目前分为 3 点来做出比照:
- 数据锁定
AT 模式应用全局锁保障根本的写隔离,实际上也是锁定数据的,只不过锁在 TC 侧集中管理,解锁效率高且没有阻塞的问题。
TCC 模式无锁,利用本地事务排他锁个性,可预留资源,在全局事务决定后执行相应操作。
XA 模式在整个事务处理过程完结前,波及数据都被锁定,读写都按隔离级别的定义束缚起来。
- 死锁(协定阻塞)
XA 模式 prepare 后(老版本的数据库中,须要 XA END 后,再下发 prepare < 三阶段由来 >),分支事务进入阻塞阶段,收到 XA commit 或 XA rollback 前必须阻塞期待。
AT 可反对降级,因为锁存储在 TC 侧,如果 Seata 呈现 bug 或者其它问题,可间接降级,对后续业务调用链无任何影响。
TCC 无此问题。
- 性能
性能的损耗次要来自两个方面:一方面,事务相干解决和协调过程,减少单个事务的 RT;另一方面,并发事务数据的锁抵触,升高吞吐。其实次要起因就是下面的协定阻塞跟数据锁定造成。
XA 模式它的一阶段不提交,在大并发场景因为锁存储在多个资源方(数据库等),加剧了性能耗损。
AT 模式锁粒度细至行级(须要主键),且所有事务锁存储在 TC 侧,解锁高效迅速。
TCC 模式性能最优,仅需些许 RPC 开销,及 2 次本地事务的性能开销,然而须要合乎资源预留场景,且是对业务侵入性较大(须要业务开发者每个接口分为 3 个,一个 try,2 个二阶段应用的 confirm 和 cancel)。
可能很多同学对 XA 和 AT 的锁 & 协定阻塞不是特地了解,那么间接来看下图:
能够试着猜一下是哪个是 XA?其实下图的是 XA,因为它带来的锁粒度更大,且锁定工夫更久,导致了并发性能绝对 AT 事务模型来说,差的比拟多,所以至今 XA 模式的遍及度都不很太高。
Seata 近期布局
- 控制台
首先控制台是 Seata 用户裸露已久的一个问题,没有一个可视化界面,使得用户对 Seata 的可靠性呈现了狐疑,更因为没有控制台,局限了很多在 Seata 上可人工染指分布式事务的可能性等问题,所以将来在 1.5.0 的版本会带来控制台的退出,也欢送更多的同学退出进来一起共建!
- Raft 集成
Raft 集成的起因,可能大部分用户不是特地通晓,首先要晓得目前 TC 端的事务信息都是存储在内部存储器,比方数据库、redis、mongodb(PR 阶段),这就造成了如果内部存储宕机,Seata-Server 集群的齐全不可用。即使 Server 是集群部署,有 10 个甚至更多节点,都会因而而不可用,这是不可承受的。
所以引入 Raft 来让每个 Seata-Server 的事务信息达到统一,即使某个节点宕机,也不会毁坏事务信息准确性,从而也让分布式事务的一致性失去了更好的保障。(对于 Seata-Server raft 的实现之后会以新篇章来分享。)
- undoLog 压缩
这个是 1.5.0 AT 模式比拟大的性能优化,因为一阶段操作的数据多且大,因为 Seata 在背地为用户插入了 undolog 信息,由此可能也会变得大,有造成了入库迟缓的可能,所以要把 undolog 进行压缩,使 undolog 的插入不再成为 AT 事务在分支数据量大的时候成为一个大的心梗开销。
以下是 Seata 的交换群欢送大家退出:
- 钉钉群:搜寻群号 32033786 进群
- QQ 群:搜寻 216012363 进群
总结
AT 说到底就是实现对资源操作的代理,并记录原先 & 变更后的状态,并用锁保障该数据的隔离性。在调用链中出现异常时,还原所有分支数据,达到分布式事务下的“原子性”。
将来呢?redis,mongodb,mq? 纵情期待。
Seata 我的项目的最外围的价值在于:构建一个全面解决分布式事务问题的标准化平台。
基于 Seata,下层利用架构能够依据理论场景的需要,灵便抉择适合的分布式事务解决方案,十分欢送大家参加到我的项目的建设中,独特打造一个标准化的分布式事务平台。