分布式事务是分布式领域必须要面对的问题,同时也是衡量一个分布式系统成熟度的重要指标。那么什么是分布式事务,哪些场景会涉及到分布式事务,如何实现分布式事务?本文将重点讨论以上问题。

分布式事务定义

分布式事务来源于数据库事务。如果说数据库事务保证了单数据库的ACID特性,那么分布式事务则是保证了分布式系统的ACID特性。针对分布式事务,业内并没有一个统一的概念,但是不同厂商对分布式事务的定义是相似的。如维基百科中分布式事务的定义是:“分布式事务是包含两个或多个跨网络主机的数据库事务。通常,主机提供事务性资源,而事务管理器负责创建和管理包含针对此类资源的所有操作的全局事务。与数据库事务一样,分布式事务必须具有所有四个 ACID(原子性、一致性、隔离性、持久性)属性,其中原子性保证一组跨网络的操作全部执行成功或全部执行失败。” 原文如下:

A distributed transaction is a database transaction in which two or more network hosts are involved.
Usually, hosts provide transactional resources, while the transaction manager is responsible for creating and managing a global transaction that encompasses all operations against such resources.
Distributed transactions, as any other transactions, must have all four ACID (atomicity, consistency, isolation, durability) properties, where atomicity guarantees all-or-nothing outcomes for the unit of work (operations bundle).

Oracle中分布式事务的定义(oracle对外博客中找到,并不一定代表oracle的观点)是:"分布式事务(有时也称为全局事务)是必须协调两个或多个相关事务的事务集合。构成分布式事务的事务可能位于同一数据库中,但更常见的是位于不同的数据库中,并且通常位于不同的位置。分布式事务的每个单独事务称为事务分支。"原文如下:

A distributed transaction, sometimes referred to as a global transaction, is a set of two or more related transactions that must be managed in a coordinated way.
The transactions that constitute a distributed transaction might be in the same database, but more typically are in different databases and often in different locations.Each individual transaction of a distributed transaction is referred to as a transaction branch.

简单来说,分布式事务就是可保证分布式系统的ACID特性的事务。注意,分布式事务并不仅仅局限于存储层,还包括计算层。
在学习分布式事务时,还要注意分布式事务与其他事务的概念区分。区分概念的最好办法是对相关概念进行分类。这里参考凤凰架构一书中的划分标准,即按照涉及的服务个数、数据源个数来划分事务。在原书中,作者将“单个服务使用单个数据源”的事务称为本地事务(Local Transaction),“单个服务使用多个数据源”的事务称为全局事务(Global Transaction)、“多个服务使用单个数据源”的事务称为共享事务(Share Transaction),“多个服务使用多个数据源”的事务称为分布式事务(Distributed Transaction)。笔者认同作者的事务划分标准,但是对划分后事务的命名,保持不一样的看法。首先,笔者认为全局事务就是分布式事务,只是全局事务仅应用在数据库领域。其次,针对"多个服务使用单个数据源”的事务,笔者认为可将其划分到“本地事务”中。对单一数据源来说,多个服务就是多个客户端,单一数据源是有能力保证事务的ACID特性的。当然,多服务对应一个数据源的架构,本身就是一种不合理的架构,这里不展开讨论。所以,笔者对事务的划分如下图所示:

同时,也要注意分布式事务与 DTP 模型中“分布式事务”的差异。DTP 模型所指的“分布式”是相对于数据源而言的,并不涉及服务,所以DTP描述的是“单个服务使用多个数据源”的场景。而分布式事务即包含“单个服务使用多个数据源”的场景,也包含“多个服务使用多个数据源”的场景。所以,在遇到分布式事务名词时,一定要注意上下文,避免不正确的理解。

分布式事务产生背景

对一个业务应用来说,起初是单库单表,但随着业务数据规模的快速发展,数据量越来越大,单库单表逐渐成为瓶颈。随着对数据库的水平拆分,原来的单库单表拆分成数据库分片。如下图所示,分库分表之后,原来在一个数据库上就能完成的写操作,可能就会跨多个数据库,这就产生了跨数据库事务问题。

在业务发展初期,单业务系统架构能满足基本的业务需求。但是随着业务的进一步发展,系统的访问量和业务复杂程度都在快速增长,单系统架构逐渐成为业务发展瓶颈,解决业务系统的高耦合、可伸缩问题的需求越来越强烈。如下图所示,业务系统架构从单业务系统(单机架构)拆分成多业务系统(分布式架构),降低了各系统之间的耦合度,使不同的业务系统专注于自身业务,更有利于业务的发展和系统容量的伸缩。

数据库分片(单服务多数据源场景)或多业务系统架构(多服务多数据源场景)会带来多个数据库的数据一致性维护问题。解决这个问题的本质是实现分布式事务。事务用来保证多个操作,要么全部执行成功,要么全部失败。本地事务可以保证一个数据库上的操作,而分布式事务则是保证分布式系统中涉及多个系统的操作,要么全部执行成功,要么全部失败。

分布式事务实现

在分布式场景下,由于分区必然存在,所以根据CAP理论,无法同时实现强一致性和高可用性。根据不同的一致性等级,可将分布式事务进一步细分为两类:刚性事务(Rigid Transactions)和柔性事务(Flexible Transactions)。刚性事务是指具备ACID特性的分布式事务。在刚性事务中,数据可以保证强一致性,可以像使用本地事务一样使用刚性事务。柔性事务是指不具备ACID特性,但遵循BASE理论的分布式事务。柔性事务放宽了一致性要求,即保证最终一致性即可。也就是说,柔性事务允许系统有中间状态。刚性事务为保证强一致性,降低了可用性的要求。相反,柔性事务位保证高可用性,降低了一致性的要求。
针对刚性事务,通常基于 XA 协议(如2PC、3PC)来实现。对于柔型事务,有 TCC(Try-Confirm-Cancel)、Saga、本地消息表、可靠消息队列、尽最大努力通知等多种实现方案。

XA 规范(XA Specification)

XA 规范是由 X/Open Group 在 1991 年推出的分布式事务的规范。XA 规范中定义了分布式事务处理模型(Distributed Transaction Processing Model, DTP Model),其模型描述如下图所示:

这个模型主要包含三个核心角色:
(a) RM (Resource Managers):资源管理器,提供数据资源的操作、管理接口,保证数据的一致性和完整性。最有代表性的就是数据库管理系统。
(b) TM (Transaction Managers):事务管理器,是一个协调者的角色,协调跨库事务关联的所有 RM 的行为。
© AP (Application Program):应用程序,按照业务规则调用 RM 接口来完成对业务模型数据的变更,当数据的变更涉及多个 RM 且要保证事务时,AP 就会通过 TM 来定义事务的边界,TM 负责协调参与事务的各个 RM 一同完成一个全局事务。
XA规范还定义了事务管理器和资源管理器之间的接口。XA接口是双向的系统接口,在一个事务管理器和一个或多个资源管理器之间形成通信桥梁。目前,Oracle、Informix、DB2、Sybase和PostgreSQL等各主流数据库都提供了对 XA 的支持。

2PC(两阶段提交)协议

XA规范只是定义了事务管理器和资源管理器之间的接口,并没有严格要求其实现。熟悉两阶段提交的同学可能发现,两阶段提交协议中定义的协调者和参与者与XA规范的事务管理器和资源管理器功能类似。这里介绍下基于2PC协议实现的分布式事务。
(1) 预备阶段(prepare)。事务管理器记录事务开始日志,并询问本地资源管理器是否可以执行提交准备操作。本地资源管理器收到指令后,评估自己的状态,尝试执行本地事务的预备操作:预留资源,为资源加锁、执行操作等,但是并不提交事务,并等待事务管理器的后续指令。如果本地资源管理器尝试失败,则告知事务管理器本阶段执行失败并回滚自己的操作,然后不再参与本次事务。在发送了否定答复并回滚了本地事务之后,本地资源管理器才能丢弃持久化的本地事务信息。事务管理器收集本地资源管理器的响应,并记录事务准备完成日志。执行过程如下图所示:

(2) 提交/回滚阶段(commit/rollback)。这一阶段会根据上阶段的协调结果发起事务的提交或者回滚操作。如果所有本地资源管理器在上一个步骤都返回执行成功,那么会执行提交操作。具体包括:1) 事务管理器记录事务 commit 日志,并向所有本地资源管理器发起事务提交指令;2) 所有的本地资源管理器收到指令后,提交事务,释放资源,并向事务管理器响应“提交完成”;3) 如果 TM 收到所有 RM 的响应,则记录事务结束日志。
如果有本地资源管理器在上一个步骤中返回执行失败或者超时没有应答,则事务管理器统一按照执行失败处理。具体包括:1) 记录事务 abort 日志,向所有本地资源管理器发送事务回滚指令;本地资源管理器收到指令后,回滚事务,释放资源,并向事务管理器响应回滚完成;3) 如果事务管理器收到所有本地资源管理器的响应,则记录事务结束日志。执行过程如下图所示:

2PC协议虽然可以保证强一致性,但是存在以下问题:
(1) 同步阻塞。在预备阶段,本地资源管理器会独占资源,并直到整个分布式事务完成后才会释放资源。这个过程中,如果有其它请求要访问这个资源,会被阻塞。
(2) 脑裂问题:在提交/回滚阶段,如果事务管理器只发送了部分 commit 消息,此时网络发生异常,那么只有部分本地资源管理器接收到 commit 消息,也就是说只有部分本地资源管理器提交了事务。

3PC(三阶段提交)协议

针对 2PC 的问题,有人提出了3PC的改进方案。3PC解决了单点故障问题,并在本地资源管理器侧引入超时机制,以避免资源的长时间锁定。但是三阶段提交方案依然无法避免脑裂的异常情况出现,实际应用案例很少,感兴趣的同学可以自行找相关资料了解。

XA规范小结

基于XA规范实现的分布式事务主要用来解决单服务多数据源的场景,对多服务多数据源的场景,其实现难度较大。尽管 XA规范比较简单,且支持 XA 规范后,使用分布式事务的成本较低。但是,XA 规范也不是银弹。XA 规范因性能不理想,无法满足高并发场景。

TCC

TCC(Try-Confirm-Cancel)最早由 Pat Helland 在 2007 年发表的论文Life beyond Distributed Transactions:an Apostate’s Opinion》中提出。TCC 本质是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认(confirm)和补偿(cancel)操作。TCC的具体含义如下:
Try 阶段:尝试执行事务,完成所有业务检查(一致性), 预留必需的业务资源。
Confirm 阶段:对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的(也应对异常进行处理)。即:只要Try成功,Confirm一定成功。Confirm 操作要求具备幂等设计,Confirm 失败后需要进行重试。
Cancel 阶段:取消执行,主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。Cancel操作满足幂等性设计。

Saga

Saga 最早由 Hector Garcaa Molrna 和 Kenneth Salem 在1987年发表的论文SAGAS中提出。Saga 和 TCC 一样,也是一种补偿事务,但是它没有 try 阶段,而是把分布式事务看作一组本地事务构成的事务链。事务链中的每一个正向事务操作,都对应一个可逆的事务操作。Saga 事务协调器负责按照顺序执行事务链中的分支事务,分支事务执行完毕,即释放资源。如果某个分支事务失败了,则按照反方向执行事务补偿操作。
假如一个 Saga 的分布式事务链有 n 个分支事务构成,[T1,T2,…,Tn],那么该分布式事务的执行情况有三种:
(1) T1,T2,…,Tn:n 个事务全部执行成功了。
(2) T1,T2,…,Ti,Ci,…,C2,C1:执行到第 i (i<=n) 个事务的时候失败了,则按照 i->1 的顺序依次调用补偿操作。如果补偿失败了,就一直重试。补偿操作可以优化为并行执行。
(3) T1,T2,…,Ti (失败),Ti (重试),Ti (重试),…,Tn:适用于事务必须成功的场景,如果发生失败了就一直重试,不会执行补偿操作。

可靠消息队列

可靠消息队列这个方案最早由 Dan Pritchett 在 2008 年发表的论文Base: An Acid Alternative(BASE理论的来源)中提出。可靠消息队列的核心思想是通过可靠消息队列确保事务参与方可接收该消息并处理成功。此方案基于消息中间件实现,其执行流程如下图所示:

本地消息表

本地消息表的核心思想是通过本地事务保证数据业务操作和消息的一致性。事务发起方维护一个本地消息表,业务执行和本地消息表的执行处在同一个本地事务中。业务执行成功,则同时记录一条“待发送”状态的消息到本地消息表中。系统中启动一个定时任务定时扫描本地消息表中状态为“待发送”的记录,并将其发送到消息系统(MQ)中。如果发送失败或者超时,则一直发送,直到发送成功后,从本地消息表中删除该记录。事务参与方在收到消息后,执行本地事务,本地事务如果执行成功,则给 MQ 系统发送 ACK 消息;如果执行失败,则不发送 ACK 消息,MQ 系统会持续推送给消息。具体执行流程如下图所示:

在实现本地消息表时,需要保证以下约束:(1) 本地事务与消息发送的原子性;(2) 事务参与方接收消息的可靠性;(3) 消息重复消费的问题。具体含义如下:
​(1) 本地事务与消息发送的原子性。针对消息创建消息表,业务逻辑和消息表通过本地事务保证一致。下边是伪代码:

begin transaction;//1.本地事务操作  //2.存储消息日志
commit transation;

这种情况下,本地数据库操作与消息日志处于同一个事务中,本地数据库操作与记录消息日志操作具备原子性。
(2) 事务参与方接收消息的可靠性。可以使用 MQ 的 ack(即消息确认)机制。事务参与方监听 MQ,如果事务参与方接收到消息并且业务处理完成后向 MQ 发送ack,此时说明事务参与方正常消费消息完成,MQ 将不再向消费者推送消息,否则 MQ 会不断重试向事务参与方发送消息。
(3) 消息重复消费的问题。​由于 MQ 的消息会重复投递,所以事务参与方在消费消息时需要确保幂等性。

尽最大努力通知

尽最大努力通知方案也是一种基于 MQ 的解决方案,但是不要求 MQ 消息可靠。尽最大努力通知方案的核心思想是事务发起方通过MQ等方式,最大努力将业务处理结果通知到事务接收方。
最大努力通知方案主要用于外部系统,因为外部的网络环境更加复杂和不可信,所以只能尽最大努力去通知,以实现数据最终一致性,比如跨企业的系统间业务交互场景。

总结

分布式事务根据实现的一致性等级、支持的业务场景等有多种实现方案。开发者需要根据业务特点选择合适的实现方案。分布式事务会增大系统的复杂度,在非必要的情况下,尽量不要引入分布式事务。主流的分布式事务方案以开源的 seata 解决方案最为成熟。云服务场景下,各厂商也有推出自己的GTS(Global Transaction Service)服务。

参考

https://docs.oracle.com/en/database/oracle/oracle-database/12.2/jjdbc/distributed-transactions.html Distributed Transactions
https://around25.com/blog/how-to-manage-database-transactions-in-a-distributed-system/ How to manage database transactions in a distributed system
http://en.wikipedia.org/wiki/Distributed_transaction distributed transaction
https://www.cnblogs.com/wzh2010/p/15311142.html 分布式:分布式事务(CAP、两阶段提交、三阶段提交)
https://www.sofastack.tech/blog/seata-distributed-transaction-deep-dive/ Seata 分布式事务实践和开源详解
https://www.sofastack.tech/blog/sofa-meetup-3-seata-retrospect/ 分布式事务 Seata Saga 模式首秀以及三种模式详解
https://www.cnblogs.com/crazymakercircle/p/13917517.html 分布式事务
https://pdai.tech/md/arch/arch-z-transection.html 分布式锁
http://icyfenix.cn/architect-perspective/general-architecture/transaction/distributed.html 分布式事务
https://programs.wiki/wiki/distributed-transaction.html Distributed transaction
https://codingusage.com/titledistributed-transactions-theorypractice.html Distributed Transactions (Theory+Practice)
https://shardingsphere.apache.org/blog/en/material/solution/ The mixed open-source distributed transaction solution
https://xiaomi-info.github.io/2020/01/02/distributed-transaction/ 分布式事务
https://pubs.opengroup.org/onlinepubs/009680699/toc.pdf Distributed Transaction Processing: The XA Specification
https://blog.csdn.net/qq_31960623/article/details/119392821 分布式事务模型–XA Specification
https://developer.aliyun.com/article/762770 如何选择分布式事务解决方案?
https://cn.bing.com/translator 微软必应翻译

分布式事务(Distributed Transactions)概述相关推荐

  1. 深入解析:MySQL对分布式事务 XA Transactions 的支持

    导读:MySQL对分布式事务(XA Transactions)进行了很好的支持,我们看看它是怎么做的,并实战验证其提供的分布式事务控制语句效果. MySQL从5.0.3开始,InnoDB存储引擎支持X ...

  2. 分布式事务 解决方案

    如何选择分布式事务解决方案? 概述 名词说明 空回滚 事务悬挂 分布式事务模式 XA Specification TCC Saga 基于消息的分布式事务 基于事务消息的分布式事务 基于本地消息的分布式 ...

  3. JDBC高级特性3--JNDI,连接池,分布式事务

    2019独角兽企业重金招聘Python工程师标准>>> System.out.print(rs.getInt(1)+"\t"); System.out.print ...

  4. java 分布式事务_Java核心知识 Spring原理十五 JPA 原理

    1. 事务 事务是计算机应用中不可或缺的组件模型,它保证了用户操作的原子性 ( Atomicity ).一致性 ( Consistency ).隔离性 ( Isolation ) 和持久性 ( Dur ...

  5. 分布式事务模型--最大努力通知型分布式事务

    本文来说下分布式事务模型之最大努力通知型分布式事务 文章目录 概述 最大努力通知型分布式事务 举个例子 特点剖析 本文小结 概述 事务是一组不可分组的操作集合,这些操作要么都成功执行,要么都取消执行. ...

  6. 分布式事务模型--基于消息的分布式事务

    本文来说下分布式事务模型之基于消息的分布式事务 文章目录 概述 基于消息的分布式事务 基于事务消息的分布式事务 基于本地消息的分布式事务 特点剖析 本文小结 概述 事务是一组不可分组的操作集合,这些操 ...

  7. java 分布式事务总结

    编程式事务 TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition( ...

  8. 分布式事务TCC方案——Hmily金融级柔性分布式事务解决方案介绍

    关于TCC的理论部分请参考:分布式事务基础理论--TCC 概述 目前市面上的TCC框架众多(都是国内的呢): 框架名称 GitHub地址 tcc-transaction https://github. ...

  9. 分布式事务概述 (资料)

    2019独角兽企业重金招聘Python工程师标准>>> 什么是分布式系统 分布式系统是由一组通过网络进行通信.为了完成共同的任务而协调工作的计算机节点组成的系统.分布式系统的出现是为 ...

最新文章

  1. linux下的usb抓包方法【转】
  2. sql多条件查询语句
  3. 8月3日云栖精选夜读 | 阿里巴巴宣布 Sentinel 开源,进一步完善 Dubbo 生态(附PPT和视频)...
  4. 总结面试时没有回答上的内存对齐问题
  5. 十个模块_专栏 | ABAQUS Part模块的十个小技巧
  6. 更复杂的缓存穿透怎么解决
  7. C语言实现二叉树的各种遍历及求解深度
  8. 2.NET Core设定数据库种子
  9. css_02 | CSS——CSS 选择器详解
  10. python--如何进行去重
  11. 【日记】python获取公众号的全部文章并截取图导出
  12. 网络安装CentOS 7
  13. 使用unity制作的一款生存类游戏demo(一)
  14. 活动回顾|Apache Doris 向量化技术实现与后续规划
  15. 计算机制图怎么学,新手学电脑学习画图的方法
  16. [问题已处理]-[nginx]-nginx 报错 could not build server_names_hash
  17. Onvif OSD相关操作
  18. 电脑可以上网,但是qq登陆不上去?
  19. 爆肝一周,完成了一款第一人称3D射击游戏,现在把源代码分享给大家,适合新手跟着学习
  20. unity人物旋转移动代码_Unity3D实现人物移动示例

热门文章

  1. 企业微信,实现群机器人监控线上服务报警
  2. 计算机双工模式,怎么给电脑设置为全双工模式
  3. MySql中对应字段属性为int取出来却为Long
  4. OSCP靶机练习--CuteNews01
  5. 使用PDFBOX提取PDF中的文字
  6. 802.11ac wave2的前世今生
  7. 一个完整的内网渗透是什么样子的
  8. 电子器件系列42:小型中功率继电器
  9. Windows使用VSPD虚拟串口
  10. 轻松管理亚马逊多账号 不关联 防关联