【重难点】【事务 03】分布式事务
文章目录
一、为什么需要分布式系统架构
项目业务越来越复杂,单体系统也越来越臃肿,代码间的耦合度越来越高,每一个小改动都需要经历大量的测试才能上线,开发效率低下。此外,新人上手的成本也越来越高
因此我们需要对臃肿的单体系统进行水平拆分,把共用的业务封装成独立的服务,供其他业务调用。把相关业务封装成子系统并提供接口,供其他系统调用。这样进行拆分之后,每个子系统内部的修改不会影响其他子系统,降低了系统的维护成本,提高了开发效率
二、系统架构演进
1.单体应用架构
优点
- 架构简单
缺点
- 代码耦合
- 难以扩展
- 难以针对性地进行优化
2.垂直应用架构
将单体应用拆分为多个互不相干的子应用
优点
- 可以根据不同子应用的访问情况,有针对性地进行优化
- 子应用故障不影响其他子应用
缺点
- 子应用之间无法调用
- 子应用难免存在重叠业务,需要重复开发
3.分布式架构
将垂直应用架构中重叠业务抽象出来,形成统一的服务
优点
- 提高了代码的复用性
缺点
- 系统间的调用关系变得复杂
- 系统维护成本高
4.SOA 架构
在分布式架构中增加统一的服务调度中心
优点
- 服务自动注册与发现
缺点
- 某个服务故障,可能导致整个系统崩溃
- 服务间的调用关系复杂
- 系统维护成本高
5.微服务架构
微服务架构强调的是服务划分,对服务彻底拆分,各服务独立打包、独立部署和独立升级
优点
- 版本发布非常快
- 业务划分明确,利于扩展和维护
缺点
- 开发成本高
- 涉及分布式事务问题
典型案例
- 主业务服务:订单服务+订单数据库
- 从业务服务:库存服务+库存数据库、积分服务+积分数据库、仓储服务+仓储数据库
三、分布式事务
项目业务越来越复杂,数据也越来越多,当单台数据库无法存储海量数据时,就需要多台数据库,形成了分布式数据库。数据分散存储后,对于数据的增删改查操作就会变得复杂,尤其是难以保证数据的一致性,也就是分布式事务问题
分布式事务场景
- 跨 JVM 进程:商城系统中的订单微服务和库存微服务,用户下单时会访问订单微服务。订单微服务在生成订单记录时,会调用库存微服务扣减库存,两个微服务部署在不同的 JVM 进程中
- 跨数据库:商城系统中的订单数据库和交易数据库,用户发起退款时,在交易数据库中执行退款操作,在订单数据库中将订单状态变更为已退款,两个数据存放在不同的数据库中
- 多服务访问单数据库:多个服务同时操作同一个数据库中的同一条数据
四、分布式事务理论
1.CAP 理论
C 是一致性,A 是可用性、P 是分区容忍性。CAP 理论就是这三者不能同时满足
在分布式系统中,分区容错性是必须的,如果因为一个分区故障或者分区之间通信故障,导致整个分布式系统不可用,这显然违背了分布式系统的初衷。既然 P 已经确定,那么就要在 C 和 A 之间取舍
选择 C,就意味着,如果出现分区之间通信故障,那么任何一个分区都不能写入数据,因为各个分区之间无法进行数据同步,会导致分区之间数据不一致。如果出现分区故障,导致各个分区之间的数据版本不一致,那么所有分区都无法读取数据
选择 A,就意味着,即使分区数据不一致,各个分区也要正常地独立处理请求
综上,要想 CA 同时满足,那么分布式系统就不能出现任何异常,或者当一个节点出现异常时就关停整个系统,这显然不可能
AP 系统
AP 系统并不是放弃了一致性,而是采用最终一致性,允许多个节点的数据在一定的时间内存在差异
CP 系统
只有对数据的一致性要求比较高的系统才会采用 CP 组合,追求强一致性,比如金融业务
2.BASE 理论
BA 是基本可用,S 是软状态,E 是最终一致性
基本可用
允许部分数据不可用,但是要保证核心功能可用
软状态
允许系统中存在中间状态,也就是允许节点之间的数据同步存在延迟,例如 “支付中” 和 “退款中”,一段时间后会变成 “支付成功” 和 “退款成功”
最终一致性
允许数据在一段时间内不一致,但经过一段时间后,数据最终是一致的
五、强一致性分布式事务
强一致性分布式事务要求应用程序在任何时间,读取任意节点上的数据,都是最新写入的
1.DTP
DTP 只是一个分布式事务的标准,这套标准主要定义了实现分布式事务的规范,具体的实现规则交给相应的厂商实现
核心组件
- 应用程序
- 资源管理器:数据库节点
- 事务管理器:负责管理全局事务和资源管理器,并提供编程接口
重要概念
- 全局事务:由事务管理器管理的事务,能够一次性操作多个资源管理器
- 分支事务:由事务管理器管理的全局事务中,每个资源管理器中独立执行的事务
- 控制线程:执行全局事务的线程,这个线程用来关联应用程序、事务管理器和资源管理器三者之间的关系
假设系统中有两个数据库节点,也就是有两个资源管理器 A 和 B,应用程序启动时,向事务管理器注册 A 和 B 两个资源管理器
2.2PC
Prepare 阶段
事务管理器询问资源管理器是否可以提交事务,资源管理器执行分支事务并持久化后,根据执行结果返回可以提交或者不可以提交
Commit 阶段
如果资源管理器都返回可以提交,那么事务管理器令所有资源管理器提交;只要有一个资源管理器返回不可以提交,那么事务管理器会令所有资源管理回滚
2PC 模型存在的问题
- 同步阻塞:参与全局事务的资源在整个过程中会被阻塞
- 单点故障:事务管理器故障,所有资源管理器都会阻塞
- 数据不一致:事务管理器发送 Commit 消息时,如果部分资源管理器由于故障没有收到,会导致数据不一致
- 无法解决的问题:事务管理器发出 Commit 消息后宕机,而唯一接收这条消息的资源管理器也宕机,则无法确认事务是否已经提交
单点故障的解决方案:集群
数据不一致解决方案:记录资源管理器在每个流程中的执行状态,需要包含的字段有,事务 id、事务管理器 id、资源管理器 id、分支事务状态、更新时间和超时时间
3.3PC
CanCommit 阶段
事务管理器向资源管理器发送准备请求,资源管理器返回 Yes、No 或者超时
事务管理器汇总返回结果,如果收到任意一个 No 或者超时,就发送中止消息,否则进入 PreCommit 阶段
PreCommit 阶段
事务管理器询问资源管理器是否可以提交,资源管理器返回 Yes、No 或者超时
事务管理器汇总返回结果,如果收到任意一个 No 或者超时,就发送中止消息,否则进入 doCommit 阶段
doCommit 阶段
事务管理器向资源管理器发送提交请求,资源管理器正式提交事务
如果超时,资源管理器也会正式提交事务
3PC 存在的问题
3PC 主要解决了单点故障的问题,并减少了同步阻塞。如果事务管理器故障,那么资源管理器在一段时间后就会自动提交
但是并没有解决数据不一致的问题,在 PreCommit 阶段,某个资源管理器返回 Yes,但是由于网络故障导致这个资源管理器无法收到事务管理器的消息,此时如果另一个资源管理器返回 No,当前资源管理器是无从得知的,只会收到超时的消息,然后自动提交事务
2PC 和 3PC 的抉择
3PC 提交多了一个阶段,因此性能不如 2PC
即使解决了单点故障和减少了同步阻塞,但依靠超时来检测单点故障并不可靠,有的时候只是网络延迟
我们可以采用选举算法来检测单点故障
综合考虑,2PC 的应用还是更广泛
六、最终一致性分布式事务
1.TCC
TCC 分布式事务是一种在应用层实现的 2PC,它将一个完整的业务操作分为 Try、Confirm 和 Cancel 三个阶段,并且 Cacel 能够补偿 Confirm 的操作
Try 阶段
完成业务检查和预留资源
Cofirm 阶段
使用 Try 阶段预留的资源执行业务
Cancel 阶段
释放 Try 阶段预留的资源并回滚数据
TCC 核心组成
- 主业务服务:TCC 分布式事务的发起方
- 从业务服务:负责执行 Try、Confirm 和 Cancel,因此一个规范的从业务服务应该包含 Try、Confirm 和 Cancel 三个接口。并且 Confirm 和 Cancel 可能会被重复调用,因此必须保证它们的幂等性
- TCC 管理器:管理并控制全局事务,Try 阶段成功时,自动调用每个从业务服务的 Confirm,Try 阶段失败时,自动调用每个从业务服务的 Cancel
典型案例
- 主业务服务:订单服务+订单数据库
- 从业务服务:库存服务+库存数据库、积分服务+积分数据库、仓储服务+仓储数据库
TCC 存在的问题
- 代码需要耦合到具体业务中,每个业务方法都要拆分成 Try、Confirm 和 Cancel 三个方法,提高了开发成本
- 需要生成全局事务记录
- 需要注意幂等问题
2.可靠消息最终一致性分布式事务
可靠消息最终一致性分布式事务指的事务发起方执行完本地事务后,利用消息队列发出一条消息,事务的参与方能够成功消费这条消息,事务最终达到一致的状态
存在的问题
- 为了保证消息的可靠性,消息可能被重复投递并消费,因此需要注意接口的幂等性
- 需要注意防止消息丢失、消息的顺序性以及保证消息队列的可用性
3.最大努力通知型分布式事务
当分布式事务跨越两个不同的系统并且对一致性要求不高时,通常会使用到最大努力通知型分布式事务
与可靠消息最终一致性分布式事务的区别
- 可靠消息最终一致性要求消息一定能够成功发送和接收;最大努力通知型尽最大努力将消息通知给接收方,但是消息可能丢失
- 可靠消息最终一致性相对来说对时间更敏感;最大努力通知型相对来说对时间不敏感,只需要将消息发送出去即可
- 可靠消息最终一致性要求事务最终必须一致,当消息丢失会自动重发;最大努力通知型往往不会自动重发,只有主动调用查询接口发现消息丢失时,再根据需要选择是否重发消息