作者:京东物流技术与数据智能部 张硕

1 布景知识

随着业务的快速发展、业务复杂度越来越高,几乎每个公司的体系都会从单体走向分布式,特别是转向微服务架构。随之而来就必然遇到分布式业务这个难题,这篇文章经过seata结构总结了分布式业务的几种处理计划

1.1 ACID

联系型数据库具有处理复杂业务场景的能力,联系型数据库的业务满意 ACID 的特性。

  • Atomicity:原子性(要么都做,要么都不做)
  • Consistency:共同性(数据库只需一个状况,不存在未确定状况)
  • Isolation:阻隔性(业务之间互不搅扰)
  • Durability:永久性(业务一旦提交,数据库记载永久不变)

1.2 CAP

CAP 是指在一个分布式体系下, 包括三个要素:Consistency(共同性)、Availability(可用性)、Partition tolerance(分区容错性),而且三者不行得兼。

  • C:Consistency,共同性,一切数据变化都是同步的。
  • A:Availability,可用性,即在能够接受的时刻范围内正确地响使用户恳求。
  • P:Partition tolerance,分区容错性,即某节点或网络分区故障时,体系仍能够供给满意共同性和可用性的服务。

1.3 BASE

BASE 理论首要是处理 CAP 理论中分布式体系的可用性和共同性不行兼得的问题。BASE 理论包括以下三个要素:

  • BA:Basically Available,基本可用。
  • S:Soft State,软状况,状况能够有一段时刻不同步。
  • E:Eventually Consistent,终究共同,终究数据是共同的就能够了,而不是时时坚持强共同。

2 完结形式

2.1 二段提交

第一阶段(预备阶段)

基于Seata探寻分布式事务的实现方案

TM 告诉一切参与业务的各个 RM,给每个 RM 发送 prepare 音讯。
RM 接纳到音讯后进入预备阶段后,要么直接回来失利,要么创立并履行本地业务,写本地业务日志(redo 和 undo 日志),但是不提交(此处只保存终究一步耗时最少的提交操作给第二阶段履行)。

第二阶段(提交 / 回滚阶段)

基于Seata探寻分布式事务的实现方案

Seata结构

基于两阶段提交形式,从规划上咱们能够将全体分红三个大模块,即TM、RM、TC,详细解释如下:

  • TM(Transaction Manager):大局业务办理器,操控大局业务边界,担任大局业务敞开、大局提交、大局回滚。
  • RM(Resource Manager):资源办理器,操控分支业务,担任分支注册、状况报告,并接纳业务和谐器的指令,驱动分支(本地)业务的提交和回滚。
  • TC(Transaction Coordinator):业务和谐器,维护大局业务的运转状况,担任和谐并驱动大局业务的提交或回滚。

基于Seata探寻分布式事务的实现方案

一个典型的分布式业务进程:

  • TM 向 TC 恳求敞开一个大局业务,大局业务创立成功并生成一个大局唯一的 XID。
  • XID 在微服务调用链路的上下文中传播。
  • RM 向 TC 注册分支业务,将其纳入 XID 对应大局业务的统辖。
  • TM 向 TC 建议针对 XID 的大局提交或回滚抉择。
  • TC 调度 XID 下统辖的全部分支业务完结提交或回滚恳求。

基于Seata探寻分布式事务的实现方案

2.2 XA

在 XA 形式下,每一个 XA 业务都是一个业务参与者。分布式业务敞开之后

首先在一阶段履行“xa start”、“业务 SQL”、“xa end”和 “xa prepare” 完结 XA 业务的履行和预提交;

二阶段假如提交的话就履行 “xa commit”,假如是回滚则履行“xa rollback”。这样便能确保一切 XA 业务都提交或许都回滚。

基于Seata探寻分布式事务的实现方案

无论 Phase2 的抉择是 commit 仍是 rollback,业务性资源的锁都要坚持到 Phase2 完结才开释。

一个正常运转的业务,大概率是 90% 以上的业务终究应该是成功提交的,咱们是否能够在 Phase1 就将本地业务提交呢?这样 90% 以上的情况下,能够省去 Phase2 持锁的时刻,全体进步功率。

基于Seata探寻分布式事务的实现方案

分支业务中数据的本地锁由本地业务办理,在分支业务 Phase1 完毕时开释,这时候其他本地业务就能读取到最新的数据。 – 一起,随着本地业务完毕,连接也得以开释。 – 分支业务中数据的大局锁在业务和谐器办理,在抉择 Phase2 大局提交时,大局锁马上能够开释,留意这里是先开释锁,再进行分支业务的提交进程。只需在抉择大局回滚的情况下,大局锁才被持有至分支的 Phase2 完毕,即一切分支业务回滚完毕。

这个规划,极大地减少了分支业务对资源(数据和连接)的锁定时刻,给全体并发和吞吐的提升供给了基础。但是分布式业务的阻隔级别变化了

基于Seata探寻分布式事务的实现方案

XA 形式和 下面的AT 形式相同是一种对业务无侵入性的处理计划;但与 AT 形式不同的是,XA 形式将快照数据和行锁等经过 XA 指令托付给了数据库来完结,这样 XA 形式完结愈加轻量化

2.3 AT

AT 形式是一种无侵入的分布式业务处理计划。在 AT 形式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 便是大局业务一阶段,Seata 结构会主动生成业务的二阶段提交和回滚操作。

一阶段

基于Seata探寻分布式事务的实现方案

首先,使用要使用 Seata 的 JDBC 数据源署理,也便是前面提到的 RM 概念,一切对 DB 的操作都是经过 Seata RM 署理完结。在这层署理中,Seata 会主动操控 SQL 的履行,提交,回滚。

Seata署理会把业务数据在更新前后的数据镜像(beforeImage & afterImage)组织成回滚日志,利用本地业务的 ACID 特性,将业务数据的更新和回滚日志的写入在同一个本地业务中提交。这样,能够确保:任何提交的业务数据的更新必定有相应的回滚日志存在。

然后,本地业务在提交之前, 还需求经过 RM 向 TC 注册本地分支,这个注册进程中会依据刚才履行的 SQL 拿到一切触及到的数据主键,以 resourceId + tableName + rowPK 作为锁的 key,向 TC 恳求一切触及数据的写锁,当取得一切相关数据的写锁后,再履行本地业务的 Commit 进程。假如有任何一行数据的写锁没有拿到的话,TC 会以 fastfail 的方式回复该 RM,RM 会以重试 + 超时机制重复该进程,直到超时。

完结本地业务后,RM 会向 TC 报告本地业务的履行情况,并完结业务 RPC 的调用进程。

二阶段

基于Seata探寻分布式事务的实现方案

case1:假如 TM 抉择是大局提交,此刻分支业务实践上现已完结提交,TC 立刻开释该大局业务的一切锁,然后异步调用 RM 整理回滚日志,Phase2 能够非常快速地完结。

基于Seata探寻分布式事务的实现方案

case2:假如抉择是大局回滚,RM 收到和谐器发来的回滚恳求,经过 XID 和 Branch ID 找到相应的回滚日志记载,经过回滚记载生成反向的更新 SQL 并履行,以完结分支的回滚。当分支回滚顺利完毕时,告诉 TC 回滚完结,这时候 TC 才开释该分支业务相关的一切锁。

注:RM 在进行回滚时,会先跟 afterImage 进行比较: – 假如共同:则履行逆向 SQL – 假如不共同: 再跟 beforeImage 进行比较 – 假如共同:阐明没必要履行回滚 SQL 了,数据现已康复了 – 假如不共同:阐明呈现了脏数据,这时候就抛出反常,需求人工处理

2.4 TCC

TCC 形式需求用户依据自己的业务场景完结 Try、Confirm 和 Cancel 三个操作;业务建议方先在 TC 中注册大局业务,然后在一阶段履行 Try 办法,在二阶段提交的话 TC 会去履行各个 RM 的 Confirm 办法,二阶段回滚则 TC 会去履行各个 RM 的 Cancel 办法。

基于Seata探寻分布式事务的实现方案

与 AT 形式相同,Seata 会给实践办法的履行加切面,该切面会阻拦一切对 TCC 接口的调用。在调用 Try 接口时,假如发现处在大局业务中,切面会先向 TC 注册一个分支业务,和 AT 不同的是TCC 注册分支业务是不加锁的,注册完结后去履行原来的 RPC 调用。当恳求链路调用完结后,TC 经过分支业务的资源 ID 回调到正确的参与者去履行对应 TCC 资源的 Confirm 或 Cancel 办法。

TCC 形式的全体结构相关于 AT 来说愈加简略,首要是扫描 TCC 接口,注册资源,阻拦接口调用,注册分支业务,终究回调二阶段接口。最中心的实践上是 TCC 接口的完结逻辑。

1)使用原则

从 TCC 模型的结构能够发现,TCC 模型的中心在于 TCC 接口的规划。用户在接入 TCC 时,大部分工作都集中在如何完结 TCC 服务上。这便是 TCC 形式最首要的问题,对业务侵入比较大,要花很大的功夫来完结 TCC 服务。

规划一套 TCC 接口最重要的是什么?首要有两点,第一点,需求将操作分红两阶段完结。TCC(Try-Confirm-Cancel)分布式业务模型相关于 XA 等传统模型,其特征在于它不依靠 RM 对分布式业务的支撑,而是经过对业务逻辑的分化来完结分布式业务。

TCC 分布式业务模型需求业务体系供给三段业务逻辑: 1. 开始操作 Try:完结一切业务检查,预留有必要的业务资源。 2. 承认操作 Confirm:真实履行的业务逻辑,不做任何业务检查,只使用 Try 阶段预留的业务资源。因而,只需 Try 操作成功,Confirm 有必要能成功。另外,Confirm 操作需满意幂等性,确保一笔分布式业务能且只能成功一次。 3. 撤销操作 Cancel:开释 Try 阶段预留的业务资源。同样的,Cancel 操作也需求满意幂等性。因而,TCC 模型的阻隔性思维便是经过业务的改造,在第一阶段完毕之后,从底层数据库资源层面的加锁过渡为上层业务层面的加锁,开释底层数据库锁资源,放宽分布式业务锁协议,将锁的粒度降到最低,以最大极限进步业务并发功能。

第二点,便是要依据自身的业务模型去操控并发,Seata 结构自身仅供给两阶段原子提交协议,确保分布式业务原子性。业务的阻隔需求交给业务逻辑来完结。阻隔的实质便是操控并发,避免并发业务操作相同资源而引起的成果错乱。例如:“账户 A 上有 100 元,业务 T1 要扣除其间的 30 元,业务 T2 也要扣除 30 元,呈现并发”。在第一阶段 Try 操作中,需求先利用数据库资源层面的加锁,检查账户可用余额,假如余额充足,则预留业务资源加到各自的冻住里,扣除本次买卖金额,一阶段完毕后,虽然数据库层面资源锁被开释了,但这笔资金被业务阻隔,不答应除本业务之外的其它并发业务动用。

2)反常操控

空回滚

空回滚便是关于一个分布式业务,在没有调用 TCC 资源 Try 办法的情况下,调用了二阶段的 Cancel 办法,Cancel 办法需求识别出这是一个空回滚,然后直接回来成功。

基于Seata探寻分布式事务的实现方案

Cancel 要识别出空回滚,直接回来成功。那关键便是要识别出这个空回滚。思路很简略便是需求知道一阶段是否履行,假如履行了,那便是正常回滚;假如没履行,那便是空回滚。因而,需求一张额外的业务操控表,其间有分布式业务 ID 和分支业务 ID,第一阶段 Try 办法里会刺进一条记载,表明一阶段履行了。Cancel 接口里读取该记载,假如该记载存在,则正常回滚;假如该记载不存在,则是空回滚。

悬挂

悬挂便是关于一个分布式业务,其二阶段 Cancel 接口比 Try 接口先履行。由于答应空回滚的原因,Cancel 接口认为 Try 接口没履行,空回滚直接回来成功,关于 Seata 结构来说,认为分布式业务的二阶段接口现已履行成功,整个分布式业务就完毕了。但是这之后 Try 办法才真实开始履行,预留业务资源,回想一下前面提到业务并发操控的业务加锁,关于一个 Try 办法预留的业务资源,只需该分布式业务才干使用,然而 Seata 结构认为该分布式业务现已完毕,也便是说,当呈现这种情况时,该分布式业务第一阶段预留的业务资源就再也没有人能够处理了。

比方在 RPC 调用时,先注册分支业务,再履行 RPC 调用,假如此刻 RPC 调用的网络发生拥堵,一般 RPC 调用是有超时时刻的,RPC 超时今后,建议方就会告诉 TC 回滚该分布式业务,或许回滚完结后,RPC 恳求才到达参与者,真实履行,然后形成悬挂。

基于Seata探寻分布式事务的实现方案

幂等

幂等便是关于同一个分布式业务的同一个分支业务,重复去调用该分支业务的第二阶段接口,因而,要求 TCC 的二阶段 Confirm 和 Cancel 接口确保幂等,不会重复使用或许开释资源。假如幂等操控没有做好,很有或许导致资损等严重问题。

处理思路

Try 办法首要需求考虑两个问题,一个是 Try 办法需求能够告诉二阶段接口,现已预留业务资源成功。第二个是需求检查第二阶段是否现已履行完结,假如已完结,则不再履行

Confirm 办法。由于 Confirm 办法不答应空回滚,也便是说,Confirm 办法必定要在 Try 办法之后履行。因而,Confirm 办法只需求关注重复提交的问题。需求一张业务履行记载表,能够先锁定业务记载,假如业务记载为空,则阐明是一个空提交,不答应,终止履行。假如业务记载不为空,则持续检查状况是否为初始化,假如是,则阐明一阶段正确履行,那二阶段正常履行即可。假如状况是已提交,则认为是重复提交,直接回来成功即可;假如状况是已回滚,也是一个反常,一个已回滚的业务,不能从头提交,需求能够阻拦到这种反常情况,并报警。

Cancel 办法。由于 Cancel 办法答应空回滚,而且要在先履行的情况下,让 Try 办法感知到 Cancel 现已履行,所以和 Confirm 办法略有不同。首先依然是锁定业务记载。假如业务记载为空,则认为 Try 办法还没履行,便是空回滚。空回滚的情况下,应该先刺进一条业务记载,确保后续的 Try 办法不会再履行。假如刺进成功,则阐明 Try 办法还没有履行,空回滚持续履行。假如刺进失利,则认为Try 办法正在履行,等待 TC 的重试即可。假如一开始读取业务记载不为空,则阐明 Try 办法现已履行完毕,再检查状况是否为初始化,假如是,则还没有履行过其他二阶段办法,正常履行 Cancel 逻辑。假如状况为已回滚,则阐明这是重复调用,答应幂等,直接回来成功即可。假如状况为已提交,则同样是一个反常,一个已提交的业务,不能再次回滚

2.5 Saga

Saga 形式是 Seata 行将开源的长业务处理计划。在 Saga 形式下,分布式业务内有多个参与者,每一个参与者都是一个冲正补偿服务,需求用户依据业务场景完结其正向操作和逆向回滚操作。

分布式业务履行进程中,依次履行各参与者的正向操作,假如一切正向操作均履行成功,那么分布式业务提交。假如任何一个正向操作履行失利,那么分布式业务会去退回去履行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式业务回到初始状况。

基于Seata探寻分布式事务的实现方案

Saga 正向服务与补偿服务也需求业务开发者完结。有点像是 TCC 形式将 Try 进程和 Confirm 进程兼并,一切参与者直接履行 Try + Confirm,假如有人失利了,就反向依次 Cancel。

由于该形式首要用于长业务场景,所以一般是由事情驱动的,各个参与者之间是异步履行的。

Saga 形式适用于业务流程长且需求确保业务终究共同性的业务体系,Saga 形式一阶段就会提交本地业务,无锁、长流程情况下能够确保功能

Saga形式的优势是:

  • 一阶段提交本地数据库业务,无锁,高功能;
  • 参与者能够采用业务驱动异步履行,高吞吐;
  • 补偿服务即正向服务的“反向”,易于了解,易于完结;

缺点:Saga 形式由于一阶段现已提交本地数据库业务,且没有进行“预留”动作,所以不能确保阻隔性。

业务阻隔

纵观 Seata 供给的一切分支业务形式, 除了 AT 形式和 XA 形式能够运转在读已提交的阻隔级别下, 其他形式都是运转在读未提交的级别下。在有必要时,使用需求经过业务逻辑的巧妙设定,来处理分布式业务阻隔级别带来的问题

AT 形式经过大局写排他锁,来确保业务间的写阻隔,将大局业务默认界说在读未提交的阻隔级别上,大局业务读未提交,并不是说本地业务的db数据没有正常提交,而是指大局业务二阶段commit | rollback未真实处理完(即未开释大局锁),而且这时候其他业务会读到一阶段提交的内容。

有些使用假如需求到达大局的读已提交,AT 也供给了相应的机制来到达意图,那便是 select for update +@GlobalLock, 当履行该命令时 RM 会去 TC 承认该锁是否由别人占有, 这样假如有一个分布式业务 T1 正在进行中时, 另一个业务 T2 会由于发现锁冲突而阻塞后续代码的履行, 当时面的分布式业务 T1 完毕时, 开释了相应的资源锁, T2 才干读取到相应的数据, 这样就到达读已提交的作用

2.6 音讯组件

利用 MQ 组件完结的二阶段提交。此计划触及 3 个模块:

  • 上游使用,履行业务并发送 MQ 音讯。
  • 可靠音讯服务和 MQ 音讯组件,和谐上下流音讯的传递,并确保上下流数据的共同性。
  • 下流使用,监听 MQ 的音讯并履行自身业务。

上游使用将本地业务履行和音讯发送绑定在同一个本地业务中,确保要么本地操作成功并发送 MQ 音讯,要么两步操作都失利并回滚。

基于Seata探寻分布式事务的实现方案

  1. 上游使用发送待承认音讯到可靠音讯体系
  2. 可靠音讯体系保存待承认音讯并回来
  3. 上游使用履行本地业务
  4. 上游使用告诉可靠音讯体系承认业务已履行并发送音讯。
  5. 可靠音讯体系修正音讯状况为发送状况并将音讯投递到 MQ 中心件。

基于Seata探寻分布式事务的实现方案

  1. 下流使用监听 MQ 音讯组件并获撤销息
  2. 下流使用依据 MQ 音讯体信息处理本地业务
  3. 下流使用向 MQ 组件主动发送 ACK 承认音讯被消费
  4. 下流使用告诉可靠音讯体系音讯被成功消费,可靠音讯将该音讯状况更改为已完结。

基于Seata探寻分布式事务的实现方案

反常处理

上游反常
可靠音讯服务定时监听音讯的状况,假如存在状况为待承认而且超时的音讯,则表明上游使用和可靠音讯交互中的过程 4 或许 5 呈现反常。

基于Seata探寻分布式事务的实现方案

  1. 可靠音讯查询超时的待承认状况的音讯
  2. 向上游使用查询业务履行的情况
  3. 业务未履行,则删除该音讯,确保业务和可靠音讯服务的共同性。业务已履行,则修正音讯状况为已发送,并发送音讯到 MQ 组件。

下流反常

基于Seata探寻分布式事务的实现方案

  1. 可靠音讯服务定时查询状况为已发送并超时的音讯
  2. 可靠音讯将音讯从头投递到 MQ 组件中
  3. 下流使用监听音讯,在满意幂等性的条件下,从头履行业务。
  4. 下流使用告诉可靠音讯服务该音讯现已成功消费。

实践进程中,还需求引进人工干预功能。比方引进重发次数约束,超越重发次数约束的将音讯修正为逝世音讯,等待人工处理。

3 总结

3.1 sql支撑上

AT其实便是一个自完结的XA业务,其实能够知道,AT在sql支撑上远不及XA形式,AT需求做sql解析背后的完结只能自己处理,目前只能靠社区的贡献者来供给处理计划,这是一个长期的关键性的问题,也有许多用户挑选在AT形式上重写sql来获取AT形式的支撑,sql支撑上XA是完胜的。

3.2 阻隔性

AT形式是经过解析sql获取触及的主键id,生成行锁。也便是AT形式的阻隔靠的是大局锁来确保的,粒度细至行级。锁信息存储在seata server侧。XA的阻隔级别是由本地数据库确保,锁存储在各个本地数据库中。由于XA形式一旦履行了prepare后,再也无法重入这个XA业务也无法跟其他XA业务同享锁,由于XA协议仅仅是经过XID来start一个业务,自身不存在分支业务的说法。也便是说他只管自己

3.3 侵略性

经过上面的信息能够发现谁更底层,侵略性则更小,所以由数据库自身支撑的XA形式来说,侵略性无疑最小,使用本钱最低。 XA的RM实践是在数据库,而AT则是以中心件层部署在使用这一侧的,不依靠数据库自身的协议支撑,这点关于微服务架构来说是至关重要的。使用层不需求为本地业务和分布式业务多类不同场景来适配多套不通的驱动

基于Seata探寻分布式事务的实现方案

3.4 补偿性业务的问题

实质上seata结构支撑了3大补偿业务形式,AT, TCC,Saga都是补偿型的。补偿型业务处理机制是构建在业务资源之上的,业务资源自身对分布式业务是无感知的,业务资源对分布式业务无感知存在一个根本性问题,便是无法做到真实的大局共同性。 比方一条库存记载,处在补偿型业务处理进程中,由100扣减为50,此刻仓库办理员连接数据库检查就会查询到50,之后业务反常回滚,库存就会被补偿回滚为100,明显仓库办理员查询到的50便是脏数据。那XA的价值是什么,与补偿型业务不同,XA协议要求业务资源自身供给对标准和协议的支撑。由于业务资源感知并参与分布式业务处理进程,所以业务资源能够确保从任意视角对数据的拜访有用阻隔,比方上面XA业务处理进程中,中心态的50是不会被查询到的(当然阻隔级别要在读已提交以上),以此来满意大局共同性。