今天来聊聊分布式事务吧
- 我之前有篇文章写过CAP以及BASE理论(文章链接),但对于分布式讲的比较浅也比较模糊。随着项目实践应用以及码龄的渐长,对分布式的理解又多了一点点点点点的认知,因此重写一篇对分布式事务来记录一下入门概念。
- 上篇文章(Kafka,RabbitMQ,RockedMQ真实应用开发大汇总1)设置了只可粉丝可见,收割了五十位粉丝。我对设置粉丝可见的文章的标准是:1.对实际生产开发有很大帮助 2.我自己也花了一段时间去学习理解的东西 3.能够锻炼编程思维 4.目前主流的技术栈和技术难点的文章
- 如果有任何问题,欢迎留言区或私信进行交流。
- 欢迎点赞,关注
- 本篇文章核心快速概览
- 1.本地事物与分布式事务
- 2.分布式理论
- 3.分布式事务解决方案概览
- 4.刚性,柔性事务(XA,TCC)
- 5.主流分布式方案介绍(seata,事务消息mq)
本地事务与分布式事务
- 事务指的就是一个操作单元,在这个操作单元中的所有操作最终要保持一致的行为,要么所有操作都成功,要么所有的操作都被撤销
- 事务分两种:
- 一个是本地事务:本地事物其实可以认为是数据库提供的事务机制
- 一个是分布式事务
本地事务
- 相信大家对本地事务都不陌生,在单体开发中需要数据强一致性的时候或多或少都有用到过。目前最用的就是以注解的形式,进行事务标注。
@Transactional(rollbackFor=Exception.class,propagation= Propagation.REQUIRED)
public void test() {
//数据库操作
}
- @Transactional()里的参数不做过多赘述,有兴趣的小伙伴可以自行了解或复习,最常用的参数其实就是上面这个,在发生exception的时候进行回滚,事物创建机制为required。
分布式事务
- 那分布式事务又是怎么一回事?
- 先看一些定义
- 指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。
- 简单的说,就是一次大的操作由不同的小操作组成,这些小的操作分布在不同的服务器上,且属于不同的应用分布式事务需要保证这些小操作要么全部成功,要么全部失败。
- 本质上来说,分布式事务就是为了保证不同数据库的数据一致性
- 我的一些理解:首先明确分布式事务是发生在多节点部署服务中的(SOA或微服务框架),目的是为了保证多节点的数据一致性。
可能看完定义还有点蒙,上一个业务场景来看一下。
(刚刚学着画图分析业务场景,比较丑。)
- 我们来设想一个业务场景,当用户下单后,由订单服务远程调用商品服务的接口,这个时候,商品服务库存扣减成功了,但是优惠券服务宕机
- 或者出错了,优惠券却没有扣减成功,发生了数据不一致。
或者是优惠券给用户扣减掉了,但是商品服务出错了,库存没有扣减成功,但是用户的优惠券却永远的消失在了他的背包里。。。 - 我们原来的本地事务这个时候就失效了,本地事务只能保证自己的数据一致,而不能通知隔壁人家业务执行的非仓成功的服务进行回滚操作。
在讲分布式事务之前还是得先引入两个非常重要的理论,CAP和BASE
CAP定理
- 指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可同时获得
-
一致性(C):所有节点都可以访问到最新的数据
-
可用性(A):每个请求都是可以得到响应的,不管请求是成功还是失败
-
分区容错性(P):除了全部整体网络故障,其他故障都不能导致整个系统不可用
-
CAP理论就是说在分布式存储系统中,最多只能实现上面的两点。而由于当前的网络硬件肯定会出现延迟丢包等问题,所以分区容忍性是我们必须需要实现的。所以我们只能在一致性和可用性之间进行权衡
-
CA: 如果不要求P(不允许分区),则C(强一致性)和A(可用性)是可以保证的。但放弃P的同时也就意味着放弃了系统的扩展性,也就是分布式节点受限,没办法部署子节点,这是违背分布式系统设计的初衷的
-
CP: 如果不要求A(可用),每个请求都需要在服务器之间保持强一致,而P(分区)会导致同步时间无限延长(也就是等待数据同步完才能正常访问服务),一旦发生网络故障或者消息丢失等情况,就要牺牲用户的体验,等待所有数据全部一致了之后再让用户访问系统
-
AP:要高可用并允许分区,则需放弃一致性。一旦分区发生,节点之间可能会失去联系,为了高可用,每个节点只能用本地数据提供服务,而这样会导致全局数据的不一致性。
- 分布式系统中P,肯定要满足,所以只能在CA中二选一
- 没有最好的选择,最好的选择是根据业务场景来进行架构设计
- 如果要求一致性,则选择zookeeper/Nacos,如金融行业 CP
- 如果要求可用性,则Eureka/Nacos,如电商系统 AP
- CP : 适合支付、交易类,要求数据强一致性,宁可业务不可用,也不能出现脏数据
- AP: 互联网业务,比如信息流架构,不要求数据强一致,更想要服务可用
BASE理论
- 由来自 ebay 的架构师提出:CAP 中的一致性和可用性进行一个权衡的结果,核心思想就是:我们无法做到强一致,但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性
-
Basically Available(基本可用)
- 假设系统,出现了不可预知的故障,但还是能用, 可能会有性能或者功能上的影响
-
Soft state(软状态)
- 允许系统中的数据存在中间状态,并认为该状态不影响系统的整体可用性,即允许系统在多个不同节点的数据副本存在数据延时
-
Eventually consistent(最终一致性)
- 系统能够保证在没有其他新的更新操作的情况下,数据最终一定能够达到一致的状态,因此所有客户端对系统的数据访问最终都能够获取到最新的值。
- 关于数据一致性
- 强一致:操作后的能立马一致且可以访问
- 弱一致:容忍部分或者全部访问不到
- 最终一致:弱一致性经过多一段时间后,都一致且正常
- 不难看出来,最终一致性在性能与业务方面得到了一个综合,也是目前大多数分布式框架采用的思想。
分布式事务常见解决方案概览
-
常见分布式事务解决方案
- 2PC 和 3PC
- 两阶段提交, 基于XA协议
- TCC
- Try、Confirm、Cancel
- 事务消息
- 最大努力通知型
- 2PC 和 3PC
-
分布式事务分类
- 刚性事务:遵循ACID
- 柔性事务:遵循BASE理论
-
分布式事务框架
- TX-LCN:支持2PC、TCC等多种模式
- https://github.com/codingapi/tx-lcn
- 更新慢(个人感觉处于停滞状态)
- Seata:支持 AT、TCC、SAGA 和 XA 多种模式
- https://github.com/seata/seata
- 背靠阿里,专门团队推广
- 阿里云商业化产品GTS
- https://www.aliyun.com/aliware/txc
- RocketMq:自带事务消息解决分布式事务
- https://github.com/apache/rocketmq
- TX-LCN:支持2PC、TCC等多种模式
XA
-
X/OpenDTP 事务模型
是X/Open 这个组织定义的一套分布式事务的标准,也就是定义了规范和 API 接口,由各个厂商进行具体的实现
DTP 是分布式事物处理(Distributed Transaction Processing)的简称 -
XA协议
A是由X/Open组织提出的分布式事务规范;
XA规范主要定义了(全局)事务管理器™和(局 部)资源管理器(RM)之间的接口;
主流的数据库产品都实现了XA接口,是一个双向的系统接口,在事务管理器以及多个资源管理器之间作为通信桥梁/ -
JTA
Java Transaction API,java根据XA规范提供的事务处理标准 -
AP
Application, 应用程序也就是业务层,微服务等 -
RM
Resource Manager,资源管理器。一般是数据库,也可以是其他资源管理器,比如消息队列,文件系统 -
TM
Transaction Manager ,事务管理器、事务协调者,负责接收来自用户程序(AP)发起的 XA 事务指令,并调度和协调参与事务的所有 RM(数据库),确保事务正确完成
- 在分布式系统中,每一个机器节点能够明确知道自己在进行事务操作过程中的 结果是成功还是失败,但无法直接获取到其他分布式节点的操作结果
当一个事务操作跨越多个分布式节点的时候,为了保持事务处理的 ACID 特性,
需要引入一个“协调者”(TM)来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为 AP。
TM 负责调度 AP 的行为,并最终决定这些 AP 是否要把事务真正进行提交到(RM)
XA协议规范-实现分布式事务的原理
-
一般习惯称为 两阶段提交协议(The two-phase commit protocol,2PC)
-
是XA用于在全局事务中协调多个资源的机制,MySql5.5以上开始支持
-
准备阶段:
事务管理器给每个参与者都发送Prepared消息,每个数据库参与者在本地执行事务,并写本地的Undo/Redo日志,此时事务没有提交。
- Undo日志是记录修改前的数据,用于数据库回滚
- Redo日志是记录修改后的数据,用于提交事务后写入数据
-
提交阶段:
- 如果事务管理器收到了参与者的执行失败或者超时消息时,直接给每个参与者发送回滚(Rollback)消息,否则发送提交(Commit)消息;
- 参与者根据事务管理器的指令执行【提交】或者【回滚】操作,并释放事务处理过程中使用的锁资源
- 注意:必须在最后阶段释放锁资源。
XA总结
- XA协议简单,数据库支持XA协议,开发使用成本比较低
- 对业务侵⼊很小,最⼤的优势就是对使⽤⽅透明
- 用户可以像使⽤本地事务⼀样使⽤基于 XA 协议的分布式事务,能够严格保障事务 ACID 特性
- 事务执⾏过程中需要将所需资源全部锁定,也就是俗称的刚性事务
- 刚性事务:遵循ACID
- 柔性事务:遵循BASE理论
- 性能不理想,占用锁资源比较多,高并发常见下无法满足
- 商业付费数据库支持好,mysql目前支持不是很完善
- 基于 XA 协议的 除了2PC,还有 3PC等
- 三段提交(3PC)是二阶段提交(2PC)的一种改进版本 ,为解决两阶段提交协议的阻塞问题
- 采用超时机制,解决TM故障后RM的阻塞问题,但与此同时却多了一次网络通信,性能上也不理想
- 2PC和3PC目前使用不是很多,只做简单了解即可
TCC
- 什么是TCC柔性事务
- 刚性事务:遵循ACID
- 柔性事务:遵循BASE理论
- TCC:
- 将事务提交分为
- Try:完成所有业务检查( 一致性 ) ,预留必须业务资源( 准隔离性 )
- Confirm :对业务系统做确认提交,默认 Confirm阶段不会出错的 即只要Try成功,Confirm一定成功
- Cancel : 业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放, 进行补偿性
- TCC 事务和 2PC 的类似,Try为第一阶段,Confirm - Cancel为第二阶段,它对事务的提交/回滚是通过执行一段 confirm/cancel 业务逻辑来实现,并且也并没有全局事务来把控整个事务逻辑
- 将事务提交分为
含义 | 操作方法 |
---|---|
预留业务资源/数据效验 | Try |
确认执行业务操作,提交数据,不做任何业务检查,try成功,confirm必定成功,需保证幂等 | Confirm |
取消执行业务操作,回滚数据,需保证幂等,也是常说的补偿性事务 | Cancel |
- TCC的交互图(图片来源网上,没有找到原出处,侵权必删)
- 优点:
- 它把事务运行过程分成 Try、Confirm/Cancel 两个阶段
- 每个阶段由业务代码控制,这样事务的锁力度可以完全自由控制
- 不存在资源阻塞的问题,每个方法都直接进行事务的提交
- 缺点
- 在业务层编写代码实现的两阶段提交,原本一个方法,现在却需要三个方法来支持
- 对业务的侵入性很强,不能很好的复用
- 注意:使用TCC时要注意Try - Confirm - Cancel 3个操作的幂等控制,由于网络原因或者重试操作都有可能导致这几个操作的重复执行
简介:讲解分布式事务的解决方案之一事务消息
-
事务消息
- 消息队列提供类似Open XA的分布式事务功能,通过消息队列事务消息能达到分布式事务的最终一致
-
半事务消息
- 暂不能投递的消息,发送方已经成功地将消息发送到了消息队列服务端,但是服务端未收到生产者对该消息的二次确认,此时该消息被标记成“暂不能投递”状态,处于该种状态下的消息即半事务消息。
-
消息回查
- 由于网络闪断、生产者应用重启等原因,导致某条事务消息的二次确认丢失,消息队列服务端通过扫描发现某条消息长期处于“半事务消息”时,需要主动向消息生产者询问该消息的最终状态(Commit或是Rollback),该询问过程即消息回查
-
交互图(来源rocketmq官方文档)
-
目前较为主流的MQ,比如ActiveMQ、RabbitMQ、Kafka、RocketMQ等,只有RocketMQ支持事务消息
- 如果其他队列需要事务消息,可以开发个消息服务,自行实现半消息和回查功能
-
好处
- 事务消息不仅可以实现应用之间的解耦,又能保证数据的最终一致性
- 同时将传统的大事务可以被拆分为小事务,能提升效率
- 不会因为某一个关联应用的不可用导致整体回滚,从而最大限度保证核心系统的可用性
-
缺点
- 不能实时保证数据一致性
- 极端情况下需要人工补偿,比如 假如生产者成功处理本地业务,消费者始终消费不成功
Seata介绍
-
分布式事务框架
- TX-LCN:支持2PC、TCC等多种模式
- https://github.com/codingapi/tx-lcn
- 更新慢(个人感觉处于停滞状态)
- Seata:支持 AT、TCC、SAGA 和 XA 多种模式
- https://github.com/seata/seata
- 背靠阿里,专门团队推广
- TX-LCN:支持2PC、TCC等多种模式
-
为啥选择Seata呢
- git地址
- https://github.com/seata/seata
- 背靠阿里,专门团队推广和技术团队
- 已经线上大规模使用且没出现重大漏洞
- 和主流微服务框架轻松整合
- 社区活跃度高、文档齐全、功能强大
(图片来源seata官网)
- git地址
什么是Seata
-
一个开源的分布式事务框架, 由阿里中间件团队发起的开源项目Fescar,后更名为Seata
-
中文文档地址
-
详细介绍
是一款开源的分布式事务解决方案,致力于在微服务架构下提供高性能和简单易用的分布式事务服务。
在 Seata 开源之前,Seata 对应的内部版本在阿里经济体内部一直扮演着分布式一致性中间件的角色,帮助经济体平稳的度过历年的双11,对各BU业务进行了有力的支撑。
经过多年沉淀与积累,商业化产品先后在阿里云、金融云进行售卖。
2019.1 为了打造更加完善的技术生态和普惠技术成果,Seata 正式宣布对外开源,未来 Seata 将以社区共建的形式帮助其技术更加可靠与完备 -
Seata主要由三个重要组件组成:
-
TC:Transaction Coordinator 事务协调器,管理全局的分支事务的状态,用于全局性事务的提交和回滚。
-
TM:Transaction Manager 事务管理器,用于开启、提交或者回滚【全局事务】。
-
RM:Resource Manager 资源管理器,用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接受TC的命令来提交或者回滚分支事务
- 传统XA协议实现2PC方案的 RM 是在数据库层,RM本质上就是数据库自身;
- Seata的RM是以jar包的形式嵌入在应用程序里面
-
架构:TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端
XID
- TM 请求 TC 开启一个全局事务, TC 会生成一个 XID 作为该全局事务的编号XID, XID会在微服务的调用链路中传播,保证将多个微服务的子事务关联在一起
一个典型的事务过程包括:
- A服务的TM 向 TC 申请开启(Begin)一个全局事务,全局事务创建成功并生成一个全局唯一的 XID。
- A服务的RM向TC注册分支事务
- A服务执行分支事务,对数据库做操作
- A服务开始远程调用B服务,并把XID 在微服务调用链路的上下文中传播。
- B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖
- B服务执行分支事务,向数据库做操作
- 全局事务调用链处理完毕,TM 根据有无异常向 TC 发起针对 XID 的全局提交(Commit)或回滚(Rollback)决议。
- TC 调度 XID 下管辖的全部分支事务完成提交(Commit)或回滚(Rollback)请求。
- Seata 实现分布式事务,关键角色UNDO_LOG(回滚日志记录表)
在每个应用需要分布式事务的业务库中创建这张表,这个表的核心作用是将业务数据在更新前后的数据镜像组织成回滚日志,保存在UNDO_LOG表中,以便业务异常能随时回滚这张表的sql语句在官网上提供了,有兴趣的可以直接去官网学习,也可以关注一下我,之后来讲解seata的生产运用)
seats有四种模式:只讲最常用的AT
-
AT
-
AT模式可以应对大多数的业务场景,并且基本可以做到无业务入侵、开发者无感知
-
用户只需关心自己的 业务SQL. AT 模式分为两个阶段,可以认为是2PC
- 一阶段:执行用户SQL
Seata 会拦截“业务 SQL”,找到“业务 SQL”要更新的业务数据,在业务数据被更新前,将其保存成“before image”,然后执行“业务 SQL”更新业务数据 在业务数据更新之后,再将其保存成“after image”,最后生成行锁 以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性
- 二阶段:seata框架自动生成提交或者回滚
二阶段提交: 因为“业务 SQL”在一阶段已经提交至数据库, 所以 Seata 框架只需将阶段一保存的快照数据和行锁删掉,完成数据清理即可。 二阶段回滚: 还原业务数据, 回滚方式便是用“before image”还原业务数据; 但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image” 如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写,出现脏写就需要转人工处理
-
-
TCC
-
Sage
-
XA
欢迎点赞,评论区交流!
之后会不断更新技术文章,欢迎关注!