【进阶之路】分布式系统中的柔性事务解决方案
导言
大家好,我是南橘,从接触java到现在也有差不多两年时间了,两年时间,从一名连java有几种数据结构都不懂超级小白,到现在懂了一点点的进阶小白,学到了不少的东西。知识越分享越值钱,我这段时间总结(包括从别的大佬那边学习,引用)了一些平常学习和面试中的重点(自我认为),希望给大家带来一些帮助
随着互联网应用的量级越来越大,单体应用架构越来越无法满足需求,所以,分布式集群架构就应运而生了。这几章,我们就来谈谈如何在分布式系统中实现事务的一致性。
1、CAP
CAP理论:一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项。
-
一致性:同样数据在分布式系统的各个节点上都是一致的
-
可用性:所有在分布式系统活跃的节点都能够处理操作且能响应查询
-
分区容忍性 :如果出现了网络故障、一部分节点无法通信,但是系统仍能够工作
从客户端角度,多进程并发访问时,还有不同的一致性的划分:
1.强一致性:对于关系型数据库,要求更新过的数据能被后续的访问都能看到.
2.弱一致性:能容忍后续的部分或者全部访问不到。
3.最终一致性:经过一段时间后要求能访问到更新后的数据。
但是,在CAP理论中虽然说是三者只能满足两者,但实际情况是,在分布式场景中分区一定存在,即必须有分区容忍性对应的策略,之后才能在一致性和可用性间二者之间选择.所以对主流架构来说不是三选二,而是二选一,而我们今天要讲的柔性柔性事务,就是基于BASE理论,放弃实现强一致性,首先实现可用性和分区容忍性,进而实现最终一致性。
base是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写。base是对cap中一致性和可用性的权衡的结果。是根据cao理论演变而来,核心思想是即使无法做到强一致性,但是每个应用根据自身的业务特点,采用适当的方式来使系统达到最终与执行。
2、两阶段提交(2PC)
阶段一:提交事务请求
-
1、协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。 -
2、各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中。 -
3、各参与者向协调者反馈事务询问的响应
阶段二:执行事务
在阶段二中,协调者会根据各参与者的反馈情况来决定最终是否可以进行事务提交操作,正常情况下,包含以下两种可能:
-
1、假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务提交。 -
2、假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。
步骤 | 执行事务提交 | 中断事务 |
---|---|---|
1 | 协调者向所有参与者节点发出Commit请求(发送提交请求) | 协调者向所有参与者节点发出Rollback请求(事务提交) |
2 | 参与者接收到Commit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源(反馈事务提交结果)。 | 参与者收到Rollback请求之后,会利用其在阶段一种记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源(完成事务) |
3 | 参与者在完成事务提交之后,协调者发送Ack消息(发送回滚请求) | 参与者在完成事务回滚之后,向协调者发送Ack消息(事务回滚) |
4 | 协调者接收到所有参与者反馈的Ack消息后,完成事务(反馈事务回滚结果) | 协调者接收所有参与者反馈的Ack消息后,完成事务中断(中断事务) |
二阶段提交协议的优点:原理简单,实现方便。
二阶段提交协议的缺点:同步阻塞、单点问题、数据不一致
-
同步阻塞:二阶段提交协议存在的最明显也是最大的一个问题就是同步阻塞,这会极大地限制分布式系统的性能。在二阶段提交的过程中个,所有参与该事务操作的逻辑都处于阻塞状态,也就是说,各个参与者在等待其他参与者响应的过程中,将无法进行任何操作。 -
单点问题:一旦协调者出现问题,那么整个二阶段提交流程将无法运转,更为严重的是,如果协调者在阶段二中出现问题的话,那么其他参与者将会一直处于锁定事务资源的状态中,而无法继续完成事务操作。 -
数据不一致:在二阶段提交协议的阶段二,即执行事务提交请求的时候,当协调者向所有的参与者发送Commit请求之后,发生了局部网络异常或者协调者尚未发送完Commit请求之前自身发生了崩溃,导致最终只有部分参与者收到了Commit请求。于是,这部分收到Commit请求的参与者就会进行事务的提交,而其他没有收到Commit请求的参与者则无法进行事务提交,于是整个分布式系统边出现了数据不一致现象。
3、三阶段提交(3PC)
3PC 是在 2PC 阶段的改进版,将二阶段提交协议的 "提交事务请求" 过程一分为二。形成由CanCommit、PreCommit、doCommit三个阶段组成的事务处理协议。
阶段一:CanCommit
-
事务询问:协调者向所有的参与者发送一个包含事务内容的 canCommit 请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。 -
各参与者向协调者反馈事务询问的响应:参与者在接收到来自协调者的 canCommit 请求后,正常情况下,如果其自身认为可以顺利执行事务,则反馈 Yes 响应,并进入预备状态,否则反馈 No 响应。
阶段二:PreCommit(事务预提交阶段)
-
执行事务预提交:假设协调者从所有的参与者获得反馈都是 Yes 响应,那么就会执行事务预提交。 -
中断事务:若是任何一个参与者向协调者反馈了 No 响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,则中断事务)
步骤 | 执行事务预提交 | 中断事务 |
---|---|---|
1 | 协调者向所有参与者节点发出 preCommit 的请求,并进入 Prepared 阶段(发送预提交请求) | 协调者向所有参与者节点发出 abort 请求(发送中断请求) |
2 | 参与者接收到 preCommit 请求后,会执行事务操作,并将 Undo 和 Redo 信息记录到事务日志中(事务预提交) | 无论是收到来自协调者的 abort 请求,或者是在等待协调者请求过程中出现超时,参与者都会中断事务(中断事务) |
3 | 各参与者向协调者反馈事务执行的响应:如果参与者成功执行了事务操作,那么就会反馈给协调者 Ack 响应,同时等待最终的指令:提交 (commit) 或中止(abort) |
阶段三:doCommit(事务提交)
步骤 | 执行事务 | 中断事务 |
---|---|---|
1 | 假设协调者处于正常工作状态,并且接收到来自所有参与者的 Ack 响应,则它将从 “预提交” 状态转换到 “提交” 状态,向所有参与者发送 doCommit 请求(发送提交请求) | 假设协调者处于正常工作状态,并且任意一个参与者向协调者反馈了 No 响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应。则中断事务(发送中断请求) |
2 | 参与者接收到 doCommit 请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源(事务提交)。 | 参与者接收到 abort 请求后,会利用其在阶段二中记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源(事务回滚) |
3 | 参与者在完成事务提交之后,向协调者发送 Ack 消息(反馈事务提交结果) | 协调者接收到所有参与者反馈的 Ack 消息后,中断事务(中断事务) |
4 | 协调者接收到所有参与者反馈的 Ack 消息后,完成事务(完成事务) |
进入阶段三,可能出现一些问题:
-
1、协调者出现问题 -
2、协调者和参与者之间的网络出现故障。
无论出现那种情况,参与者都无法及时接收到来自协调者的 doCommit 和 abort 请求,对于该问题,参与者都会在等待超时之后,继续进行事务提交。(会导致数据不一致性)
3PC的优点:相较于二阶段提交协议,降低了参与者的阻塞范围,并且能够在出现单点故障后继续达成一致。
3PC的缺点:三阶段在去除阻塞的同时也引入了新问题,当参与者接收到 preCommit 消息后,如果网络出现分区,此时协调者所在节点和参与者无法进行正常的网络通信,在这种情况下,该参与者依然会进行事务的提交,这必然出现数据的不一致性。
4、补偿事务TCC(Try-Confirm-Cancel)
1、Try阶段
协调者调用所有参与者的try操作,并在活动管理器中登记所有参与者。当所有参与者服务的 try 操作都调用成功或者某个参与者服务的 try 操作失败,进入第二阶段。
这个阶段中,需要完成所有业务检查(一致性)、并且预留必须业务资源(准隔离性)。
2、Confirm阶段
活动管理器根据第一阶段的执行结果来执行 confirm 或 cancel 操作。
这个阶段中已经不需要做业务检查,因为Try阶段已经完成了业务检查,同时只使用Try阶段预留的业务资源来执行业务。
如果第一阶段所有 try 操作都成功,则活动管理器调用所有参与者的 confirm操作。
3、Cancel阶段
如果第一阶段有 try 操作失败,则调用所有参与者服务的 cancel 操作,同时释放Try阶段预留的业务资源
TCC事务的优点如下:
-
1、解决了跨应用业务操作的原子性问题,在诸如组合支付、账务拆分场景非常实用。 -
2、TCC实际上把数据库层的二阶段提交上提到了应用层来实现,对于数据库来说是一阶段提交,规避了数据库层的2PC性能低下问题。
第二阶段 confirm 或 cancel 操作本身也是满足最终一致性的过程,在调用 confirm 或 cancel 的时候也可能因为某种原因(比如网络)导致调用失败,所以需要活动管理支持重试的能力,同时这也就要求 confirm 和 cancel 操作具有幂等性。
如果有些业务由于瞬时的网络故障或调用超时等问题,很多服务是依赖于外部系统的可用性情况,在一些重要的业务场景下还需要周期性的对账来保证真实的一致性。比如支付系统和银行之间每天日终是都会有对账过程。
结语
简单介绍了一下柔性分布式事务
同时需要思维导图的话,可以联系我,毕竟知识越分享越香!