生活就像海洋,只有意志坚强的人,才能到达彼岸 —— 马克思
一、导读
事务提供一种机制将一个活动涉及的所有操作纳入到一个不可分割的执行单元,保整原子性、持久性、隔离性(执行过程中互不可见)、一致性,提供一种“要么什么都不做,要么做全套(All or Nothing)”机制。
分布式事务可以看为两块,一个是service产生多个节点,另一个是resource产生多个节点。
- 多个service间进行rpc调用操作,一系列的操作组成了一个事务
- 多个resource的修改放在一个事务里完成(分库分表导致异地更新、多个表修改、缓存修改等)
ACID相关概念可以参考我早期写的一篇博客: 博文链接
二、分布式理论
CAP理论
C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。
A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。
P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。
对于分布式,P是必须的,CA选其一达成CP(牺牲一点可用达成强一致)、AP(牺牲一点一致性达成高可用)。同时对于被牺牲的那个特性,也要尽可能保证。
比如你选择了CP,并不是叫你放弃A。因为P出现的概率实在是太小了,大部分的时间你仍然需要保证CA。就算分区出现了你也要为后来的A做准备,比如通过一些日志的手段,使其他机器恢复至可用。
BASE理论: AP理论的衍生
- 基本可用:分布式系统在出现故障时,允许损失部分可用功能,保证核心功能可用。
- 软状态:允许系统中存在中间状态,这个状态不影响系统可用性,这里指的是CAP中的不一致。
- 最终一致:最终一致是指经过一段时间后,所有节点数据都将会达到一致。
业内通常把严格遵循 ACID 的事务称为刚性事务;而基于 BASE 思想实现的事务称为柔性事务。柔性事务并不是完全放弃了 ACID,仅仅是放宽了一致性要求:事务完成后的一致性严格遵循,事务中的一致性可适当放宽(最终一致性)。
三、分布式解决方案
warm: 不要为了过度设计导致需要分布式事务
我见过太多团队一个人维护几个微服务,太多团队过度设计,搞得所有人疲劳不堪,而微服务过多就会引出分布式事务,这个时候我不会建议你去采用下面任何一种方案,而是请把需要事务的微服务聚合成一个单机服务。
2PC策略
2PC策略就是两阶段提交策略。核心流程如下所示(以XA协议举例),在各大主流数据库都有自己实现。
XA协议基于X/Open Distributed Transaction Processing(DTP)模型,该模型中包含一个全局事务管理器(TM,Transaction Manager)和多个资源管理器(RM,Resource Manager)。由于事务管理器是单点的,可能会成为分布式事务的性能瓶颈。
全局事务管理器
负责管理全局事务状态与参与的资源,协同资源一起提交或回滚资源管理器
则负责具体的资源操作
原子性保障
![img](https://i-blog.csdnimg.cn/blog_migrate/cec06c77a8a6de7fcfd4399a827e4b26.png)
XA协议分为两阶段,一阶段做本地事务执行和预提交校验,如果预提交通过则commit事务
,否则回滚本地事务。
准备阶段:
TM 向每个 RM 发送准备消息。如果 RM 的本地事务操作执行成功,则返回成功;如果 RM 的本地事务操作执行失败,则返回失败。
提交阶段
如果 TM 收到了所有 RM 回复的成功消息,则向每个 RM 发送提交消息;否则发送回滚消息;RM 根据 TM 的指令执行提交或者回滚本地事务操作,释放所有事务处理过程中使用的锁资源。
隔离性保障
XA协议中的隔离性由各个RM(资源管理器)保障。在一个本地事务中,每执行一条更新操作之前,都会先获取对应的锁资源,只有获取锁资源成功才会执行该操作,并且一旦获取了锁资源就会持有该锁资源直到本事务执行结束。
RM可以保障本地事务对于其他事务不可见,但当出现事务套事务
的情况时,中间事务的状态可能暴露出来。
用下图举个例子,RM1和RM2本省是可以保证事务隔离性的,但在在 RM1 的本地子事务提交完毕到 RM2 的本地子事务提交完毕之间,RM1作为中间状态是可以被观察到的。
MySQL建议使用SERIALIZABLE
级别的隔离级别来保证全局事务隔离性,但这个性能太差,许多分布式实物库都实现了分布式 MVCC 机制来提供全局的一致性读,使用快照
机制保证中间事务状态不被读取。
小结
- 业务方只需要关注自己的
业务SQL
,由XA框架实现事务保障
。 - XA协议一般来说只适合短时间的事务,因为整个事务期间都是独占数据,长时间的事务容易造成堵塞。
TCC协议
一句话总结: 业务开发程序员保障分布式事务。
TCC(Try-Confirm-Cancel)由业务活动管理器控制一致性。它不依赖资源管理器(RM)对分布式事务的支持,而是通过对业务逻辑的分解来实现分布式事务。业务接入 TCC 模型需要拆分业务逻辑成两个阶段,并实现 Try、Confirm、Cancel 三个接口,定制化程度高,开发成本高。
原子性保障
TCC 模型也使用 2PC 原子提交协议来保证事务原子性。Try 操作对应 2PC 的一阶段准备(Prepare);Confirm 对应 2PC 的二阶段提交(Commit),Cancel 对应 2PC 的二阶段回滚(Rollback),可以说 TCC 就是应用层的 2PC。
一致性保障
TCC 分布式事务模型仅提供两阶段原子提交协议,保证分布式事务原子性。事务的隔离交给业务逻辑来实现。
隔离性保障
一般来讲,服务之间的一致性比服务内部的一致性要更加容易弱化,这也是为什么 XA 等直接在资源层面上实现通用分布式事务的模型会注重一致性的保证,而当上升到服务层面,服务与服务之间已经实现了功能的划分,逻辑的解耦,也就更容易弱化一致性,这就是 SOA 架构下 BASE 理论的最终一致性思想
TCC策略其实可以看做服务层面的两阶段提交协议,是否需要保证中间事务的状态隔离性由具体的业务而定。
Saga 协议
Saga 是一种补偿协议,在 Saga 模式下,分布式事务内有多个参与者,每一个参与者都是一个冲正补偿服务,需要用户根据业务场景实现其正向操作和逆向回滚操作。
本质上就是说在一个事物中的多个正向操作里如果有一个正向操作失败了,则所有操作过的正向操作所对应的逆向操作会执行。
小结
除了以上三种解决方案外还有其他的方案,例如本地消息表
、尽最大努力通知
等,可以参考这个文章小米技术团队,具体的选型结合业务进行判断。