分布式系统常见的事务处理机制

CAP 定理

CAP 定理(也称为 Brewer 定理),是由计算机科学家 Eric Brewer 提出的,即在分布式计算机系统不可能同时提供如下所有三个保证:算法

  • 一致性(Consistency):全部节点同一时间看到是相同的数据;
  • 可用性(Availability):不论是否成功,确保每个请求都能接收到响应;
  • 分区容错性(Partition tolerance):系统任意分区后,在网络故障时,仍能操做

显然,为了保障性能和可靠性,咱们将数据复制多份,分布到多个节点上,同时也带来了一个难点,那就是如何保持各个副本数据的一致性。换句话说,咱们选择了 AP ,则必需要牺牲掉 C 了。数据库

可是,在实际的应用场景中,数据的一致性每每也是须要保证的。那么这是否违背了 CAP 定理呢?性能优化

一致性模型

其实,数据的一致性也分几种状况,大体能够分为:网络

  • Weak 弱一致性:当你写入一个新值后,读操做在数据副本上可能读出来,也可能读不出来。好比:某些存储系统,搜索引擎,实时游戏,语音聊天等,这些数据本文对完整性要求不高,数据是否一致关系也不大。
  • Eventually 最终一致性:当你写入一个新值后,并不必定能立刻读出来,但在某个时间窗口以后保证最终能读出来。好比:DNS,电子邮件,消息中间件等系统,大部分分布式系统技术都采用这类模式。
  • Strong 强一致性:新的数据一旦写入,在任意副本任意时刻都能读到新值。好比:文件系统,RDBMS都是强一致性的。

也就是说,在设计分布式系统时,咱们并不必定要求是强一致性的,根据应用场景能够选择弱一致性或者是最终一致性。异步

事务的做用

事务有以下做用:分布式

  • 保证执行结果的正确性
  • 保证数据的一致性
  • ACID

常见的事务处理机制

Master-Slave 复制

Slave 通常是 Master 的备份。在这样的系统中,通常是以下设计的:oop

  • 读写请求都由 Master 负责。
  • 写请求写到 Master 上后,由 Master 同步到 Slave 上。

这种机制的特色是:性能

  • 数据同步一般是异步的
  • 有良好的吞吐量,低延迟 * 在大多数 RDBMS 中支持,好比 MySQL二进制日志
  • 弱/最终一致性

这种机制的缺点是,若是 Master 挂了,Slave 只能提供读服务,而没有写服务。学习

Master-Master 多主复制

指一个系统存在两个或多个Master,每一个Master都提供读写服务。这个机制是Master-Slave的增强版,数据间同步通常是经过Master间的异步完成,因此是最终一致性。 Master-Master的好处是,一台Master挂了,别的Master能够正常作读写服务,他和Master-Slave同样,当数据没有被复制到别的Master上时,数据会丢失。不少数据库都支持Master-Master的Replication的机制。优化

这种机制的特色是:

  • 异步
  • 最终的一致性
  • 多个节点间须要序列化协议

两阶段提交

两阶段提交协议 (Two-phase commit protocol,2PC)的过程涉及到协调者和参与者。协调者能够看作成事务的发起者,同时也是事务的一个参与者。对于一个分布式事务来讲,一个事务是涉及到多个参与者的。具体的两阶段提交的过程以下:

第一阶段(准备阶段)

  • 协调者节点向全部参与者节点询问是否能够执行提交操做(vote),并开始等待各参与者节点的响应。
  • 参与者节点执行询问发起为止的全部事务操做,并将 Undo 信息和 Redo 信息写入日志。(注意:若成功这里其实每一个参与者已经执行了事务操做)
  • 各参与者节点响应协调者节点发起的询问。若是参与者节点的事务操做实际执行成功,则它返回一个“赞成”消息;若是参与者节点的事务操做实际执行失败,则它返回一个“停止”消息。

第二阶段(提交阶段)

若是协调者收到了参与者的失败消息或者超时,直接给每一个参与者发送回滚(Rollback)消息;不然,发送提交(Commit)消息;参与者根据协调者的指令执行提交或者回滚操做,释放全部事务处理过程当中使用的锁资源。(注意:必须在最后阶段释放锁资源)

  • 当协调者节点从全部参与者节点得到的相应消息都为“赞成”时:
    • 协调者节点向全部参与者节点发出“正式提交(commit)”的请求。
    • 参与者节点正式完成操做,并释放在整个事务期间内占用的资源。
    • 参与者节点向协调者节点发送“完成”消息。
  • 若是任一参与者节点在第一阶段返回的响应消息为”停止”,或者 协调者节点在第一阶段的询问超时以前没法获取全部参与者节点的响应消息时:
    • 协调者节点向全部参与者节点发出”回滚操做(rollback)”的请求。
    • 参与者节点利用以前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
    • 参与者节点向协调者节点发送”回滚完成”消息。
    • 协调者节点受到全部参与者节点反馈的”回滚完成”消息后,取消事务。
    • 协调者节点受到全部参与者节点反馈的”完成”消息后,完成事务

无论最后结果如何,第二阶段都会结束当前事务。

二段式提交协议的优缺点:

优势:原理简单,实现方便;

缺点:

  • 同步阻塞问题。执行过程当中,全部参与节点都是事务阻塞型的。
  • 单点故障。因为协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去。尤为在第二阶段,协调者发生故障,那么全部的参与者还都处于锁定事务资源的状态中,而没法继续完成事务操做。
  • 数据不一致。在阶段二中,当协调者向参与者发送 commit 请求以后,发生了局部网络异常或者在发送 commit 请求过程当中协调者发生了故障,这回致使只有一部分参与者接受到了 commit 请求。而在这部分参与者接到 commit 请求以后就会执行 commit 操做。可是其余部分未接到 commit 请求的机器则没法执行事务提交。因而整个分布式系统便出现了数据部一致性的现象。
  • 二阶段没法解决的问题:协调者再发出 commit 消息以后宕机,而惟一接收到这条消息的参与者同时也宕机了。那么即便协调者经过选举协议产生了新的协调者,这条事务的状态也是不肯定的,没人知道事务是否被已经提交。

为了解决两阶段提交协议的种种问题,研究者们在二阶段提交的基础上作了改进,提出了三阶段提交。

三阶段提交

三阶段提交协议(Three-phase commit protocol,3PC),是二阶段提交(2PC)的改进版本。与两阶段提交不一样的是,三阶段提交有两个改动点:

  • 引入超时机制。同时在协调者和参与者中都引入超时机制。
  • 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段以前各参与节点的状态是一致的。

即 3PC 把 2PC 的准备阶段再次一分为二,这样三阶段提交就有 CanCommit、PreCommit、DoCommit 三个阶段。

CanCommit 阶段

CanCommit 阶段其实和 2PC 的准备阶段很像。协调者向参与者发送 commit 请求,参与者若是能够提交就返回 Yes 响应,不然返回 No 响应。

  • 事务询问:协调者向参与者发送 CanCommit 请求。询问是否能够执行事务提交操做。而后开始等待参与者的响应。
  • 响应反馈:参与者接到 CanCommit 请求以后,正常状况下,若是其自身认为能够顺利执行事务,则返回 Yes 响应,并进入预备状态。不然反馈 No

PreCommit 阶段

协调者根据参与者的反应状况来决定是否能够记性事务的 PreCommit 操做。根据响应状况,有如下两种可能。

  • 假如协调者从全部的参与者得到的反馈都是 Yes 响应,那么就会执行事务的预执行。
    • 发送预提交请求:协调者向参与者发送 PreCommit 请求,并进入Prepared 阶段。
    • 事务预提交:参与者接收到 PreCommit 请求后,会执行事务操做,并将undo 和 redo 信息记录到事务日志中。
    • 响应反馈:若是参与者成功的执行了事务操做,则返回 ACK 响应,同时开始等待最终指令。
  • 假若有任何一个参与者向协调者发送了 No 响应,或者等待超时以后,协调者都没有接到参与者的响应,那么就执行事务的中断。
  • 发送中断请求:协调者向全部参与者发送 abort 请求。
  • 中断事务:参与者收到来自协调者的 abort 请求以后(或超时以后,仍未收到协调者的请求),执行事务的中断。

doCommit 阶段

该阶段进行真正的事务提交,也能够分为如下两种状况。

  • 执行提交
    • 发送提交请求:协调接收到参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态。并向全部参与者发送 doCommit 请求。
    • 事务提交:参与者接收到 doCommit 请求以后,执行正式的事务提交。并在完成事务提交以后释放全部事务资源。
    • 响应反馈:事务提交完以后,向协调者发送 ACK 响应。
    • 完成事务:协调者接收到全部参与者的 ACK 响应以后,完成事务。
  • 中断事务:协调者没有接收到参与者发送的 ACK 响应(多是接受者发送的不是 ACK 响应,也可能响应超时),那么就会执行中断事务。
    • 发送中断请求:协调者向全部参与者发送 abort 请求
    • 事务回滚:参与者接收到 abort 请求以后,利用其在阶段二记录的undo 信息来执行事务的回滚操做,并在完成回滚以后释放全部的事务资源。
    • 反馈结果:参与者完成事务回滚以后,向协调者发送 ACK 消息
    • 中断事务:协调者接收到参与者反馈的 ACK 消息以后,执行事务的中断。

在 doCommit 阶段,若是参与者没法及时接收到来自协调者的 doCommit 或者 rebort 请求时,会在等待超时以后,会继续进行事务的提交。即当进入第三阶段时,因为网络超时等缘由,虽然参与者没有收 到 commit 或者 abort 响应,事务仍然会提交。

三阶段提交不会一直持有事务资源并处于阻塞状态。可是这种机制也会致使数据一致性问题,由于,因为网络缘由,协调者发送的 abort 响应没有及时被参与者接收到,那么参与者在等待超时以后执行了 commit 操做,这样就和其余接到 abort 命令并执行回滚的参与者之间存在数据不一致的状况。

Paxos 算法

Paxos 算法是 Leslie Lamport 于1990年提出的一种基于消息传递且具备高度容错特性的一致性算法。Paxos 算法目前在 Google 的 Chubby、MegaStore、Spanner 等系统中获得了应用,Hadoop 中的 ZooKeeper 也使用了 Paxos 算法。

在 Paxos 算法中,分为4种角色:

  • Proposer :提议者
  • Acceptor:决策者
  • Client:产生议题者
  • Learner:最终决策学习者

算法能够分为两个阶段来执行:

阶段1

  • Proposer 选择一个议案编号 n,向 acceptor 的多数派发送编号也为 n 的 prepare 请求。
  • Acceptor:若是接收到的 prepare 请求的编号 n 大于它已经回应的任何prepare 请求,它就回应已经批准的编号最高的议案(若是有的话),并承诺再也不回应任何编号小于 n 的议案;

阶段2

  • Proposer:若是收到了多数 acceptor 对 prepare 请求(编号为 n)的回应,它就向这些 acceptor 发送议案{n, v}的 accept 请求,其中 v 是全部回应中编号最高的议案的决议,或者是 proposer 选择的值,若是回应说尚未议案。
  • Acceptor:若是收到了议案{n, v}的 accept 请求,它就批准该议案,除非它已经回应了一个编号大于 n 的议案。
  • Proposer 能够提出多个议案,只要它遵循上面的算法。它能够在任什么时候刻放弃一个议案。(这不会破坏正确性,即便在议案被放弃后,议案的请求或者回应消息才到达目标)若是其它的 proposer 已经开始提出更高编号的议案,那么最好能放弃当前的议案。所以,若是 acceptor 忽略一个 prepare 或者 accept 请求(由于已经收到了更高编号的 prepare 请求),它应该告知 proposer 放弃议案。这是一个性能优化,而不影响正确性。
相关文章
相关标签/搜索