分布式系列第二弹:分布式事务!

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 分布式系列第二弹:分布式事务!

之前文章:分布式系列第一弹:分布式一致性!

柔性事务

柔性事务主要分为补偿型和通知型。

补偿型事务分:TCC、Saga。

通知型事务分:MQ事务消息、最⼤努⼒通知型。

补偿型事务都是同步的,通知型事务都是异步的。

二阶段提交

二阶段提交协议(2PC)

二阶段提交算法的成立基于以下假设:

在该分布式系统中,存在一个节点作为协调者(Coordinator),其他节点作为参与者(Participants),且节点之间可以进行网络通信;

所有节点都采用预写式日志,日志被写入后被保存在可靠的存储设备上,即使节点损坏也不会导致日志数据的丢失;

所有节点不会永久性损坏,即使损坏后仍然可以恢复。

两阶段提交中的两个阶段,指的是 Commit-Request 阶段和 Commit 阶段,两阶段提交的流程如下:

提交请求阶段(Commit-Request)

在提交请求阶段,协调者将通知事务参与者准备提交事务,然后进入表决过程。

在表决过程中,参与者将告知协调者自己的决策:

  • 同意(事务参与者本地事务执行成功)或取消(本地事务执行故障),在第一阶段,参与节点并没有进行Commit操作。

提交阶段(Commit)

在提交阶段,协调者将基于第一个阶段的投票结果进行决策:

提交或取消这个事务,这个结果的处理和前面基于半数以上投票的一致性算法不同,必须当且仅当所有的参与者同意提交,协调者才会通知各个参与者提交事务,否则协调者将通知各个参与者取消事务。

参与者在接收到协调者发来的消息后将执行对应的操作,也就是本地 Commit 或者 Rollback。

两阶段提交存在的问题:

资源被同步阻塞:

  • 在执行过程中,所有参与节点都是事务独占状态,当参与者占有公共资源时,那么第三方节点访问公共资源会被阻塞。

协调者可能出现单点故障:

  • 一旦协调者发生故障,参与者会一直阻塞下去。

在 Commit 阶段出现数据不一致:

在第二阶段中,假设协调者发出了事务 Commit 的通知,但是由于网络问题该通知仅被一部分参与者所收到并执行 Commit,其余的参与者没有收到通知,一直处于阻塞状态,那么,这段时间就产生了数据的不一致性。

三阶段提交

三阶段提交协议(3PC)

为了解决二阶段协议中的同步阻塞等问题,三阶段提交协议在协调者和参与者中都引入了超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。

三阶段中的 Three Phase 分别为 CanCommit、PreCommit、DoCommit 阶段。

三阶段提交做了哪些改进:

引入超时机制:

在 2PC 中,只有协调者拥有超时机制,如果在一定时间内没有收到参与者的消息则默认失败,3PC 同时在协调者和参与者中都引入超时机制。

添加预提交阶段:

在 2PC 的准备阶段和提交阶段之间,插入一个准备阶段,使 3PC 拥有 CanCommit、PreCommit、DoCommit 三个阶段,PreCommit 是一个缓冲,保证了在最后提交阶段之前各参与节点的状态是一致的。

三阶段提交协议存在的问题

在阶段三中,如果参与者接收到了 PreCommit 消息后,出现了不能与协调者正常通信的问题,在这种情况下,参与者依然会进行事务的提交,这就出现了数据的不一致性。

MQ消息事务

方案一:依靠MQ的事务消息机制来实现投递消息和参与者⾃身本地事务的⼀致性保障。

消息的可靠发送由发送端 Producer进行保证(消费端无需考虑),可靠发送消息的步骤如下:

  • 发送一个事务消息,RocketMQ将消息状态标记为Prepared,注意此时这条消息消费者是无法消费到的。
  • 执行业务代码逻辑,可能是一个本地数据库事务操作。
  • 确认发送消息,RocketMQ将消息状态标记为可消费,这个时候消费者,才能真正的保证消费到这条数据。

如果确认消息发送失败了怎么办?

  • RocketMQ会定期扫描消息集群中的事务消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认。
  • RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。
  • 这样就保证了消息发送与本地事务同时成功或同时失败。

如果消费失败怎么办?

  • 阿里提供的解决方法是:人工解决。

方案二:并不是所有的MQ都支持事务消息。

  • 也就是消息一旦发送到消息队列中,消费者立马就可以消费到,此时可以使用独立消息服务、或者本地事务表。

刚开始处于prepare状态,业务逻辑处理成功后,确认发送消息,这个时候 独立消息服务 才会真正的把消息发送给消息队列。

消费者消费成功后,ack时,除了对消息队列进行ack,对于独立消息服务也要进行ack,独立消息服务一般是把这条消息删除。

而定时扫描prepare状态的消息,向消息发送端(生产者)确认的工作也由独立消息服务来完成。

最大努力通知

适用于一些最终一致性时间敏感度低的业务,且被动方处理结果 不影响主动方的处理结果。

典型的使用场景:如银行通知、商户通知等。

最大努力通知型的实现方案,一般符合以下特点:

不可靠消息:

  • 业务活动主动方,在完成业务处理之后,向业务活动的被动方发送消息,直到通知N次后不再通知,允许消息丢失(不可靠消息)。

定期校对:

  • 业务活动的被动方,根据定时策略,向业务活动主动方查询(主动方提供查询接口),恢复丢失的业务消息。

举例:一个短信发送平台,背景是公司内部有多个业务都有发送短信的需求,如果每个业务独立实现短信发送功能,存在功能实现上的重复。

有一个短信平台项目,所有的业务方都接入这个短信平台,来实现发送短信的功能。

1、业务方将短信发送请求提交给短信平台

2、短信平台接收到要发送的短信,记录到数据库中,并标记其状态为已接收

3、短信平台调用外部短信发送供应商的接口,发送短信

4、更新短信发送状态为已发送

5、短信发送供应商异步通知短信平台短信发送结果,而通知可能失败,因此最多只会通知N次

6、短信平台接收到短信发送结果后,更新短信发送状态,可能是成功,也可能失败(如手机欠费)

7、如果最多只通知N次,如果都失败了的话,那么短信平台将不知道短信到底有没有成功发送

8、短信发送供应商需要提供一个查询接口,以方便短信平台驱动的去查询,进行定期校对

TCC事务

TCC将事务过程分为Try(尝试)、Confirm(确认)和Cancel(取消)三个阶段,每个阶段由业务代码控制,避免了长事务的问题,从而提高了性能。

Try阶段

  • 尝试执行业务并完成所有业务检查,预留业务资源。

Confirm和Cancel阶段:这两个操作是互斥的,只可以选择其中一个。

  • Confirm操作是确认提交,执行业务操作,不进行其他业务检查,只使用Try阶段预留的业务资源。
  • Cancel操作在业务执行错误需要回滚的情况下执行,释放预留的资源。

Try 阶段失败可以 Cancel,如果 Confirm 和 Cancel 阶段失败了呢?

TCC事务模型中会使用事务日志来记录Try、Confirm和Cancel阶段的操作。

如果在Confirm或Cancel阶段发生错误,系统会进行重试操作。

因此,这两个阶段需要支持幂等性,以确保多次执行的结果与一次执行的结果相同。

如果重试操作失败,就需要人工介入来进行恢复和处理。

TCC的缺点:

TCC对微服务的侵入性较强,需要对业务系统进行改造,每个分支的业务逻辑都需要实现try、confirm和cancel操作,并且confirm和cancel操作必须保证幂等性。

TCC的事务管理器需要记录事务日志,这也会带来一定的性能损耗。

与2PC/XA两阶段提交的区别:

2PC/XA关注数据库层面的强一致性,持有数据库锁。

而TCC关注业务层面的最终一致性,不涉及加锁,并且将相关的处理从数据库转移到业务中,实现跨数据库的事务。

空回滚问题:

在 try 阶段服务 发生了故障,try 阶段在不考虑重试的情况下,全局事务必须要走向结束状态,这样就需要在服务上执行一次 cancel 操作,这样就空跑了一次回滚操作。

解决办法:

  • 第一阶段 Try 方法里会插入一条记录(事务记录表),表示一阶段执行了。
  • Cancel 接口里读取该记录,如果该记录存在,则正常回滚;如果该记录不存在,则是空回滚。

防悬挂控制:

在调用TCC服务的一阶段Try操作时,可能会出现因网络拥堵而导致的超时,此时触发二阶段回滚,调用TCC服务的Cancel操作;

在此之后,拥堵在网络上的一阶段Try数据包被TCC服务收到,出现了二阶段Cancel请求比一阶段Try请求先执行的情况;

解决思路:

  • 如果二阶段执行完成,那一阶段就不能再继续执行。
  • 在执行一阶段事务时判断在该全局事务下,事务记录表中是否已经有二阶段事务记录,如果有则不执行Try。

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
7月前
|
缓存 算法 NoSQL
【分布式详解】一致性算法、全局唯一ID、分布式锁、分布式事务、 分布式缓存、分布式任务、分布式会话
分布式系统通过副本控制协议,使得从系统外部读取系统内部各个副本的数据在一定的约束条件下相同,称之为副本一致性(consistency)。副本一致性是针对分布式系统而言的,不是针对某一个副本而言。强一致性(strong consistency):任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。单调一致性(monotonic consistency):任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值。
666 0
|
7月前
|
消息中间件 Dubbo 应用服务中间件
分布式事物【Hmily实现TCC分布式事务、Hmily实现TCC事务、最终一致性分布式事务解决方案】(七)-全面详解(学习总结---从入门到深化)
分布式事物【Hmily实现TCC分布式事务、Hmily实现TCC事务、最终一致性分布式事务解决方案】(七)-全面详解(学习总结---从入门到深化)
207 0
|
4月前
|
存储 NoSQL Java
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
这篇文章是关于Java面试中的分布式架构问题的笔记,包括分布式架构下的Session共享方案、RPC和RMI的理解、分布式ID生成方案、分布式锁解决方案以及分布式事务解决方案。
一天五道Java面试题----第十一天(分布式架构下,Session共享有什么方案--------->分布式事务解决方案)
|
7月前
|
Dubbo 应用服务中间件 微服务
分布式事物【Hmily实现TCC分布式事务、Hmily实现TCC事务、最终一致性分布式事务解决方案】(七)-全面详解(学习总结---从入门到深化)(上)
分布式事物【Hmily实现TCC分布式事务、Hmily实现TCC事务、最终一致性分布式事务解决方案】(七)-全面详解(学习总结---从入门到深化)
101 1
|
7月前
|
Java 数据库连接 API
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
141 0
|
5月前
|
消息中间件
分布式篇问题之通过本地消息表实现分布式事务的最终一致性问题如何解决
分布式篇问题之通过本地消息表实现分布式事务的最终一致性问题如何解决
219 0
|
7月前
|
开发框架 Java 数据库连接
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)(下)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
114 0
|
7月前
|
数据库 微服务
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)(上)
分布式事物【XA强一致性分布式事务实战、Seata提供XA模式实现分布式事务】(五)-全面详解(学习总结---从入门到深化)
82 0
|
5月前
|
消息中间件 Java 中间件
Java面试题:解释分布式事务的概念,讨论常见的分布式事务解决方案。
Java面试题:解释分布式事务的概念,讨论常见的分布式事务解决方案。
67 0
|
6月前
|
缓存 NoSQL 数据库
分布式系统面试全集通第一篇(dubbo+redis+zookeeper----分布式+CAP+BASE+分布式事务+分布式锁)
分布式系统面试全集通第一篇(dubbo+redis+zookeeper----分布式+CAP+BASE+分布式事务+分布式锁)
111 0
下一篇
DataWorks