分布式事务讲解 - 2PC、3PC、TCC
前置知识
BASE理论:
- BASE是Basically Availbale(基本可用)、Soft state(软状态)、Eventually consistent(最终一致性)三个词语的缩写。
- BASE理论是对CAP理论中AP的一个扩展,通过牺牲强一致性来获得可用性,当系统出现故障时,允许部分功能不可用,但是要保证核心功能可用,允许数据在一段时间内不一致,但是数据最终要达到一致状态。\
- 基本可用:分布式系统再出现故障时,允许损失部分功能的可用性,保证核心功能必须可用。
- 软状态:由于不要求数据的强一致性,所以BASE理论允许系统中出现中间状态(软状态),如:“支付中”、“数据同步中”等状态,待数据达到最终一致后状态修改为最终状态。
- 最终一致性:指经过一段时间后,所有节点的数据将会达到一致状态,即软状态都变成了最终状态,但是这个需要一定时间的延迟和等待。
What(什么是分布式事务)
- 一个操作由多个不同服务器的子操作组成,分布式事务就是要保证这些子操作要不全部执行成功,要不全部执行失败。从本质上讲,分布式事务就是为了保证不同数据库的数据一致性。
Why(为什么使用分布式事务)
-
使用分布式事务的原因总结起来就一个:
-
一个逻辑对于不同数据库的操作会造成数据不一致的情况,可能一个数据库数据修改成功,但是另一个数据库修改失败,但是这个又不能在本地事务进行处理,为了解决这个问题,分布式事务应运而生。
-
有人可能会问,微服务情况下调用(使用OpenFeign、RestTemplate等)其他服务方法时,如果一个本地事务执行失败就返回异常,其他服务已执行的操作回滚不就可以了吗?
-
如果按照从上往下顺序分别调用【订单服务】、【库存服务】、【积分服务】,这三个服务都要操作数据库,如果前两个服务操作成功,但是调用第三个服务时操作失败,那该怎么办,前两个服务的操作能回滚吗?不能,除非你保存了前两个服务执行的更新sql的所有undo操作,所以,在这种情况下,只能用分布式事务来解决这种数据不一致问题。
How(怎么使用分布式事务)
- 到现在,分布式事务已经有很多的解决方案了,有2PC、3PC、TCC,这一篇博客,我们先来分别讲讲最早的2PC、3PC这两种解决方案的模型及理论基础,以后再丰富其他的分布式事务解决方案。
2PC(Two Phase Commit)
工作流程
- 2PC工作流程分两个阶段。
第一阶段
- 第一阶段:执行事务操作
- 事务询问。
- 协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
- 执行事务。
- 参与者节点执行询问发起的所有事务操作,并将Undo信息和Redo信息写入数据库日志。(若操作成功,这里其实每个参与者已经执行了事务操作)
- 各参与者向协调者反馈事务询问的响应。
- 各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则反馈一个“同意”消息;如果执行失败,则反馈一个“中止”消息。
第二阶段
- 第二阶段:执行事务提交
- 当所有参与者节点在第一阶段向协调者节点反馈的消息全都是“同意”时:
- 发送事务提交请求
- 协调者节点向所有参与者节点发出“正式提交(commit)”请求。
- 事务正式提交
- 参与者节点正式完成提交操作,并释放在整个事务期间占用的资源。
- 反馈事务提交结果
- 参与者节点向协调者节点发送“完成”消息。
- 完成事务
- 协调者节点收到所有参与者节点反馈的“完成”消息后,完成分布式事务。
- 当任一参与者节点在第一阶段反馈的响应为“中止”,或者协调者在第一阶段接收反馈信息超时:
- 发送事务回滚请求
- 协调者节点向所有参与者节点发出“回滚操作(rollback)”请求。
- 事务回滚
- 各参与者节点收到请求,利用之前写入的Undo信息执行本地事务回滚操作,并释放在整个事务期间占用的资源。
- 反馈事务回滚结果
- 参与者节点向协调者节点发送“回滚完成”消息。
- 中断事务
- 协调者节点收到所有参与者节点反馈的“回滚完成”消息后,取消事务。
3PC(Three Phase Commit)
- 3PC没有解决2PC所有问题,只是降低了灾难的发生概率。
TCC(Try Commit Cancel)
- 这个模型有两个个阶段,但是与2PC的三个阶段又有所不同。
第一阶段
第一阶段:执行Try操作
- 事务发起。
- 协调者节点向所有参与者节点发起执行业务逻辑包含数据操作的命令,并开始等待各参与者节点的响应。
- 执行数据操作。
- 直接对数据库的数据执行操作。
- 各参与者向协调者反馈执行操作的响应。
- 各参与者节点响应协调者节点发起的数据操作。如果参与者节点的数据操作实际执行成功,则反馈一个“同意”消息;如果有一个参与者执行失败,则反馈一个“中止”消息。
第二阶段
- 第二阶段:执行Confirm/Cancel操作
- 根据阶段一各事务参与者的响应,如果所有事务参与者在阶段一执行成功,那就执行Confirm操作,有一个事务参与者在阶段一执行失败,就执行Cancel操作,进行数据库数据恢复。
- Confirm 操作用于执行数据操作成功之后的业务逻辑。
- Cancel 操作用于恢复阶段一对数据的操作。
网络语录解析
- 【Confirm操作使用预留的资源执行业务操作】
- 就是在数据更新完这件事的基础上进行后续逻辑的执行。
- 【Cancel操作取消执行业务操作,释放预留的资源】
- 就是恢复数据,可以看做是一个搂底的操作。
总结
2PC、3PC、TCC区别
- 在2PC的第一阶段基础之上,3PC新加了一个准备阶段(上图所示),用于询问所有参与者节点是否已经准备好要进行分布式事务操作了,这一阶段没有对资源的占用,只是测试数据库是否能获取锁即可,只是保证所有参与者都有能力参与事务,如果有网络或者其他问题就不用进行第二、三阶段了。
- 在3PC的所有阶段,都为所有参与者节点和协调者节点添加了超时机制,防止因某一节点宕机造成的资源无法释放问题。
- 2PC中所有阶段,只对协调者节点添加了超时机制。
- TCC模型主要是在应用层做的一个分布式事务,2PC和3PC则是在数据库层做的分布式事务。
- TCC模型可以用于不支持事务的数据库,但是2PC和3PC要求数据库必须支持事务。
2PC存在问题
- 2PC只对协调者节点添加了超时机制,如果协调者这一重要角色宕机,所有参与者的资源就会一直得不到释放,降低系统的可用性。
- 2PC中,当其中任一节点宕机情况出现时,不能保证数据的最终一致性。
TCC存在问题
- 代码编写难度高
每个参与者的业务都要写try、confirm、cancel三个操作,并且各个操作的业务都有所不同 - 为满足一致性,要满足try、canfirm、cancel三个操作的幂等。
TCC的优点
- TCC的优点全是因为实现了应用层的分布式事务,优点如下:
- 可以降低数据库锁冲突,提高吞吐量。
- 可以通过应用控制数据库锁的粒度,而不用像2PC和3PC使数据库阻塞等待。
- 可以通过部署集群解决单点故障。
对比2PC,3PC有什么优点
- 为所有参与者节点和协调者节点添加了超时机制:
- 1)如果协调者等待参与者反馈信息超时,直接给所有参与者发送中断分布式事务请求。
- 2)如果在第三阶段任一参与者等待协调者提交事务信息超时,那就默认提交事务
添加超时机制,可以防止因某一节点宕机造成的资源无法释放问题以及资源占用时间过长问题。
- 在第一阶段,不锁定资源,就好像在Synchronized外面加了个if条件,能降低分布式事务执行失败率。