乐趣区

关于数据库:分布式事务最经典的七种解决方案

随着业务的疾速倒退、业务复杂度越来越高,简直每个公司的零碎都会从单体走向分布式,特地是转向微服务架构。随之而来就必然遇到分布式事务这个难题,这篇文章总结了分布式事务最经典的解决方案,分享给大家。

基础理论

在解说具体计划之前,咱们先理解一下分布式事务所波及到的基础理论常识。

咱们拿转账作为例子,A 须要转 100 元给 B,那么须要给 A 的余额 -100 元,给 B 的余额 +100 元,整个转账要保障,A-100 和 B +100 同时胜利,或者同时失败。看看在各种场景下,是如何解决这个问题的。

事务

把多条语句作为一个整体进行操作的性能,被称为数据库事务。数据库事务能够确保该事务范畴内的所有操作都能够全副胜利或者全副失败。

事务具备 4 个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为 ACID 个性。

  • Atomicity(原子性):一个事务中的所有操作,要么全副实现,要么全副不实现,不会完结在两头某个环节。事务在执行过程中产生谬误,会被复原到事务开始前的状态,就像这个事务素来没有执行过一样。
  • Consistency(一致性):在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。完整性包含外键束缚、利用定义的等束缚不会被毁坏。
  • Isolation(隔离性):数据库容许多个并发事务同时对其数据进行读写和批改的能力,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。
  • Durability(持久性):事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。

分布式事务

银行跨行转账业务是一个典型分布式事务场景,假如 A 须要跨行转账给 B,那么就波及两个银行的数据,无奈通过一个数据库的本地事务保障转账的 ACID,只可能通过分布式事务来解决。

分布式事务就是指事务的发起者、资源及资源管理器和事务协调者别离位于分布式系统的不同节点之上。在上述转账的业务中,用户 A -100 操作和用户 B +100 操作不是位于同一个节点上。实质上来说,分布式事务就是为了保障在分布式场景下,数据操作的正确执行。

分布式事务在分布式环境下,为了满足可用性、性能与降级服务的须要,升高一致性与隔离性的要求,一方面遵循 BASE 实践(BASE 相干实践,波及内容十分多,感兴趣的同学,能够参考 BASE 实践):

根本业务可用性(Basic Availability)
柔性状态(Soft state)
最终一致性(Eventual consistency)
同样的,分布式事务也局部遵循 ACID 标准:

原子性:严格遵循
一致性:事务实现后的一致性严格遵循;事务中的一致性可适当放宽
隔离性:并行事务间不可影响;事务两头后果可见性容许平安放宽
持久性:严格遵循

分布式事务的解决方案

两阶段提交 /XA

XA 是由 X /Open 组织提出的分布式事务的标准,XA 标准次要定义了 (全局) 事务管理器 (TM) 和(部分)资源管理器 (RM) 之间的接口。本地的数据库如 mysql 在 XA 中表演的是 RM 角色

XA 一共分为两阶段:

第一阶段(prepare):即所有的参与者 RM 筹备执行事务并锁住须要的资源。参与者 ready 时,向 TM 报告已准备就绪。
第二阶段 (commit/rollback):当事务管理者 (TM) 确认所有参与者 (RM) 都 ready 后,向所有参与者发送 commit 命令。
目前支流的数据库根本都反对 XA 事务,包含 mysql、oracle、sqlserver、postgre

XA 事务由一个或多个资源管理器(RM)、一个事务管理器(TM)和一个应用程序(ApplicationProgram)组成。

把下面的转账作为例子,一个胜利实现的 XA 事务时序图如下:

如果有任何一个参与者 prepare 失败,那么 TM 会告诉所有实现 prepare 的参与者进行回滚。

XA 事务的特点是:

  • 简略易了解,开发较容易
  • 对资源进行了长时间的锁定,并发度低

如果读者想要进一步钻研 XA,go 语言可参考 DTM,java 语言可参考 seata

SAGA

Saga 是这一篇数据库论文 saga 提到的一个计划。其核心思想是将长事务拆分为多个本地短事务,由 Saga 事务协调器协调,如果失常完结那就失常实现,如果某个步骤失败,则依据相同程序一次调用弥补操作。

把下面的转账作为例子,一个胜利实现的 SAGA 事务时序图如下:


SAGA 事务的特点:

  • 并发度高,不必像 XA 事务那样长期锁定资源
  • 须要定义失常操作以及弥补操作,开发量比 XA 大
  • 一致性较弱,对于转账,可能产生 A 用户已扣款,最初转账又失败的状况

论文外面的 SAGA 内容较多,包含两种复原策略,包含分支事务并发执行,咱们这里的探讨,仅包含最简略的 SAGA

SAGA 实用的场景较多,长事务实用,对两头后果不敏感的业务场景实用

如果读者想要进一步钻研 SAGA,go 语言可参考 DTM,java 语言可参考 seata

TCC

对于 TCC(Try-Confirm-Cancel)的概念,最早是由 Pat Helland 于 2007 年发表的一篇名为《Life beyond Distributed Transactions:an Apostate’s Opinion》的论文提出。

TCC 分为 3 个阶段

  • Try 阶段:尝试执行,实现所有业务查看(一致性), 预留必须业务资源(准隔离性)
  • Confirm 阶段:确认执行真正执行业务,不作任何业务查看,只应用 Try 阶段预留的业务资源,Confirm 操作要求具备幂等设计,Confirm 失败后须要进行重试。
  • Cancel 阶段:勾销执行,开释 Try 阶段预留的业务资源。Cancel 阶段的异样和 Confirm 阶段异样解决计划基本上统一,要求满足幂等设计。

把下面的转账作为例子,通常会在 Try 外面解冻金额,但不扣款,Confirm 外面扣款,Cancel 外面冻结金额,一个胜利实现的 TCC 事务时序图如下:

TCC 特点如下:

  • 并发度较高,无长期资源锁定。
  • 开发量较大,须要提供 Try/Confirm/Cancel 接口。
  • 一致性较好,不会产生 SAGA 已扣款最初又转账失败的状况
  • TCC 实用于订单类业务,对中间状态有束缚的业务

如果读者想要进一步钻研 TCC,go 语言可参考 DTM,java 语言可参考 seata

本地音讯表

本地音讯表这个计划最后是 ebay 架构师 Dan Pritchett 在 2008 年发表给 ACM 的文章。设计外围是将须要分布式解决的工作通过音讯的形式来异步确保执行。

大抵流程如下:


写本地音讯和业务操作放在一个事务里,保障了业务和发消息的原子性,要么他们全都胜利,要么全都失败。

容错机制:

  • 扣减余额事务 失败时,事务间接回滚,无后续步骤
  • 轮序生产音讯失败,减少余额事务失败都会进行重试

本地音讯表的特点:

  • 长事务仅须要分拆成多个工作,应用简略
  • 生产者须要额定的创立音讯表
  • 每个本地音讯表都须要进行轮询
  • 消费者的逻辑如果无奈通过重试胜利,那么还须要更多的机制,来回滚操作

实用于可异步执行的业务,且后续操作无需回滚的业务

事务音讯

在上述的本地音讯表计划中,生产者须要额定创立音讯表,还须要对本地音讯表进行轮询,业务累赘较重。阿里开源的 RocketMQ 4.3 之后的版本正式反对事务音讯,该事务音讯实质上是把本地音讯表放到 RocketMQ 上,解决生产端的音讯发送与本地事务执行的原子性问题。

事务音讯发送及提交:

  • 发送音讯(half 音讯)
  • 服务端存储音讯,并响应音讯的写入后果
  • 依据发送后果执行本地事务(如果写入失败,此时 half 音讯对业务不可见,本地逻辑不执行)
  • 依据本地事务状态执行 Commit 或者 Rollback(Commit 操作公布音讯,音讯对消费者可见)

失常发送的流程图如下:

弥补流程:

对没有 Commit/Rollback 的事务音讯(pending 状态的音讯),从服务端发动一次“回查”
Producer 收到回查音讯,返回音讯对应的本地事务的状态,为 Commit 或者 Rollback
事务音讯计划与本地音讯表机制十分相似,区别次要在于原先相干的本地表操作替换成了一个反查接口

事务音讯特点如下:

  • 长事务仅须要分拆成多个工作,并提供一个反查接口,应用简略
  • 消费者的逻辑如果无奈通过重试胜利,那么还须要更多的机制,来回滚操作

实用于可异步执行的业务,且后续操作无需回滚的业务

如果读者想要进一步钻研事务音讯,可参考 rocketmq,为了不便大家学习事务音讯,DTM 也提供了简略实现

最大致力告诉

发动告诉方通过肯定的机制最大致力将业务处理结果告诉到接管方。具体包含:

有肯定的音讯反复告诉机制。因为接管告诉方可能没有接管到告诉,此时要有肯定的机制对音讯反复告诉。
音讯校对机制。如果尽最大致力也没有告诉到接管方,或者接管方生产音讯后要再次生产,此时可由接管方被动向告诉方查问音讯信息来满足需要。
后面介绍的的本地音讯表和事务音讯都属于可靠消息,与这里介绍的最大致力告诉有什么不同?

可靠消息一致性,发动告诉方须要保障将音讯收回去,并且将音讯发到接管告诉方,音讯的可靠性要害由发动告诉方来保障。

最大致力告诉,发动告诉方尽最大的致力将业务处理结果告诉为接管告诉方,然而可能音讯接管不到,此时须要接管告诉方被动调用发动告诉方的接口查问业务处理结果,告诉的可靠性要害在接管告诉方。

解决方案上,最大致力告诉须要:

  • 提供接口,让承受告诉放可能通过接口查问业务处理结果
  • 音讯队列 ACK 机制,音讯队列依照距离 1min、5min、10min、30min、1h、2h、5h、10h 的形式,逐渐拉大告诉距离,直到达到告诉要求的工夫窗口下限。之后不再告诉

最大致力告诉实用于业务告诉类型,例如微信交易的后果,就是通过最大致力告诉形式告诉各个商户,既有回调告诉,也有交易查问接口

AT 事务模式

这是阿里开源我的项目 seata 中的一种事务模式,在蚂蚁金服也被称为 FMT。长处是该事务模式应用形式,相似 XA 模式,业务无需编写各类弥补操作,回滚由框架主动实现,毛病也相似 AT,存在较长时间的锁,不满足高并发的场景。有趣味的同学能够参考 seata-AT

分布式事务中的网络异样

在分布式事务的各个环节都有可能呈现网络以及业务故障等问题,这些问题须要分布式事务的业务方做到防空回滚,幂等,防悬挂三个个性,上面以 TCC 事务阐明这些异常情况:

空回滚:

在没有调用 TCC 资源 Try 办法的状况下,调用了二阶段的 Cancel 办法,Cancel 办法须要辨认出这是一个空回滚,而后间接返回胜利。

呈现起因是当一个分支事务所在服务宕机或网络异样,分支事务调用记录为失败,这个时候其实是没有执行 Try 阶段,当故障复原后,分布式事务进行回滚则会调用二阶段的 Cancel 办法,从而造成空回滚。

幂等

因为任何一个申请都可能呈现网络异样,呈现反复申请,所以所有的分布式事务分支,都须要保障幂等性

悬挂:

悬挂就是对于一个分布式事务,其二阶段 Cancel 接口比 Try 接口先执行。

呈现起因是在 RPC 调用分支事务 try 时,先注册分支事务,再执行 RPC 调用,如果此时 RPC 调用的网络产生拥挤,RPC 超时当前,TM 就会告诉 RM 回滚该分布式事务,可能回滚实现后,RPC 申请才达到参与者真正执行。

上面看一个网络异样的时序图,更好的了解上述几种问题

业务解决申请 4 的时候,Cancel 在 Try 之前执行,须要解决空回滚
业务解决申请 6 的时候,Cancel 反复执行,须要幂等
业务解决申请 8 的时候,Try 在 Cancel 后执行,须要解决悬挂

面对上述简单的网络异常情况,目前看到各家倡议的计划都是业务方通过惟一键,去查问相关联的操作是否已实现,如果已实现则间接返回胜利。相干的判断逻辑较简单,易出错,业务负担重。

这里咱们提出一项子事务屏障技术,这是咱们独创的技术。在咱们的我的项目 DTM 中,提供了办法 ThroughBarrierCall,办法的原型为:

func ThroughBarrierCall(db *sql.DB, transInfo *TransInfo, busiCall BusiFunc)

业务开发人员,在 busiCall 外面编写本人的相干逻辑,调用该函数。ThroughBarrierCall 保障,在空回滚、悬挂等场景下,busiCall 不会被调用;在业务被反复调用时,有幂等管制,保障只被提交一次。

子事务屏障会治理 TCC、SAGA、XA、事务音讯等,也能够扩大到其余畛域

子事务屏障技术的原理是,在本地数据库,建设分支事务状态表 sub_trans_barrier,惟一键为全局事务 id- 子事务 id- 子事务分支名称(try|confirm|cancel)

  • 开启事务
  • 如果是 Try 分支,则那么 insert ignore 插入 gid-branchid-try,如果胜利插入,则调用屏障内逻辑
  • 如果是 Confirm 分支,那么 insert ignore 插入 gid-branchid-confirm,如果胜利插入,则调用屏障内逻辑
  • 如果是 Cancel 分支,那么 insert ignore 插入 gid-branchid-try,再插入 gid-branchid-cancel,如果 try 未插入并且 cancel 插入胜利,则调用屏障内逻辑
  • 屏障内逻辑返回胜利,提交事务,返回胜利
  • 屏障内逻辑返回谬误,回滚事务,返回谬误

在此机制下,解决了网络异样相干的问题

  • 空弥补管制 – 如果 Try 没有执行,间接执行了 Cancel,那么 Cancel 插入 gid-branchid-try 会胜利,不走屏障内的逻辑,保障了空弥补管制
  • 幂等管制 – 任何一个分支都无奈反复插入惟一键,保障了不会反复执行
  • 防悬挂管制 –Try 在 Cancel 之后执行,那么插入的 gid-branchid-try 不胜利,就不执行,保障了防悬挂管制

对于 SAGA 事务,也是相似的机制。

子事务屏障技术,为 DTM 独创,它的意义在于设计简略易实现的算法,提供了简略易用的接口,在独创,它的意义在于设计简略易实现的算法,提供了简略易用的接口,在这两项的帮忙下,开发人员彻底的从网络异样的解决中解放出来。

该技术目前须要搭配 DTM 事务管理器,目前 SDK 曾经提供给 go 语言的开发者。其余语言的 sdk 正在布局中。对于其余的分布式事务框架,只有提供了适合的分布式事务信息,可能依照上述原理,疾速实现该技术。

总结

本文介绍了分布式事务的一些基础理论,并对罕用的分布式事务计划进行了解说,在文章的后半局部还给出了事务异样的起因、分类以及优雅的解决方案。

分布式事务自身就是一个技术难题,业务中具体应用哪种计划还是须要依据本身业务特点自行抉择,每种计划在理论执行过程中须要思考的点都十分多,复杂度较大,所以审慎应用分布式事务。

退出移动版