2PC

2PC,是Two-Phase Commit的缩写,即二阶段提交,是计算机网络尤其是在数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务处理过程中能够保持原子性和一致性而设计的一种算法。通常,二阶段提交协议也被认为是一种一致性协议,用来保证分布式系统数据的一致性。

目前,绝大部分的关系型数据库都是采用二阶段提交协议来完成分布式事务处理的,利用该协议能够非常方便地完成所有分布式事务参与者的协调,统一决定事务的提交或回滚,从而能够有效地保证分布式数据一致性,因此二阶段提交协议被广泛地应用在许多分布式系统中。

协议说明

顾名思义,二阶段提交协议是将事务的提交过程分成了两个阶段来进行处理,其执行流程如下。

阶段一:提交事务请求

1) 事务询问。

协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。

2) 执行事务。

各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中。

3) 各参与者向协调者反馈事务询问的响应。

如果参与者成功执行了事务操作,那么就反馈给协调者 Yes 响应,表示事务可以执行;如果参与者没有成功执行事务,那么就反馈给协调者No响应,表示事务不可以执行。

由于上面讲述的内容在形式上近似是协调者组织各参与者对一次事务操作的投票表态过程,因此二阶段提交协议的阶段一也被称为“投票阶段”,即各参与者投票表明是否要继续执行接下去的事务提交操作。

阶段二:执行事务提交

在阶段二中,协调者会根据各参与者的反馈情况来决定最终是否可

以进行事务提交操作,正常情况下,包含以下两种可能。

执行事务提交

假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务提交。

1) 发送提交请求。

协调者向所有参与者节点发出Commit请求。

2) 事务提交。

参与者接收到 Commit 请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。

3) 反馈事务提交结果。

参与者在完成事务提交之后,向协调者发送Ack消息。

4) 完成事务。

协调者接收到所有参与者反馈的Ack消息后,完成事务。

中断事务

假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

1) 发送回滚请求。

协调者向所有参与者节点发出Rollback请求。

2) 事务回滚。

参与者接收到 Rollback 请求后,会利用其在阶段一中记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。

3) 反馈事务回滚结果。

参与者在完成事务回滚之后,向协调者发送Ack消息。

4) 中断事务。

协调者接收到所有参与者反馈的Ack消息后,完成事务中断。

以上就是二阶段提交过程中,前后两个阶段分别进行的处理逻辑。

简单地讲,二阶段提交将一个事务的处理过程分为了投票和执行两个阶段,其核心是对每个事务都采用先尝试后提交的处理方式,因此也可以将二阶段提交看作一个强一致性的算法。

优缺点

二阶段提交协议的优点:原理简单,实现方便。

二阶段提交协议的缺点:同步阻塞、单点问题、脑裂、太过保守。

同步阻塞

二阶段提交协议存在的最明显也是最大的一个问题就是同步阻塞,这会极大地限制分布式系统的性能。

在二阶段提交的执行过程中,所有参与该事务操作的逻辑都处于阻塞状态,也就是说,各个参与者在等待其他参与者响应的过程中,将无法进行其他任何操作。

单点问题

在上面的讲解过程中,相信读者可以看出,协调者的角色在整个二阶段提交协议中起到了非常重要的作用。

一旦协调者出现问题,那么整个二阶段提交流程将无法运转,更为严重的是,如果协调者是在阶段二中出现问题的话,那么其他参与者将会一直处于锁定事务资源的状态中,而无法继续完成事务操作。

数据不一致

在二阶段提交协议的阶段二,即执行事务提交的时候,当协调者向所有的参与者发送Commit请求之后,发生了局部网络异常或者是协调者在尚未发送完Commit请求之前自身发生了崩溃,导致最终只有部分参与者收到了Commit请求。

于是,这部分收到了Commit请求的参与者就会进行事务的提交,而其他没有收到Commit请求的参与者则无法进行事务提交,于是整个分布式系统便出现了数据不一致性现象。

太过保守

如果在协调者指示参与者进行事务提交询问的过程中,参与者出现故障而导致协调者始终无法获取到所有参与者的响应信息的话,这时协调者只能依靠其自身的超时机制来判断是否需要中断事务,这样的策略显得比较保守。

换句话说,二阶段提交协议没有设计较为完善的容错机制,任意一个节点的失败都会导致整个事务的失败。


2PC

SQL 分布式事务两阶段提交协议(2PC)是一种原子承诺协议(ACP)。它是一种分布式算法,协调参与分布式原子事务的所有进程,以决定是否提交或回滚事务(这是一种专门的共识协议类型)。

概念

在事务处理、数据库和计算机网络中,两阶段提交协议(2PC)是一种原子承诺协议(ACP)。

它是一种分布式算法,协调参与分布式原子事务的所有进程,以决定是否提交或回滚事务(这是一种专门的共识协议类型)。即使在许多临时系统故障(包括进程、网络节点、通信等故障)中,协议也达到了它的目的,因此得到了广泛的应用。

然而,并不能适应所有可能的故障配置,在极少数情况下,需要用户(例如系统管理员)的干预来补救结果。

为了适应故障恢复(在大多数情况下是自动的),协议的参与者使用协议状态的日志记录。日志记录通常生成速度较慢,但在失败后仍能正常运行,协议的恢复过程使用日志记录。存在许多协议变体,主要在日志记录策略和恢复机制上有所不同。由于协议要考虑和支持许多可能的故障场景,虽然恢复过程通常不经常使用,但它构成了协议的很大一部分。

协议

在任何单个分布式事务的“正常执行”中(例如,当没有发生故障时(这通常是最常见的情况),协议包括两个阶段:

投票阶段

commit-request阶段(或投票阶段),一个协调过程试图准备所有事务的参与过程(名叫参与者、群组或工人)采取必要的步骤提交或中止事务和投票,要么“是的”:提交(如果正确地执行事务参与者的本地部分已经结束),或“不”:中止(如果一个问题已经被发现与当地的部分)

提交阶段

提交阶段,在此阶段中,协调器根据队列的投票决定是提交(仅当所有队列都投了“是”)还是中止事务(否则),并将结果通知所有队列。

队列接着使用本地事务资源(也称为可恢复资源)执行所需的操作(提交或中止);例如,数据库数据)和它们在事务的其他输出中的各自部分(如果适用的话)。

缺点

两阶段提交中的第二阶段, 协调者需要等待所有参与者发出yes请求, 或者一个参与者发出no请求后, 才能执行提交或者中断操作. 这会造成长时间同时锁住多个资源, 造成性能瓶颈, 如果参与者有一个耗时长的操作, 性能损耗会更明显。

实现复杂, 不利于系统的扩展。

理解二阶段提交协议及故障恢复

假设

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

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

  2. 所有节点都采用WAL,日志写入后就被保存在可靠的存储设备上

  3. 所有节点上的本地事务即使机器crash也可从WAL日志上恢复

回滚流程

2pc-rollback

提交流程

2pc-commit

准备阶段

  1. 协调者记录事务开始日志

  2. 协调者向所有参与者发送prepare消息,询问是否可以执行事务提交,并等待参与者响应

  3. 参与者收到prepare消息后,根据自身情况,进行事务预处理,执行询问发起为止的所有事务操作

  4. 如果能够提交该事务,将undo信息和redo信息写入日志,进入预提交状态

  5. 如果不能提交该事务,撤销所做的变更,并记录日志

  6. 参与者响应协调者发起的询问。如果事务预处理成功返回commit,否者返回abort。

准备阶段只保留了最后一步耗时短暂的正式提交操作给第二阶段执行。

提交阶段

协调者等待所有参与者准备阶段的反馈

  • 如果收到某个参与者发来的abort消息或者迟迟未收到某个参与者发来的消息

标识该事务不能提交,协调者记录abort日志

向所有参与者发送abort消息,让所有参与者撤销准备阶段的预处理

  • 如果协调者收到所有参与者发来的commit消息

标识着该事务可以提交,协调者记录commit日志

向所有参与者发送commit消息,让所有参与者提交事务

参与者等待协调者的指令

  • 如果参与者收到的是abort消息

中止事务,利用之前写入的undo日志执行回滚,释放准备阶段锁定的资源

记录abort日志

向协调者发送rollback done消息

  • 如果参与者收到的是commit消息

提交事务,释放准备阶段锁定的资源

记录commit日志

向协调者发送commit done消息

协调者等待所有参与者提交阶段的反馈

  • 如果协调者收到所有参与者发来的commit done消息

完成事务,记录事务完成日志

  • 如果协调者收到所有参与者发来的rollback done消息

取消事务,记录事务取消日志

为什么先写日志

如果没有日志,宕机后重启将无法知道事务状态,无法进行故障恢复。

宕机异常处理

正常情况下,两阶段提交机制都能较好的运行,当在事务进行过程中,参与者或协调者宕机了怎么办?

阶段一

阶段一,协调者记录全局事务开始日志前宕机 协调者重启后,所有参与者均未开始分支事务,不做任何处理

阶段一,协调者记录全局事务开始日志后,发出prepare消息后宕机 协调者重启后,可能部分参与者已经完成一阶段准备,所以需要进行回滚

阶段一,协调者收到参与者响应后,记录准备完成日志前宕机 协调者重启后,因为没有记录准备完成日志,所以需要进行回滚

阶段一,协调者收到参与者响应后,记录准备完成日志后宕机 协调者重启后,已经记录准备完成日志,可以根据协商结果进行提交或回滚

阶段二

阶段二,协调者发出commit消息后宕机 协调者重启后,已经记录准备完成日志,可以根据协商结果进行提交或回滚

阶段二,协调者收到参与者响应后,记录事务完成日志前宕机 协调者重启后,已经记录准备完成日志,可以根据协商结果进行提交或回滚

阶段二,协调者收到参与者响应后,记录事务完成日志后宕机 协调者重启后,发现事务已经完成,不做任何处理

阶段一,某个参与者宕机(未完成prepare) 参与者重启后,需要进行回滚(分布式事务的状态是abort)

阶段二,某个参与者宕机(已完成prepare) 参与者重启后,参与者如果没有询问其他参与者或协调者事务是否提交的能力,恢复后事务处于悬挂状态,等待协调者指令(分布式事务的决策可能是提交,可能是回滚)

超时异常处理

阶段一,协调者发出prepare消息,但没有收到所有参与者的响应 协调者给所有参与者发送回滚指令

阶段二,协调者发出commit消息,但没有收到所有参与者的响应 协调者可异步不断对超时节点尝试提交

二阶段提交的缺点

同步阻塞

事务执行过程中,所有参与节点都是事务阻塞的。当参与者占有资源时,其他访问相关资源的进程也将处于阻塞状态。

参与者对锁资源的释放必须等到事务结束,所以与一阶段提交相比,执行同样的事务,二阶段会耗费更多时间。

事务执行时间的延长意味着锁资源发生冲突的概率增加,当事务并发量达到一定数量时,会出现大量事务积压甚至出现死锁,系统性能会严重下滑。

单点故障

一旦协调者发生故障,参与者会一直阻塞。参与者完成准备阶段后,协调者发生故障,所有的参与者都将处于锁定事务资源的状态中(事务悬挂状态),无法继续完成事务操作。

数据不一致

在提交阶段中,当协调者向参与者发送commit消息后,发生了局部网络异常或者在发送commit消息过程中协调者发生了故障, 这会导致只有一部分参与者接受到了commit消息。而这些参与者收到commit消息后就会执行commit操作。 但是其他未收到commit消息的参与者无法执行commit。 于是整个分布式系统出现了数据不一致性。

spring 实现

由于系统涉及到多个数据源,所以想用两阶段提交事务(分布式事务)来保证数据的一致性,完整性。

TODO…

参考资料

  • 2pc

https://en.wikipedia.org/wiki/Two-phase_commit_protocol

https://www.kancloud.cn/sql-jdxia/mysql/513205

https://www.linuxidc.com/Linux/2015-11/124942.htm

https://zhuanlan.zhihu.com/p/38197629

http://www.itboth.com/d/f2qaya/delete-sql-insert-ssh-mysql

http://www.voidcn.com/article/p-ngewxhzc-bhd.html

https://jin-yang.github.io/post/mysql-group-commit.html

  • spring

http://victor-jan.iteye.com/blog/671310