分布式事务:两阶段提交与三阶段提交

在分布式系统中著有 CAP 理论,该理论由加州大学伯克利分校的 Eric Brewer 教授提出,阐述了在一个分布式系统中不可能同时知足 一致性(Consistency)可用性(Availability) ,以及 分区容错性(Partition tolerance)数据库

  • C:一致性

在分布式系统中数据每每存在多个副本,一致性描述的是这些副本中的数据在内容和组织上的一致。服务器

  • A:可用性

可用性描述了系统对用户的服务能力,所谓可用是指在用户容忍的时间范围内返回用户指望的结果。网络

  • P:分区容错性

分布式系统一般由多个节点构成,因为网络是不可靠的,因此存在分布式集群中的节点由于网络通讯故障致使被孤立成一个个小集群的可能性,即网络分区,分区容错性要求在出现网络分区时系统仍然可以对外提供一致性的可用服务。分布式

对于一个分布式系统而言,咱们要始终假设网络是不可靠的,所以分区容错性是对一个分布式系统最基本的要求,咱们的切入点更多的是尝试在可用性和一致性之间寻找一个平衡点,但这也并不是要求咱们在系统设计时一直创建在网络出现分区的前提之上,而后对一致性和可用性在选择时非此即彼。性能

Eric Brewer 在 2012 年就曾指出 CAP 理论证实不能同时知足一致性、可用性,以及分区容错性的观点在实际系统设计指导上存在必定的误导性 。传统对于 CAP 理论的理解认为在设计分布式系统时必须知足 P,而后在 C 和 A 之间进行取舍,这是片面的,实际中网络出现分区的可能性仍是比较小的,尤为是目前网络环境正在变得愈来愈好,甚至许多系统都拥有专线的支撑,因此在网络未出现分区时,仍是应该兼顾 A 和 C;另外就是对于一致性、可用性,以及分区容错性三者在度量上也应该有一个评定范围,最简单的以可用性来讲,当有多少占比请求出现响应超时才能够被认为是不知足可用性,而不是一出现超时就认为是不可用的;最后咱们须要考虑的一点就是分布式系统通常都是一个比较大且复杂的系统,咱们应该从更小的粒度上对各个子系统进行评估和设计,而不是简单的从总体上武断决策。spa

让分布式集群始终对外提供可用的一致性服务一直是富有挑战和趣味的一项任务。暂且抛开可用性,拿一致性来讲,对于关系型数据库咱们一般利用事务来保证数据的强一致性,当咱们的数据量愈来愈大,大到单库已经没法承担时,咱们不得不采起分库分表的策略对数据库实现水平拆分,构建分布式数据库集群,这样能够将一个数据库的压力分摊到多个数据库,极大的提高了数据库的存储和响应能力,可是拆分以后也为咱们使用数据库带来了许多的限制,好比主键的全局惟1、联表查询、数据聚合等等,另一个至关棘手的问题就是数据库的事务由原先的单库事务变成了如今的分布式事务。设计

分布式事务的实现并非无解的,好比下文要展开的两阶段提交(2PC:Two-Phrase Commit)和三阶段提交(3PC:Three-Phrase Commit)都给咱们提供了思路,可是如何保证数据的强一致性,并对外提供高可用的服务仍是至关棘手的,所以不少分布式系统对于数据强一致性都敬而远之。日志

两阶段提交协议(2PC:Two-Phrase Commit)

两阶段提交协议的目标在于在分布式系统中保证数据的一致性,许多分布式系统采用该协议提供对分布式事务的支持。顾名思义,该协议将一个分布式的事务过程拆分红两个阶段: 投票阶段事务提交阶段 。为了让整个数据库集群可以正常的运行,该协议指定了一个“协调者”单点,用于协调整个数据库集群的运行,为了简化描述,咱们将数据库里面的各个节点称为“参与者”,三阶段提交协议中一样包含“协调者”和“参与者”这两个定义。blog

第一阶段:投票阶段

该阶段的主要目的在于打探数据库集群中的各个参与者是否可以正常的执行事务,具体步骤以下:事务

  1. 协调者向全部的参与者发送事务执行请求,并等待参与者反馈事务执行结果。
  2. 事务参与者收到请求以后,执行事务但不提交,并记录事务日志。
  3. 参与者将本身事务执行状况反馈给协调者,同时阻塞等待协调者的后续指令。

第二阶段:事务提交阶段

在通过第一阶段协调者的询盘以后,各个参与者会回复本身事务的执行状况,这时候存在三种可能性:

  1. 全部的参与者都回复可以正常执行事务
  2. 一个或多个参与者回复事务执行失败
  3. 协调者等待超时

对于第一种状况,协调者将向全部的参与者发出提交事务的通知,具体步骤以下:

  1. 协调者向各个参与者发送 commit 通知,请求提交事务。
  2. 参与者收到事务提交通知以后,执行 commit 操做,而后释放占有的资源。
  3. 参与者向协调者返回事务 commit 结果信息。

2pc-success

对于第2、三种状况,协调者均认为参与者没法成功执行事务,为了整个集群数据的一致性,因此要向各个参与者发送事务回滚通知,具体步骤以下:

  1. 协调者向各个参与者发送事务 rollback 通知,请求回滚事务。
  2. 参与者收到事务回滚通知以后,执行 rollback 操做,而后释放占有的资源。
  3. 参与者向协调者返回事务 rollback 结果信息。

2pc-failed

两阶段提交协议解决的是分布式数据库数据强一致性问题,实际应用中更多的是用来解决事务操做的原子性,下图描绘了协调者与参与者的状态转换。站在协调者的角度,在发起投票以后就进入了 WAIT 状态,等待全部参与者回复各自事务执行状态,并在收到全部参与者的回复后决策下一步是发送 commit 或 rollback 信息。站在参与者的角度,当回复完协调者的投票请求以后便进入 READY 状态(可以正常执行事务),接下去就是等待协调者最终的决策通知,一旦收到通知即可依据决策执行 commit 或 rollback 操做。

2pc-state

两阶段提交协议原理简单、易于实现,可是缺点也是显而易见的,主要缺点以下:

  • 单点问题

协调者在整个两阶段提交过程当中扮演着举足轻重的做用,一旦协调者所在服务器宕机,就会影响整个数据库集群的正常运行,好比在第二阶段中,若是协调者由于故障不能正常发送事务提交或回滚通知,那么参与者们将一直处于阻塞状态,整个数据库集群将没法提供服务。

  • 同步阻塞

两阶段提交执行过程当中,全部的参与者都须要遵从协调者的统一调度,期间处于阻塞状态而不能从事其余操做,这样效率极其低下。

  • 数据不一致性

两阶段提交协议虽然是分布式数据强一致性所设计,但仍然存在数据不一致性的可能性,好比在第二阶段中,假设协调者发出了事务 commit 通知,可是由于网络问题该通知仅被一部分参与者所收到并执行了commit 操做,其他的参与者则由于没有收到通知一直处于阻塞状态,这时候就产生了数据的不一致性。

针对上述问题能够引入 超时机制互询机制 在很大程度上予以解决。对于协调者来讲若是在指定时间内没有收到全部参与者的应答,则能够自动退出 WAIT 状态,并向全部参与者发送 rollback 通知。对于参与者来讲若是位于 READY 状态,可是在指定时间内没有收到协调者的第二阶段通知,则不能武断地执行 rollback 操做,由于协调者可能发送的是 commit 通知,这个时候执行 rollback 就会致使数据不一致。此时,咱们能够介入互询机制,让参与者 A 去询问其余参与者 B 的执行状况,若是 B 执行了 rollback 或 commit 操做,则 A 能够大胆的与 B 执行相同的操做;若是 B 此时尚未到达 READY 状态,则能够推断出协调者发出的确定是 rollback 通知;若是 B 一样位于 READY 状态,则 A 能够继续询问另外的参与者,只有当全部的参与者都位于 READY 状态时,此时两阶段提交协议没法处理,将陷入长时间的阻塞状态。

三阶段提交协议(3PC:Three-Phrase Commit)

针对两阶段提交存在的问题,三阶段提交协议经过引入一个 “预询盘” 阶段,以及超时策略来减小整个集群的阻塞时间,提高系统性能。三阶段提交的三个阶段分别为:can_commit,pre_commit,do_commit。

第一阶段:can_commit

该阶段协调者会去询问各个参与者是否可以正常执行事务,参与者根据自身状况回复一个预估值,相对于真正的执行事务,这个过程是轻量的,具体步骤以下:

  1. 协调者向各个参与者发送事务询问通知,询问是否能够执行事务操做,并等待回复。
  2. 各个参与者依据自身情况回复一个预估值,若是预估本身可以正常执行事务就返回肯定信息,并进入预备状态,不然返回否认信息。

第二阶段:pre_commit

本阶段协调者会根据第一阶段的询盘结果采起相应操做,询盘结果主要有三种:

  1. 全部的参与者都返回肯定信息
  2. 一个或多个参与者返回否认信息
  3. 协调者等待超时

针对第一种状况,协调者会向全部参与者发送事务执行请求,具体步骤以下:

  1. 协调者向全部的事务参与者发送事务执行通知。
  2. 参与者收到通知后,执行事务但不提交。
  3. 参与者将事务执行状况返回给客户端。

在上述步骤中,若是参与者等待超时,则会中断事务。 针对第2、三种状况,协调者认为事务没法正常执行,因而向各个参与者发出 abort 通知,请求退出预备状态,具体步骤以下:

  1. 协调者向全部事务参与者发送 abort 通知。
  2. 参与者收到通知后中断事务。

3pc-fail-1

第三阶段:do_commit

若是第二阶段事务未中断,那么本阶段协调者将会依据事务执行返回的结果来决定提交或回滚事务,分为三种状况:

  1. 全部的参与者都能正常执行事务
  2. 一个或多个参与者执行事务失败
  3. 协调者等待超时

针对第一种状况,协调者向各个参与者发起事务提交请求,具体步骤以下:

  1. 协调者向全部参与者发送事务 commit 通知。
  2. 全部参与者在收到通知以后执行 commit 操做,并释放占有的资源。
  3. 参与者向协调者反馈事务提交结果。

3pc-success

针对第2、三种状况,协调者认为事务没法成功执行,因而向各个参与者发送事务回滚请求,具体步骤以下:

  1. 协调者向全部参与者发送事务 rollback 通知。
  2. 全部参与者在收到通知以后执行 rollback 操做,并释放占有的资源。
  3. 参与者向协调者反馈事务回滚结果。

3pc-fail-2

在本阶段若是由于协调者或网络问题,致使参与者迟迟不能收到来自协调者的 commit 或 rollback 请求,那么参与者将不会如两阶段提交中那样陷入阻塞,而是等待超时后继续 commit,相对于两阶段提交虽然下降了同步阻塞,但仍然没法彻底避免数据的不一致。

3pc-state

两阶段提交协议中所存在的长时间阻塞状态发生的概率仍是很是低的,因此虽然三阶段提交协议相对于两阶段提交协议对于数据强一致性更有保障,可是由于效率问题,两阶段提交协议在实际系统中反而更加受宠。在分布式数据库中,若是指望达到数据的强一致性,则服务基本没有可用性可言,这也是为何许多分布式数据库提供了跨库事务,但也只是个摆设的缘由,在实际应用中咱们追求的更可能是数据的弱一致性。


鉴于做者水平有限,文中难免有错误之处,欢迎批评指正
我的博客:www.zhenchao.org

相关文章
相关标签/搜索