分布式事务一文总结
概念介绍
什么是事务
事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。程序执行单元里有一系列操作,要么全部执行,要么全部不执行。
事务的四大特性
原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
- 原子性(atomicity)。一个事务是一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。
- 一致性(consistency)。事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。
- 隔离性(isolation)。一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability)。持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
事务的隔离级别
什么是分布式
分布式是架构演进的结果,解决了单机瓶颈问题。
(图省略)
单机、集群、分布式、微服务、SOA之间的区别
名词 |
概念 |
单机 |
所有业务全部写在一个项目中,部署服务到一台服务器上,所有请求业务都由这台服务器处理 |
集群 |
单机的多实例。在多个服务器上部署多个服务,每个服务就是一个节点,部署N个节点,处理业务的能力就提升N倍,这些节点的结合就叫做集群 |
分布式 |
分布式结构就是一个完整的系统,按照业务功能,拆分成一个个独立的子系统,在分布式结构中,每个子系统就被称为“服务”。这些子系统能够独立运行在Web容器中,他们之间通过RPC方式通信。 |
微服务 |
一种软件开发技术- 面向服务的体系结构(SOA)架构样式的一种变体,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相沟通(通常是基于HTTP的RESTful API)。 |
SOA |
面向服务架构(SOA)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协议联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构建在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。 |
单机和集群的区别
集群是单机的多实例。
集群和分布式的区别
分布式:将一套系统拆分成不同子系统部署在不同服务器上
集群:部署多个相同的子系统在不同的服务器上
分布式和微服务的区别
微服务是一种架构风格,分布式是一种部署模式。两者都是把一个模块拆分多个模块完成一件事情。
微服务架构下多个服务可能部署在同一台服务器上,而分布式一定是多台服务器。
微服务和SOA的区别
微服务去中心化,去掉ESB企业总线。微服务不再强调传统SOA架构里面比较重的ESB企业服务总线,同时SOA的思想进入到单个业务系统内部实现真正的组件化。
SOA注重的是系统集成方面,而微服务关注的是完全分离。
什么是分布式事务
分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
分布式事务产生的原因
数据库分库分表
在单库单表场景下,当业务数据量达到单库单表的极限时,就需要考虑分库分表,将之前的单库单表拆分成多库多表;
分库分表之后,原来在单个数据库上的事务操作,可能就变成跨多个数据库的操作,此时就需要使用分布式事务;
业务服务化
在业务发展初期,“一块大饼”的单业务系统架构,能满足基本的业务需求。但是随着业务的快速发展,系统的访问量和业务复杂程度都在快速增长,单系统架构逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求越来越强烈。
如下图所示,按照面向服务架构(SOA)的设计原则,将单业务系统拆分成多个业务系统,降低了各系统之间的耦合度,使不同的业务系统专注于自身业务,更有利于业务的发展和系统容量的伸缩。
分布式事务和本地事务的区别
本地事务是在单个数据源上进行数据的访问和更新,而分布式事务是跨越多个数据源来进行数据的访问和更新。
分布式事务的问题/需求是随着系统分布式化后必然产生的,目的或本质是追求分布式的多个DB数据的一致性。
和本地数据库事务的本质原理是一样的。只不过是放大到一套技术栈中去实现,更多考虑因素和复杂度。
分布式环境的问题
通信异常
从集中式到分布式,必然引入了网络因素,而由于网络本身的不可靠性,因此就引入了额外的问题。分布式系统各节点之间的网络通信能够正常进行,其延时也会远大于单机操作,在消息的收发过程中,消息丢失和消息延迟变得十分普遍。
网络分区
当网络发生异常情况时,导致分布式系统中部分节点之间的网络延时不断增大,最终导致组成分布式系统的所有节点中,只有部分节点之间能够正常通信,而另一些节点则不能,这种现象称之为网络分区,当网络分区出现时,分布式系统会出现局部小集群,在极端情况下,这些局部小集群会独立完成原本需要整个分布式系统才能完成的功能,包括对数据的事务处理,这就对分布式一致性提出了非常大的挑战。
三态
由于网络可能会出现各种各样的问题,因此分布式系统的每一次请求与响应,存在特有的三态概念:成功、失败、超时。当网络在异常情况下,可能会出现超时现象,通常由以下两种情况:
- 由于网络原因,该请求并没有被成功地发送到接收方,而是在发送过程就发生了消息丢失现象。
- 该请求成功的被接收方接受后,并进行了处理,但是在将响应反馈给发送方时,发生了消息丢失现象。
节点故障
节点故障是指组成分布式系统的服务器节点出现宕机或僵死现象,每个节点都有可能出现故障,并且每天都在发生。
CAP原则
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):保证每个请求不管成功或者失败都有响应。
分区容错性(P):系统中任意信息的丢失或失败不会影响系统的继续运作。 [1]
CAP原则的精髓就是要么AP,要么CP,要么AC,但是不存在CAP。
为什么CAP三个要素最多只能同时实现两点
(1)当前满足CA(一致性+可用性),能否也满足P(分区容错性)?
假设当前分布式系统满足CA,C要求各个客户端读到的数据必须是一致的,考虑发生网络分区的情况,这个时候各个服务器存在数据不一致,那么根据C一致性要求,系统是不可以对外提供服务的(因为不同的客户端访问同一份数据会得到不同的结果),那么也就违背了分区容错性P。
但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的。
(2)当前满足P(分区容错性),CA(一致性+可用性)能否同时满足?
假设当前分布式系统满足P, 在网络发生分区的情况下,假设有1000台服务器,各服务器数据间数据进行同步。
- 满足C一致性:当1000台机器数据不一致时,需要时间进行数据同步,无法立即返回结果,无法满足可用性。
- 满足A可用性:当用户请求时,为保证可用性,立即返回数据。但返回的数据可能不是最新的,无法满足一致性。
CAP选择哪两个场景合适
为什么分区容错性P是必须的
对于一个业务系统来说,可用性和分区容错性是必须要满足的两个条件,并且这两者是相辅相成的。业务系统之所以使用分布式系统,主要原因有两个:
- 提升整体性能
当业务量猛增,单个服务器已经无法满足我们的业务需求的时候,就需要使用分布式系统,使用多个节点提供相同的功能,从而整体上提升系统的性能,这就是使用分布式系统的第一个原因。 - 实现分区容错性
单一节点 或 多个节点处于相同的网络环境下,那么会存在一定的风险,万一该机房断电、该地区发生自然灾害,那么业务系统就全面瘫痪了。为了防止这一问题,采用分布式系统,将多个子系统分布在不同的地域、不同的机房中,从而保证系统高可用性。
这说明分区容错性是分布式系统的根本,如果分区容错性不能满足,那使用分布式系统将失去意义。
CP还是AP
对于CP来说,放弃可用性,追求一致性和分区容错性,例如zookeeper、redis、hbase、银行转账。
对于AP来说,放弃一致性(这里说的一致性是放弃强一致性,但是最终一致性还是有的),追求分区容错性和可用性,这是很多分布式系统设计时的选择,BASE也是根据AP来扩展的。
BASE理论
BASE 理论是对 CAP 中的一致性和可用性进行一个权衡的结果,理论的核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性。
单数据库事务完全遵循ACID规范,属于刚性事务,分布式事务要完全遵循ACID规范比较困难, 分布式事务属于柔性事务,满足BASE理论。
- BA:Basic Available 基本可用
- 整个系统在某些不可抗力的情况下,仍然能够保证“可用性”,即一定时间内仍然能够返回一个明确的结果。只不过“基本可用”和“高可用”的区别是:
- “一定时间”可以适当延长
当举行大促时,响应时间可以适当延长 - 给部分用户返回一个降级页面
给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意,返回降级页面仍然是返回明确结果。
- S:Soft State:柔性状态
同一数据的不同副本的状态,可以不需要实时一致。 - E:Eventual Consisstency:最终一致性
同一数据的不同副本的状态,可以不需要实时一致,但一定要保证经过一定时间后仍然是一致的。
ACID、CAP、BASE理论之间的关系
ACID 是数据库事务完整性的理论。
CAP 是分布式系统设计理论。
BASE 是 CAP 理论中 AP(可用性+分区容错性) 方案的延伸。
一致性的分类
强一致性
这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大。
弱一致性
这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不久承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态。
最终一致性
最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型。
强一致性解决方案
XA规范
XA一般是指由 X/Open 组织提出的分布式事务处理的规范,XA 规范主要定义了事务管理器(Transaction Manager)和局部资源管理器(Local Resource Manager)之间的接口。
以下的函数使事务管理器可以对资源管理器进行的操作:
1)xa_open,xa_close:建立和关闭与资源管理器的连接。
2)xa_start,xa_end:开始和结束一个本地事务。
3)xa_prepare,xa_commit,xa_rollback:预提交、提交和回滚一个本地事务。
4)xa_recover:回滚一个已进行预提交的事务。
5)ax_开头的函数使资源管理器可以动态地在事务管理器中进行注册,并可以对XID(TRANSACTION IDS)进行操作。
6)ax_reg,ax_unreg;允许一个资源管理器在一个TMS(TRANSACTION MANAGER SERVER)中动态注册或撤消注册。
2PC
2PC ,全称 “two phase commit”,即两阶段提交协议,是将整个事务流程分为两个阶段:准备阶段(prepare p)、提交阶段(commit phase),2是指两个阶段,P是指准备阶段,C是指提交阶段。
2PC 通常使用到XA中的三个角色RM、TM、AP。
- AP:事务发起方,通常为微服务自身;定义事务边界(事务开始、结束),并访问事务边界内的资源
- TM:事务协调方,事务操作总控;管理事务全局事务,分配事务唯一标识,监控事务的执行进度,负责事务的提交、回滚、失败恢复。
- RM:本地事务资源,根据协调方命令进行操作;管理本地共享资源(即数据库)。
两个阶段过程
- 准备阶段(Prepare phase):事务管理器给每个参与者发送Prepare消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。 (Undo日志是记录修改前的数据,用于数据库回滚,Redo日志是记录修改后的数据,用于提交事务后写入数据文件)
- 提交阶段(commit phase):如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息;否则,发送提交(Commit)消息;参与者根据事务管理器的指令执行提交或者回滚操作,并释放事务处理过程中使用的锁资源。注意:必须在最后阶段释放锁资源。
2PC的异常场景处理机制
参与者挂掉
如果在第一阶段,协调者发送Prepare指令给所有的参与者后,参与者挂掉了,那么此时协调者因为迟迟收不到参与者的消息而导致超时,所以协调者在超时之后会统一发送abort指令进行事务回滚。
如果在第二阶段,协调者发送commit或者abort指令给所有参与者后,参与者挂掉了,那么协调者会在超时之后进行消息重发,直到参与者恢复后收到到commit或者abort ,向协调者返回成功。
协调者挂掉
协调者在第一阶段发送Prepare指令后挂掉,那么此时参与者此时会一直得不到协调者下一步的指令,那么此时参与者会一直陷入阻塞状态,资源也会一直被锁住,直到协调者恢复之后向参与者发出下一步的指令。
协调者在第二阶段挂掉,那么此时协调者已向所有者发出最后阶段的指令了,所以收到指令的参与者会完成最后的commit或rollback操作,对于参与者来说事务已经结束,所以不存在阻塞和锁的问题, 当协调者恢复后,会把事务日志状态标记为结束。
因为网络分区消息丢失
在第一阶段,协调者发送给参与者的消息丢失了,那么此时参与者会因为没有收到消息不会执行任何动作,所以也不会响应协调者任何消息,此时协调者会因为没有收到参与者的响应而超时,所以协调者会决定决定回滚事务,向所有参与者发送abort指令。
在第二阶段,无论是协调者发送给参与者消息丢失、还是参与者响应协调者消息丢失,都会导致协调者超时,所以这种时候协调者会进行重试,直到所有参与者都响应成功。
极端情况下数据不一致的风险
我们发现在一般的的场景里,出现了问题2PC好像都能解决 ,但我们去抽丝剥茧的挖细节的时,就会发现2PC在某些场景会出现数据不一致的情况。
比如说,协调者在第二阶段向部分参与者发送了commit指令后挂了,那么此时收到了commit指令的参与者会进行事务提交,然后未收到消息的参与者还是等着协调者的指令,所以这个时候会产生数据的不一致,此时必须要等协调者恢复之后重新发送指令,参与者才能达到最终的一致状态。
还有如果在第二阶段网络发生问题导致部分消息丢失,有些参与者收到了commit指令,有些参与者还没有收到commit指令,结果收到了指令的参与者提交了事务,没收到消息的参与者还在等指令,它不知道该进行回滚还是提交,这个时候同样也会产生数据不一致的问题。
2PC的问题
性能问题
从事务开始到事务最终提交或回滚,这期间所有参与者的资源一致处于锁定状态,所以注定2PC的性能不会太高。
数据不一致风险
从上面我们分析知道,极端情况下不管是由于协调者故障,还是网络分区都会有导致数据不一致的风险。
协调者故障导致的事务阻塞问题
在两阶段提交协议里,我们会发现协调者是一个至关重要角色,参与者无论任何时候出问题,都会在因为协调者没收到参与者的消息而超时,协调者超时之后任然能做出下一步的决策。
但是协调者问题后,流程就没办法继续了,此时参与者因为没有收到协调者下一步指令不知道是该进行commit还是rollback,所有的参与者必须等待协调者恢复之后才能做出下一步的动作。
3PC
3PC,全称 “three phase commit”,是 2PC 的改进版,将 2PC 的 “提交事务请求” 过程一分为二,共形成了由CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。
阶段一:CanCommit
第一个阶段: CanCommit
① 事务询问
协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。
② 各参与者向协调者反馈事务询问的响应参与者在接收到来自协调者的包含了事务内容的canCommit请求后,正常情况下,如果自身认为可以顺利执行事务,则反馈Yes响应,并进入预备状态,否则反馈No响应。
阶段二:PreCommit
协调者在得到所有参与者的响应之后,会根据结果有2种执行操作的情况:执行事务预提交,或者中断事务假如所有参与反馈的都是Yes,那么就会执行事务预提交。
1. 执行事务预提交分为 3 个步骤
① 发送预提交请求:协调者向所有参与者节点发出preCommit请求,并进入prepared阶段
② 事务预提交:参与者接收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中。
③ 各参与者向协调者反馈事务执行的结果:若参与者成功执行了事务操作,那么反馈Ack
若任一参与者反馈了No响应,或者在等待超时后,协调者尚无法接收到所有参与者反馈,则中断事务
2. 中断事务也分为3个步骤:
① 发送中断请求:
协调者向所有参与者发出abort请求。
② 中断事务:
无论是收到来自协调者的abort请求或者等待协调者请求过程中超时,参与者都会中断事务
③ 反馈中断结果:
参与者向协调者返回结果。
阶段三:do Commit
该阶段做真正的事务提交或者完成事务回滚,所以就会出现两种情况:
1. 执行事务提交
① 发送提交请求:
进入这一阶段,假设协调者处于正常工作状态,并且它接收到了来自所有参与者的Ack响应,那么他将从预提交状态转化为提交状态,并向所有的参与者发送doCommit请求。
② 事务提交:
参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行过程中占用的事务资源。
③ 反馈事务提交结果:参与者在完成事务提交后,向协调者发送Ack响应。
④ 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务。
2. 中断事务
① 发送中断请求:协调者向所有的参与者节点发送abort请求。
② 事务回滚:参与者收到abort请求后,会根据记录的Undo信息来执行事务回滚,并在完成回滚之后释放整个事务执行期间占用的资源。
③ 反馈事务回滚结果:参与者在完成事务回滚后,向协调者发送Ack消息。
④ 中断事务:协调者接收到所有参与者反馈的Ack消息后,中断事务。
注意:一旦进入阶段三,可能会出现 2 种故障:
- 协调者出现问题
- 协调者和参与者之间的网络故障
如果出现了任一一种情况,最终都会导致参与者无法收到 doCommit 请求或者 abort 请求,针对这种情况,参与者都会在等待超时之后,继续进行事务提交。
3PC改进点
协调者超时机制
2PC只对于参与者引入了超时机制,3PC对于协调者也引入了超时机制。在二阶段完成后,一旦事务参与者迟迟没有收到协调者的Commit请求,就会自动进行本地commit,这样相对有效地解决了协调者单点故障的问题。
为什么3PC在三阶段超时可以继续进行事务提交:
3PC
3PC相对于2PC增加了询问阶段。在3PC第一阶段执行完进入第二阶段后,可以认为各数据库都可以成功提交事务。所以如果3PC第二阶段发出了请求,则说明大家都同意执行。发出完请求后执行完协调者挂了,可以自动提交,因为收到二阶段请求说明第一阶段已经过了,第一阶段过了就说明大家都同意。
另外一方面 ,如果协调者挂了然后恢复后,协调者可以查看此时的全局事务状态为Precommit,那此时协调者就知道自己挂掉之前是向参与者发送的什么指令,而且协调者也可以推断出,自己挂掉后参与者会因为超时统一执行commit操作,这样协调者就能根据当前的状况作出下一步的决定。
2PC
但是2PC不能在协调者挂了时自动提交,2PC没有canCommit阶段,不知道大家是不是都同意,所以其他参与者将会处于一直锁定事务资源的状态中,而无法继续完成事务操作。
另外一方面,协调者和参与者才进行第一轮协商,全局事务信息还没有记录任何状态,协调者挂了恢复后,因为没有判断依据,它也不知道各个参与者当前具体的情况,也没办法做出任何决策。
决策结果透明化
2PC协议的决策结果初始阶段只有决策者知道。3PC的思想是把做重大动作时的决策结果透明化统一化,3PC协议中参与者不再过度依赖协调者的指令信号,而是有了自己的相对独立性。
3PC存在的问题
1、性能问题(相比2PC性能更差)
2PC需要锁定资源,并且时间取决于最慢的一个参与者,在3PC里这样的情况并未发生任何变化,3PC也还是需要锁定资源,同样也是必须要等待所有参与者响应才能进行下一步流程,反而3PC增加了一个阶段的协商通讯,这就使得3PC通信成本更高,性能反而会更差。
2、协调者故障导致的事务阻塞问题(解决)
因为3PC增加了询问阶段,然后在准备阶段增加了参与者超时机制,所以协调者故障并不会一直阻塞着事务进行,参与者超时之后会进行事务commit。
3、数据不一致的风险(还是存在)
如果协调者第二阶段的决策是abort,此时协调者把abort指令发送给了部分参与者之后挂掉了或者超时不通,那么收到了abort指令的参与者进行了数据回滚,但是没有收到abort指令的参与者会根据超时机制进行事务commit,最终就会有部分参与者rollback了,部分参与者进行了commit,最后数据不一致。
最终一致性解决方案
强一致性分布式事务解决方案要求参与事务的各个节点的数据时刻保持一致,在高并发场景下,系统的性能可能收到影响。而最终一致性方案并不要求数据时刻一致,允许其存在中间状态,只要一段时间后数据能够最终一致即可。
所以基于BASE理论,提出了最终一致性解决方案,典型的有:TCC解决方案,可靠消息最终一致性方案,最大努力通知型解决方案。
TCC
TCC解决方案主要包括三个阶段:
- try 尝试业务执行
- confirm 确定业务执行
- cancel 取消业务执行
TCC的特点
- TCC本质上是一个业务层面上的2PC,2PC一般指的是数据库层面的分布式事务,TCC指的是应用业务层面分布式事务。
- TCC对业务入侵较大,提供了一个编程框架,需要业务方把功能的实现上由一个接口拆分为三个,开发成本较
- TCC是业务层面的分布式事务,最终一致性,不会一直持有资源的锁。模型中的主业务服务相当于 CAP 模型中的AP。
TCC要满足的三个策略
TCC 事务为了解决异步网络中的通信失败或超时带来的异常情况,要求业务方在设计实现上要遵循三个策略。
允许空回滚
原因是异常发生在阶段 1 时,部分参与方没有收到 try 请求从而触发整个事务的 cancel 操作,try 失败或者没有执行 ,try 操作的参与方收到 cancel 请求时,要进行空回滚操作。
应对策略:
同样基于事务状态控制表,当Try方法被成功执行后,会插入一条记录,标识该分支事务处于INIT状态。所以后续当二阶段的Cancel方法被调用时,可以通过查询控制表的对应记录进行判断。如果记录存在且状态为INIT,就表示一阶段已成功执行,可以正常执行回滚操作,释放预留的资源;如果记录不存在则表示一阶段未执行,本次为空回滚,不释放任何资源。
防止资源悬挂
原因网络异常导致两个阶段无法保证严格的顺序执行,出现参与方侧 try 请求比 cancel 请求更晚到达的情况,cancel 会执行空回滚而确保事务的正确性,但是此时 try 方法也不可以再被执行。
应对策略:
在判断为空回滚的场景下(体现在对应一阶段事务控制记录不存在),插入一条状态为ROLLBACKED的控制记录。那么下次当一阶段Try抵达执行的时候,首先会尝试插入状态为INIT的事务控制记录。如果插入失败,表示当前分支事务的记录已经存在,Try无需继续执行。
保持幂等性
无论是网络数据包重传,还是异常事务的补偿执行,都会导致TCC服务的Try、Confirm或者Cancel操作被重复执行;用户在实现TCC服务时,需要考虑幂等控制,即Try、Confirm、Cancel 执行次和执行多次的业务结果是一样的。如果不能保证幂等性,则会产生严重的问题,造成资源的重复使用或者重复释放,进而导致业务故障。
应对策略:
对于幂等类型的问题,通常的手段是引入幂等字段。对于分布式事务框架中的幂等问题,可以通过增加一张事务状态控制表来实现。这个表会记录每个分布式事务的状态:INIT - 初始化、CONFIRMED - 已提交、ROLLBACKED - 已回滚。幂等记录的插入时机是参与者的Try方法,此时的分支事务状态会被初始化为INIT。然后当二阶段的Confirm/Cancel执行时会将其状态置为CONFIRMED/ROLLBACKED。事务操作时,通过对幂等记录判断之后再执行。
SAGA
Saga是由一系列的本地事务构成。每一个本地事务在更新完数据库之后,会发布一条消息或者一个事件来触发Saga中的下一个本地事务的执行。如果一个本地事务因为某些业务规则无法满足而失败,Saga会执行在这个失败的事务之前成功提交的所有事务的补偿操作。
SAGA执行方式
SAGA是两层执行的,事物按流程T1,T2,,,TN。那么与之对应的就是C1,C2,,,CN。也就是由N个分布式事务组织,同时也有N个回滚事务与之对应。如下图,3个服务,A,B,C,这3个服务是按顺序执行的,如果B事务执行失败了,那么就会执行B事务的回滚操作。
适用场景
- 业务流程长、业务流程多
- 参与者包含其它公司或遗留系统服务,无法提供TCC模式要求的三个接口
优势
- 一阶段提交本地事务,无锁,高性能
- 事件驱动架构,参与者可异步执行,高吞吐
- 补偿服务易于实现
缺点
- 不保证隔离性
TCC和SAGA的区别
TCC框架
- 在业务上侵入性更大,需要实现try、confirm、cancel三个接口;
- 没有数据隔离性问题;
- 两阶段事务提交,虽然有锁,但是可以通过业务锁的方式来提高并发能力;
SAGA框架
- 适用于无法改造接口的场景;
- 有数据隔离性问题,可能产生脏数据、更新丢失、模糊读取等问题;
- 一阶段事务提交,无锁,适用于长流程业务逻辑;
- 适用于事件驱动异步执行,吞吐量更高;
- 补偿失败的场景下需要额外的异常处理,如人工介入;
两者之间同样有一些相同点,在幂等机制、空回滚、防悬挂设计、业务锁、异常处理机制上,二者可以相互借鉴实践。
消息类
本地消息
本地消息表最初是 ebay 提出的,它让本地消息表与业务数据表处于同一个数据库中,这样就能利用本地事务来满足事务特性。
具体做法是在本地事务中插入业务数据时,也插入一条消息数据。然后在做后续操作,如果其他操作成功,则删除该消息;如果失败则不删除,异步监听这个消息,不断重试。
写消息和业务操作放在一个事务里,保证了业务和发消息的原子性,要么他们全都成功,要么全都失败。
消费消息同理。
事务消息
在上述的本地消息表方案中,生产者需要额外创建消息表,还需要对本地消息表进行轮询,业务负担较重。阿里开源的RocketMQ 4.3之后的版本正式支持事务消息,该事务消息本质上是
最大努力通知
最大努力通知型( Best-effort delivery)是最简单的一种柔性事务,适用于一些最终一致性时间敏感度低的业务,且被动方处理结果不影响主动方的处理结果。典型的使用场景:如银行通知、商户通知等。最大努力通知型的实现方案,一般符合以下特点:
- 不可靠消息:业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)。
- 定期校对:业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息。
部分参考资料
https://linux.cn/article-10693-1.html
https://developer.aliyun.com/article/762770#slide-8
https://juejin.cn/post/6898288789371027470
https://juejin.cn/post/6899645923024355336
https://zhuanlan.zhihu.com/p/40692982