事务(Transaction)是指程序执行过程中的一个不可分割的逻辑单位,由一个有限的操作序列构成。
分布式事务指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上,且属于不同的应用,分布式事务需要保证这些操作要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同数据库的数据一致性。
数据库事务要满足ACID的特性:
从 CAP 定理来看,P(可分区容错性)一般来说是分布式系统无法规避的既定事实,所以我们更需要在C(强一致性)A(可用性)方面做权衡与取舍。
对于分布式系统来说,我们当然追求高可用性,但是有时候我们也需要在发生异常情况下依然有一致性保障(如机器宕机、网络异常、消息丢失、消息乱序、数据错误、不可靠的TCP、存储数据丢失等异常情况)。
一致性通常有以下几种分类:
事务仅限于对单一数据库资源的访问控制,架构服务化以后,事务的概念延伸到了服务中。倘若将一个单一的服务操作作为一个事务,那么整个服务操作只能涉及一个单一的数据库资源,这类基于单个服务单一数据库资源访问的事务,被称为本地事务
最早的分布式事务应用架构很简单,不涉及服务间的访问调用,仅仅是服务内操作涉及到对多个数据库资源的访问。
当一个服务操作访问不同的数据库资源,又希望对它们的访问具有事务特性时,就需要采用分布式事务来协调所有的事务参与者。在这种情况下,起始于某个服务的事务在调用另外一个服务的时候,需要以某种机制流转到另外一个服务,从而使被调用的服务访问的资源也自动加入到该事务当中来。下图反映了这样一个跨越多个服务的分布式事务
如果将上面这两种场景(一个服务可以调用多个数据库资源,也可以调用其他服务)结合在一起,对此进行延伸,整个分布式事务的参与者将会组成一种树形拓扑结构。在一个跨服务的分布式事务中,事务的发起者和提交均系同一个,它可以是整个调用的客户端,也可以是客户端最先调用的那个服务。
X/Open 组织(即现在的 Open Group )定义了分布式事务处理模型;
XA协议 :XA 是 X/Open定义的交易中间件与数据库之间的接口规范(即接口函数),交易中间件用它来通知数据库事务的开始、结束以及提交、回滚等, XA 接口函数由数据库厂商提供。
两阶段提交协议分为两个阶段进行,第一阶段是预处理阶段,也就是发送 SQL 语句,业务系统校验数据库返回的响应进行校验。第二阶段是提交阶段,也就是发送 commit 指令给数据库。
第一阶段 预处理阶段:事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare 消息,每个参与者要么直接返回失败 ,要么在本地执行事务,写本地的 redo 和 undo 日志,但不提交。其预处理要分为三个子步骤:
协调者节点向所有参与者节点询问是否可以执行提交操作(vote),并开始等待各参与者节点的响应。
参与者节点执行询问发起为止的所有事务操作,并将 Undo 信息和 Redo 信息写入日志。(注意:若成功这里其实每个参与者已经执行了事务操作)
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个”同意”消息;如果参与者节点的事务操作实际执行失败,则它返回一个”中止”消息。
第二阶段 提交阶段:如果协调者收到了参与者的失败或超时,直接给每个参与者发送回滚(rollback)操作;否则发送提交(commit)消息;参与者根据协调者的指令执行提交或者回滚操作,释放所有事务处理过程中使用的锁资源。
无论最后结果如何,第二阶段都结束当前事务。
两阶段有如下几个缺点:
其在应用工程中很少使用,但其有一定的参考性;
三段提交是两段提交的升级版,主要的改动为:
三阶段提交提交协议,分为三个阶段,CanCommit 阶段、PreCommit 阶段、DoCommit 阶段
相对于2PC,3PC主要解决的单点故障问题,并减少阻塞,因为一旦参与者无法及时收到来自协调者的信息之后,他会默认执行commit。而不会一直持有事务资源并处于阻塞状态。
会导致数据一致性问题。由于网络原因,协调者发送的中断响应没有及时被参与者接收到,那么参与者在等待超时之后执行了commit操作。这样就和其他接到中断命令并执行回滚的参与者之间存在数据不一致的情况。
TTCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。三个阶段如下:
操作方法 | 含义 |
---|---|
Try | 预留业务资源/数据效验-尝试检查当前操作是否可执行 |
Confirm | 确认执行业务操作,实际提交数据,不做任何业务检查。try成功,confirm必定成功 |
Cancel | 执行业务出错时,需要回滚数据的状态下执行的业务逻辑 |
其核心在于将业务分为两个操作步骤完成。不依赖事务协调器对分布式事务的支持,而是通过对业务逻辑的分解来实现分布式事务。
通过异步的形式,或者采用第三方组件将请求确保将消息发送给子业务系统。其需要评估该请求在子业务必须执行成功的;否则子业务系统执行失败,主业务系统还需要进行回滚,逻辑将会变得复杂;什么情况下,会采用该机制呢?主要是发短信等必须执行成功的场景下,才会采用该方法
请求保存到本地数据库,交由后台程序异步将请求推送给子业务系统。一般来讲,设计重试 3 次即可,当连续发送三次失败,即通知运维人员进行跟进处理,具体什么原因导致;如果涉及不限次数的发送,那么就会导致业务系统压力过大
工作流程:
优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。
缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。
有一些第三方的MQ是支持事务消息的,比如RocketMQ,他们支持事务消息的方式也是类似于采用的二阶段提交,但是市面上一些主流的MQ都是不支持事务消息的,比如
RabbitMQ 和 Kafka 都不支持(RabbitMQ、Kafka基于ACK机制)。
以阿里的 RocketMQ 中间件为例,流程为:
2019 年 1 月,阿里巴巴中间件团队发起了开源项目 Fescar
(Fast & EaSy Commit And Rollback),和社区一起共建开源分布式事务解决方案。Fescar
的愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。
Fescar 开源后,蚂蚁金服加入 Fescar 社区参与共建,并在 Fescar 0.4.0 版本中贡献了 TCC 模式。
为了打造更中立、更开放、生态更加丰富的分布式事务开源社区,经过社区核心成员的投票,大家决定对 Fescar 进行品牌升级,并更名为 Seata,意为: Simple Extensible Autonomous Transaction Architecture,是一套一站式分布式事务解决方案。
Seata 融合了阿里巴巴和蚂蚁金服在分布式事务技术上的积累,并沉淀了新零售、云计算和新金融等场景下丰富的实践经验。
cs