分布式事务
1. 分布式事务产生场景
最近学习了分布式事务相关的知识,并在组内进行分享,对分布式事务进行整理总结
1.1 本地事务
指传统的单机数据库事务,更多的是通过关系型数据库来控制事务. 满足数据库事务的四大特性 ACID.
本地事务主要解决的还是事务的 原子性 和 一致性 ,需要保证从事务开始阶段->事务结束阶段的操作为一个原子操作,整个事务要么全部成功、要么全部失败,且保证数据的一致性。
2.1 分布式事务产生得3种场景
- 场景一
单体系统访问多个数据库实例, 跨数据库实例 产生分布式事务.
创建订单扣款流程为例:服务为单体架构,但对数据库进行了分库(订单库/账户库)。如果生成订单成功后,进行扣款就会有很多问题(账户钱不够 / 账户DB崩溃 /网络错误),可能导致数据不一致问题。
- 场景二
多服务访问同一数据库, 持不同数据库链接 操作产生分布式事务.
创建订单扣款流程为例:服务为多架构,服务直接通过远程调用。在调用账户服务进行扣款就会有很多问题(账户钱不够 / 账户DB崩溃 /网络错误),极大可能出现数据不一致情况。
- 场景三
典型的场景微服务架构,微服务之间通过远程调用完成事务.
创建订单扣款流程为例:整体架构微服务,多数据库、多服务实例,分布式事务是必须解决的问题。
2. CAP理论 & BASE 理论
目前分布式事务理论两个,CAP理论 和 BASE理论
2.1 CAP理论
1998年,加州大学的计算机科学家 Eric Brewer 提出、分布式系统有三个指标
为讲清楚三个指标以 数据库主从同步示例说明:
- Consistency (一致性):
在执行写操作后,从任意节点读,读取的数据都是最新状态。
- Availability (可用性):
任何事务操作都可以得到响应结果,且不会出现响应超时或响应错误。即使数据还没有同步过来,哪怕是旧数据也要返回。
- Partition tolerance(分区容忍性):
分布式系统各结点部署在不同子网,可能会因网络问题导致结点间通信失败,但仍可对外提供服务。
2.1.1 CAP三种组合
CAP只能两两组合,不能三种共存
- AP(可用性、分区容忍):
追求可用性和分区容忍性,放弃强一致性,这是很多分布式系统设计时的选择。
比如:订单退款,今日退款成功,明日账户到账,只要用户可以接受在一定时间内到账即可。
- CP (强一致性、分区容忍):
追求强一致性和分区容错性,放弃可用性,zookeeper其实就是追求的强一致。
比如:跨行转账,一次转账请求要等待双方银行系统都完成整个事务才算完成。
- CA(强一致性、可用性):
放弃分区容忍性,即不进行分区,系统将不是一个标准的分布式系统。
2.2 BASE理论
- 基本可用
出现故障时,允许损失部分可用功能,保证核心功能可用。
如:电商网站交易付款出现问题了,商品依然可以正常浏览
- 软状态
不要求强一致性,允许系统中存在中间状态(软状态),这个状态不影响系统可用性。
如:订单的“支付中”、“数据同步中”、“处理中”等状态
- 最终一致
指经过一段时间后,所有节点数据都将会达到一致
如:订单的“支付中”状态,最终变为“支付成功” 或 “支付失败”,但需要一定时间的延迟等待。
3. 分布事务解决方案
以理论为基础,针对不同的分布式场景业界常见的解决方案。
- 2PC(两阶段提交协议)
- TCC(Try - Confirm - Cancle)
- 最大努力通知
- 可靠消息最终一致性
3.1 两阶段提交2pc
3.1.1 示例
示例:在讲2cp之前我们一起来看一个下单付款的业务。
该业务存在几个问题:
- 库存不够
- 账户钱不够
- 远程调用网络故障
- 商品DB崩溃
问题本质:不知道该业务每个步骤执行的情况?
对上面方案进行改进:
添加事务管理器(中间协调者): 将各事务执行的情况通知管理器,由管理器根据情况统一通知各事务提交 或者 回滚。
3.1.2 两阶段提交2pc定义
两阶段提交协议将全局事务拆分为两个阶段来执行:
- 阶段一:准备阶段,各本地事务完成准备工作,但此时事务没有提交。
- 阶段二:执行阶段,协调者根据阶段一的结果,进行通知提交或者回滚。
这里大家可能会提出疑问
- 预留资源失败:协调者统一进行回滚操作
- 二阶段通知失败:协调者可重复通知
3.1.3 两阶段提交2pc优缺点
-
优点
操作简单,数据的一致性 -
缺点
单点故障,协调者挂了,每个事务会锁定资源性能低(传统)
3.2 TCC(Try - Confirm - Cancle)
3.2.1 TCC定义
阶段补偿方案,柔性事务
- Try:资源检测 和 预留
- Confirm:执行业务提交,要求Try成功Confirm必成功。
- Cancle:预留资源释放。
每个系统都必须开发try / confirm / cancle 三个方法
3.2.2 示例
示例:订单生成业务后进行扣款/减库存/加积分
第一阶段:在生成订单后,调用库存服务这个时候不是直接减库存,而是冻结库存。
第二阶段:进行Confirm / cancle,如果第一阶段都成功,那么会调用所有服务的confirm进行业务提交,否则调用Cancle进行释放资源
问题:
- 预留资源失败:调用二阶段的Cancle进行释放资源。
- Confirm/cancle失败:进行重试。
- Confirm重复执行是否或多扣款:保证接口的幂等性。
3.2.3 优缺点
-
优点
1. 每个阶段会提交本地事务并释放锁,性能比较高。,2. 不需要等待其它事务执行结果,如其他事务失败,则执行补偿操作。 -
缺点
1. 业务复杂,实现try、confirm、cancle,2. 程序员要求高,需要考虑幂等性,重试机制,confirm/cancle在try之前执行处理
3.3 最大努力通知
3.3.1 定义
目标:发起方最大努力将处理结果通知到接收方
注意点:
- 因为接收方可能没收到消息,消息重复通知
- 消息校对机制,接收方可主动查询
- 接收方需要保证消费幂等性
使用场景:
- 对于一致性要求较低的场景可使用
- 事务完成的因素在于接收方
3.3.2 示例
进行业务校对是很重要的
3.4 可靠消息最终一致性
发起方完成本地事务后,一定成功发出消息。 消费者一定能接收消息,并处理事务成功
抛出问题:如何保证本地事务 和 发消息的原子性?
- 基于本地消息表方案
将消息持久化到DB, 事务完成的必要因素在于发起方
- 基于RocketMQ方案
RocketMQ 是一个来自阿里巴巴的分布式消息中间件, 事务消息设计则,就是为了解决消息发送与本地事务执行的原子性问题。