分布式相关理论和分布式事务

原文连接: blog.wangriyu.wang/2018/06-Dis…html

分布式系统理论

CAP 定理

CAP 定理指出对于一个分布式系统来讲,不可能同时知足如下三点:mysql

  • 一致性 (Consistence): 等同于全部节点访问同一份最新的数据副本 (强一致性)
  • 可用性 (Availability): 每次请求都能获取到非错的响应,可是不保证获取的数据为最新数据
  • 分区容错性 (Partition tolerance): 系统若是不能在时限内达成数据一致性,就意味着发生了分区的状况,此时必须在 C 和 A 之间作出取舍

一个分布式系统里面,节点组成的网络原本应该是连通的。然而可能由于一些故障或者延时,使得有些节点之间不连通了,整个网络就分红了几块区域。数据就散布在了这些不连通的区域中,这就叫分区。当你一个数据项只在一个节点中保存,那么分区出现后,和这个节点不连通的部分就访问不到这个数据了。这时分区就是没法容忍的。提升分区容忍性的办法就是将一个数据项复制到多个节点上,那么出现分区以后,这一数据项就可能分布到各个区里,容忍性就提升了。然而,要把数据复制到多个节点,就会带来一致性的问题,就是多个节点上面的数据多是不一致的。要保证一致,每次写操做就都要等待所有节点写成功,而这等待又会带来可用性的问题。总的来讲就是,数据存在的节点越多,分区容忍性越高,但要复制更新的数据就越多,一致性就越难保证。为了保证一致性,更新全部节点数据所须要的时间就越长,可用性就会下降 -- 来自知乎邬江的回答算法

CAP 理论实际想表达的是任何分布式系统不能同时知足强一致性、高可用性和较好的分区容错性sql

CAP

RDBMS: Relational Database Management System,关系型数据库管理系统数据库

由 CAP 定理可知数据库的设计须要权衡取舍,因此能够把数据库大体分为三类:服务器

  • CA: 单点集群,知足一致性,可用性的系统,一般在可扩展性上不太强大
  • CP: 知足一致性,分区容忍性的系统,一般性能不是特别高
  • AP: 知足可用性,分区容忍性的系统,一般可能对一致性要求低一些

分布式和集群的区别:网络

分布式: 不一样的多台服务器上面部署不一样的服务模块,他们之间经过 Rpc/HTTP 等方式进行通讯和调用,对外提供服务和组内协做架构

集群: 不一样的多台服务器上面部署相同的服务模块,经过分布式调度软件进行统一的调度,对外提供服务和访问并发

ACID 和 BASE

ACID

ACID 指的是传统数据库中事务操做所具有的四个特性:异步

  • 原子性 (Atomicity): 一个事务 (transaction) 中的全部操做做为一个基本单元,要么所有完成,要么所有不完成,不会结束在中间某个环节。事务在执行过程当中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务历来没有执行过同样
  • 一致性 (Consistency): 在事务开始以前和事务结束之后,数据库的完整性没有被破坏。一个事务可以正确地将数据库从一个一致性状态变换为另外一个一致性状态
  • 隔离性 (Isolation): 也能够称做独立性,数据库容许多个并发事务同时对其数据进行读写和修改的能力,隔离性能够防止多个事务并发执行时因为交叉执行而致使数据的不一致。事务隔离分为不一样级别,包括读未提交 (Read uncommitted)、读提交 (read committed)、可重复读 (repeatable read) 和串行化 (Serializable)
  • 持久性 (Durability): 事务处理结束后,对数据的修改就是永久的,即使系统故障也不会丢失

BASE

BASE 理论是对 CAP 理论的延伸,核心思想是虽然没法作到强一致性 (Strong Consistency),但能够采用适合的方式达到最终一致性 (Eventual Consitency)

BASE 包含三部分:

  • 基本可用 (Basically Available): 指分布式系统在出现故障的时候,容许损失部分可用性,保证核心可用
  • 软状态 (Soft State): 指容许系统存在中间状态,而该中间状态不会影响系统总体可用性。分布式存储中通常一份数据至少会有三个副本,容许不一样节点间副本同步的延时就是软状态的体现。mysql replication 的异步复制也是一种体现
  • 最终一致性 (Eventual Consistency): 指系统中的全部数据副本通过必定时间后,最终可以达到一致的状态。最终一致性是弱一致性的一种特殊状况

二者区别

ACID 隶属于 CA,是传统数据库经常使用的设计理念,追求强一致性模型

BASE 隶属于 AP,支持的是大型分布式系统,提出经过牺牲强一致性得到高可用性

但在实际的分布式场景中,不一样业务单元和组件对数据一致性的要求是不一样的,所以在具体的分布式系统架构设计过程当中,ACID 特性与 BASE 理论每每会结合在一块儿使用。好比总体知足 BASE,局部知足 ACID。

一致性模型

  • 强一致性: 当更新操做完成以后,任何多个后续进程或者线程的访问都会返回最新的更新过的值。这种是对用户最友好的,就是用户上一次写什么,下一次就保证能读到什么。可是这种实现对性能影响较大,由于这意味着,只要上次的操做没有处理完,就不能让用户读取数据
  • 弱一致性: 系统并不保证进程或者线程的访问都会返回最新的更新过的值。系统在数据写入成功以后,不承诺当即能够读到最新写入的值,也不会具体的承诺多久以后能够读到。但会尽量保证在某个时间级别(好比秒级别)以后,可让数据达到一致性状态
    • 最终一致性: 弱一致性的特定形式。系统保证在没有后续更新的前提下,系统最终返回上一次更新操做的值。在没有故障发生的前提下,不一致窗口的时间主要受通讯延迟,系统负载和复制副本的个数影响。DNS 是一个典型的最终一致性系统
      • 因果一致性: 若是 A 进程在更新以后向 B 进程通知更新的完成,那么 B 的访问操做将会返回更新的值。若是没有因果关系的 C 进程将会遵循最终一致性的规则
      • 读己所写一致性: 因果一致性的特定形式。一个进程总能够读到本身更新的数据
      • 会话一致性: 读己所写一致性的特定形式。进程在访问存储系统同一个会话内,系统保证该进程读己之所写
      • 单调读一致性: 若是一个进程已经读取到一个特定值,那么该进程不会读取到该值旧版本的任何值
      • 单调写一致性: 系统保证对同一个进程的写操做串行化

上述最终一致性的不一样方式能够进行组合,例如单调读一致性和读己之所写一致性就能够组合实现。而且从实践的角度来看,这二者的组合,读取本身更新的数据,和一旦读取到最新的版本不会再读取旧版本,对于此架构上的程序开发来讲,会少不少额外的烦恼。

从服务端角度,如何尽快将更新后的数据分布到整个系统,下降达到最终一致性的时间窗口,是提升系统的可用度和用户体验很是重要的方面。

为了解决分布式的一致性问题,出现了不少一致性协议和算法,好比二阶段提交协议,三阶段提交协议和 Paxos 算法

FLP 不可能定理

异步通讯场景,即便只有一个进程失败了,也没有任何算法能保证非失败进程可以达到一致性。

异步通讯与同步通讯的最大区别是没有时钟、不能时间同步、不能使用超时、不能探测失败、消息可任意延迟、消息可乱序

FLP Impossibility 的证实

分布式事务

分布式事务用于在分布式系统中保证不一样节点之间的数据一致性,一般会涉及到多个数据库。分布式事务处理的关键是必须有一种方法能够知道事务在任何地方所作的全部动做,提交或回滚事务的决定必须产生统一的结果(所有提交或所有回滚)。

DRDA: Distributed Relational Database Architecture,分布式关系数据库架构

XA 规范

XA 规范是 X/Open 组织(即如今的 Open Group) 关于分布式事务处理 (DTP) 模型的处理规范。

DTP 模型包括应用程序 AP事务管理器 TM资源管理器 RM通讯资源管理器 CRM 四部分。常见的事务管理器 TM 是交易中间件,常见的资源管理器 RM 是数据库,常见的通讯资源管理器 CRM 是消息中间件。交易中间件是必需的,由它通知和协调相关数据库的提交或回滚。

规范描述了全局的事务管理器与局部的资源管理器之间的接口。XA 规范的目的是容许的多个资源(如数据库,应用服务器,消息队列,等等)在同一事务中访问,这样可使 ACID 属性跨越应用程序而保持有效。

XA 使用两阶段提交或三阶段提交来保证全部资源同时提交或回滚任何特定的事务

两阶段提交-2PC

当一个事务跨越多个节点时,为了保持事务的 ACID 特性,须要引入一个做为协调者的组件来统一掌控全部节点(称做参与者)的操做结果并最终指示这些节点是否要把操做结果进行真正的提交。因此两阶段提交 (Two-phase Commit) 的算法思路能够归纳为: 参与者将操做成败通知协调者,再由协调者根据全部参与者的反馈情报决定各参与者是否要提交操做仍是停止操做。

两阶段提交须要的条件:

  1. 分布式系统中,存在一个节点做为协调者 (Coordinator),其余节点做为参与者 (Cohorts),且节点之间能够进行网络通讯
  2. 全部节点都采用预写式日志,且日志被写入后即被保持在可靠的存储设备上,即便节点损坏不会致使日志数据的消失
  3. 全部节点不会永久性损坏,即便损坏后仍然能够恢复

两个阶段分别为:

  1. 准备阶段(投票阶段)
  2. 提交阶段(执行阶段)

准备阶段

  1. 事务协调者(事务管理器)给每一个参与者(资源管理器)发送 Prepare 消息,询问是否能够执行提交操做,并开始等待各参与者节点的响应
  2. 参与者执行事务操做,并将 Undo 信息和 Redo 信息写入日志
  3. 各参与者节点响应协调者节点发起的询问。若是参与者节点的事务操做实际执行成功,则它返回一个"赞成" (agreement) 消息;若是参与者节点的事务操做实际执行失败,则它返回一个"停止" (Abort) 消息

Prepare

第一阶段也被称做投票阶段,即各参与者投票是否要继续接下来的提交操做

提交阶段

  • 当协调者节点从全部参与者节点得到的响应消息都为"赞成"时
    1. 协调者节点向全部参与者节点发出"正式提交"的请求
    2. 参与者节点正式完成操做,并释放在整个事务期间内占用的资源
    3. 参与者节点向协调者节点发送"完成"消息
    4. 协调者节点收到全部参与者节点反馈的"完成"消息后,完成事务

Commit-Done

  • 若是任一参与者节点在第一阶段返回的响应消息为"停止",或者协调者节点在第一阶段的询问超时以前没法获取全部参与者节点的响应消息时
    1. 协调者节点向全部参与者节点发出"回滚操做"的请求
    2. 参与者节点利用以前写入的 Undo 信息执行回滚,并释放在整个事务期间内占用的资源
    3. 参与者节点向协调者节点发送"回滚完成"消息
    4. 协调者节点收到全部参与者节点反馈的"回滚完成"消息后,取消事务

Commit-Failed

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

缺陷

一、同步阻塞问题。执行过程当中,全部参与节点都是事务阻塞型的。当参与者占有公共资源时,其余第三方节点访问公共资源都将处于阻塞状态

二、单点故障。因为协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤为在第二阶段,协调者发生故障,那么全部的参与者还都处于锁定事务资源的状态中,而没法继续完成事务操做。(若是是协调者挂掉,能够从新选举一个协调者,可是没法解决由于协调者宕机致使的参与者处于阻塞状态的问题)

三、数据不一致。在第二阶段中,当协调者向参与者发送 commit 请求以后,发生了局部网络异常或者在发送 commit 请求过程当中协调者发生了故障,这会致使只有一部分参与者接受到了 commit 请求。而在这部分参与者接到 commit 请求以后就会执行 commit 操做。可是其余部分未接到 commit 请求的机器则没法执行事务提交,因而整个分布式系统便出现了数据不一致的现象

四、二阶段提交存在一个没法解决的问题:

对于现有模型,可能会出现的错误和相应的措施以下

  • 协调者出错,参与者不出错
    • 能够经过快速建立一个新的协调者来解决(或者本来就有备用协调者)
  • 协调者不出错,参与者出错
    • 若是参与者是第一阶段一开始就挂了,那么投票失败,协调者取消事务便可
    • 若是参与者是投完票才挂的,那么投票依旧有效,协调者会下达最终的事务操做指令,正常节点都会完成最后的事务提交或回滚,而挂掉的参与者恢复后只要询问协调者并执行相同的操做便可
  • 协调者出错,参与者也出错
    • 这种状况就比较复杂了,须要单独讲

关于第三个问题的详细描述以下:

2PC_Failed_0

协调者挂了,RM3 也挂了,此时分两种状况,RM3 是否执行了最后的事务操做:

  • RM3 挂以前无论收没收到指令,只要没有执行事务操做,都是不影响最终一致性的,由于 RM3 挂了但它的事务还没有做出最后的提交或回滚时,新的协调者和其余正常节点无论最后是作了提交仍是回滚,只要 RM3 恢复后询问新协调者采起相同的操做便可保证全局一致
  • 严重的问题是挂掉的 RM3 挂以前执行了事务的最终操做:
    • 协调者第二阶段发出一条指令后挂了,而 RM3 收到这条指令后对事物作了操做后也跟着挂了,等新的协调者出现时,没有人知道以前原协调者发送的是 Commit 仍是 Rollback,也不清楚 RM3 是执行了提交仍是回滚;新的协调者出现后会询问各节点状态,若是有节点回复的是 No,那么发送回滚指令,若是各节点都回复 Yes,则发送提交指令;好比上图中新协调者和其余正常节点应该会投票成功而后执行了 Commit 指令,而 RM3 恢复正常后,总体是否一致很难肯定,由于 RM3 可能在第一阶段回复的是 Abort,而后原协调者发送了 Rollback,挂以前 RM3 已经回滚,那么此时全局就不一致了(此时须要不一致的 RM3 经过其余方式恢复一致,但完成以前系统总体是不一致的)

最后这个问题就是 2PC 没法解决的问题,但 3PC 能够必定程度上解决

三阶段提交-3PC

三阶段提交 (Three-phase Commit) 是针对两阶段缺点而设计的改进型,相比较两阶段提交,作出如下两点改动:

  1. 引入超时机制。同时在协调者和参与者中都引入超时机制(2PC 只有协调者设置了超时机制),超时后能够默认执行 Commit 或者 Abort,这一点能够避免事务资源长时间被阻塞
  2. 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段以前各参与节点的状态是一致的

三个阶段分为:

  1. CanCommit
  2. PreCommit
  3. DoCommit

CanCommit

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

CanCommit

PreCommit

  • 假如协调者从全部的参与者得到的反馈都是 Yes 响应,那么就会执行事务的预执行:
    1. 发送预提交请求协调者向参与者发送 PreCommit 请求,并进入 Prepared 阶段
    2. 事务预提交参与者接收到 PreCommit 请求后,会执行事务操做,并将 Undo 和 Redo 信息记录到事务日志中
    3. 若是参与者成功的执行了事务操做,则返回 ACK 响应,同时开始等待最终指令

3PC_PreCommit_Yes

此图中若是某条 PreCommit 消息未到达或者超时,RM 应该中断本身本地的事务,就跟下图中 Abort 超时同样

  • 假若有任何一个参与者向协调者发送了 No 响应,或者等待超时以后,协调者都没有接到参与者的响应,那么就执行事务的中断:
    1. 发送中断请求协调者向全部参与者发送 Abort 请求
    2. 参与者收到来自协调者的 Abort 请求以后(或超时以后,仍未收到协调者的请求),执行事务的中断

3PC_Precommit_Abort

DoCommit

根据状况分为两种情形:

  • 正常响应后执行提交操做完成事务:
    1. 协调者接收到 PreCommit 阶段中参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态,并向全部参与者发送 DoCommit 请求
    2. 参与者接收到 DoCommit 请求以后,执行正式的事务提交,并在完成事务提交以后释放全部事务资源
    3. 事务提交完以后,向协调者发送 ACK 响应
    4. 协调者接收到全部参与者的 ACK 响应以后,完成事务

3PC_DoCommit_Done

  • 协调者没有接收到参与者发送的 ACK 响应(多是接受者发送的不是 ACK 响应,也可能响应超时),那么就会执行中断事务:
    1. 协调者向全部参与者发送 Abort 请求
    2. 参与者接收到 Abort 请求以后,利用其在阶段二记录的 Undo 信息来执行事务的回滚操做,并在完成回滚以后释放全部的事务资源。
    3. 参与者完成事务回滚以后,向协调者发送 ACK 消息
    4. 协调者接收到参与者反馈的 ACK 消息以后,结束事务

3PC_DoCommit_Failed

图中 RM3 在第二阶段未正常响应 ACK,可能有以下状况:

  • 好比 RM3 未正常接收 PreCommit 消息,在第二阶段就取消了本地事务,因此没有返回 ACK,则第三阶段接收 Abort 时并不须要作什么
  • 也多是 RM3 收到了 PreCommit 消息并执行了事务操做,可是 ACK 消息发送超时,则第三阶段接收 Abort 时也是同其余节点同样执行 Undo 回滚
  • 若是是第二阶段 RM3 挂了,若是是持久性挂了,那就挂了吧,现阶段就无论它了,等恢复了再作数据同步;若是挂了以后又恢复了,须要询问 TM 如今的事务状态来进行判断是否取消本地事务仍是继续完成事务

在第三阶段,若是参与者没法及时接收到来自协调者的 DoCommit 或者 Abort 请求时,会在等待超时以后,会继续进行事务的提交

这么作是有必定考究的,由于能进入第三阶段,说明协调者确定在第二阶段发起了 PreCommit 请求,而发起 PreCommit 请求的前提是第一阶段全部参与者都响应了 Yes,这代表全部参与者当时的状态都是乐观的,那么第三阶段参与者就算没有按时收到来自协调者的 DoCommit 请求,也继续完成本地事务的提交,这样完成全局事务的可能性仍是很大的,这一点是针对 2PC 数据不一致问题的

可是这一点也会形成 3PC 的一致性缺陷: 若是此时是协调者对某个 RM 发出的 Abort 请求超时,而那个 RM 继续完成本地事务的提交,这就会形成与其余进行回滚操做的节点数据不一致(这种状况几率较小)

缺陷

对应 2PC 的缺陷,咱们能够看到 3PC 解决了很多问题,一方面默认的超时机制能够避免单点故障形成的资源长久阻塞的问题;另外一方面状态确认和默认超时提交也能够解决 2PC 的数据不一致问题(虽然这会形成 3PC 本身的不一致问题,可是由于 3PC 的机制,这种几率更小)

至于前面提到的 2PC 没法解决的问题,3PC 又是怎么解决的呢?

咱们能够回想 2PC 的问题,冲突的条件就是 RM3 第一阶段投了反对票,而这件事只有原协调者知道,挂掉的二者执行了回滚操做,而新协调者和正常节点会执行提交操做;可是 3PC 将准备阶段分为两步能够确保最终事务操做以前,你们都知道投票结果,描述以下:

  • 假如一样是第二阶段协调者和 RM3 挂了

3PC_Failed_0

其实只要没有执行事务最终的提交或者回滚都不影响最终结果,只要后面 RM3 恢复后查询协调者并执行相同的操做便可

  • 假如是进入第三阶段协调者和 RM3 挂了,并且 RM3 已经执行了最终操做

3PC_Failed_1

这时候新协调者出现后,只要查看正常节点的状态就能够知道发送提交仍是回滚指令: 若是都是 Commited 或者 PreCommit 状态,说明第一轮投票结果都是 Yes,不然不可能进入 PreCommit 状态,因此新协调者发送提交指令便可;若是存在节点的状态是 Cancel,说明第一轮投票结果有反对票,那么新协调者发送回滚指令便可。可能这里会有个疑问,讲 DoCommit 的时候咱们说过第二阶段参与者的 ACK 可能没法正常响应协调者,若是是 RM3 第一轮投了支持票后面进入 PreCommit 状态时没有把 ACK 正常响应给协调者,所以协调者发送了 Abort 指令给 RM3 后挂了,RM3 收到指令后也挂了,此时仍是会形成 RM3 回滚,而新协调者和正常节点执行提交的情况,这里其实 3PC 好像也无法解决,可是这种状况的几率你能够估算一下,原本协调者发送指令挂了而后某些参与者执行后也挂了的几率自己就低了,还要知足挂的协调者在投完支持票后响应超时引发协调者发送 Abort 指令的几率,那就更小了

理解 2PC 和 3PC

咱们用一个实际生活的例子来讲明并理解这个过程:

假设协调者是牧师,而参与者是一男一女,他们来到教堂单独跟牧师见面并传达是否想跟另外一方创建更深的关系的信息

2PC

第一步男女双方给牧师递了小纸条,上面写着是否想跟另外一方创建更深的关系,而且各自准备好了信物 (Undo&Redo)

第二步牧师看过以后,若是双方都写“是”,那么告知双方交换信物表示能够继续深交;若是有一方写“否”,那么告知双方分手吧,信物也撤了吧

可是这里 2PC 没法解决一个问题: 假如男方写的纸条信息是“否”,而牧师看过以后先告诉男方你把信物撤了吧,大家不合适,可是牧师紧接着心脏病犯了住院了,而男方收到消息后也撤了信物准备分手,但是男方忽然被人打了住院昏迷;新牧师出现,继续询问女方的意见,由于不知道男方的意见,他们可能会促成一桩不美满的关系

3PC

第一步男女双方仍是递了小纸条,表示本身的意愿

第二步牧师看过以后,若是双方都写“是”,那么先告知双方准备信物;若是有一方写“否”,那么告知双方不用准备信物了;双方收到消息后再回应说本身知道了

第三步牧师确认双方的回应后,告知双方最后是否在一块儿,是否交换信物

在这里假如第三步牧师也是先告知男方后突发心脏病住院,而男方也是收到消息后被打昏迷,此时新牧师出现后能够查看女方是否准备了信物而完成最后的决定,由于假如男方本来是不一样意,那么第二步双方确定是没有准备信物的,而假如男方是赞成的,那么双方确定是准备了信物的,那么新牧师能够放心宣布双方能够在一块儿

3PC 还对双方都引入了超时,2PC 中只有牧师没收到消息时会取消事务,而 3PC 中若是男女双方长时间没有收到牧师消息后也会执行本身的决定,避免了 2PC 中一样情形时男女双方在这里一直耗着(阻塞)而错过了下一任。只不过咱们提到了若是第三步牧师是由于长时间没有收到男方的消息时也取消了事务,虽然这时候男女双方都是赞成的,可是仍是会被取消事务,这也可能形成新的不一致问题,可是相对来讲几率就小得多了

解决不一致问题

经过上述过程可知最后不管是二阶段提交仍是三阶段提交都没法完全解决分布式的一致性问题,若是系统正常运行都能知足强一致性,可是若是出现意外仍是会致使不一致,不过能够经过其余手段达到一致性,好比分区数据恢复或者事务补偿机制或者采用其余一致性算法

分区数据恢复

Partition-Recover

复杂的数据恢复能够参见 SVN 的版本控制,可能能够自动合并,也可能会发生冲忽然后须要人工干预

简单的数据恢复能够参见 Mysql 的主从同步,数据能够自动从主库导到从库

合并分区数据达成一致并非最困难的,更困难的是处理分区过程当中产生的错误。当分区操做引发错误,能够经过事务补偿补救错误,这多是人工的也多是自动的

分区事务补偿

好比 MQ 消息事务和 TCC 事务协议就是一种补偿机制:

  • MQ 事务: 利用消息中间件来异步完成事务的后一半更新,实现系统的最终一致性
  • TCC 事务: TCC 是阿里巴巴提出的协议,将一个事务分为 Try、Commit、Cancel 三部分,也能够达到最终一致性

其余一致性算法

  • Paxos 算法: 一种基于消息传递且具备高度容错特性的一致性算法
    • Raft 算法: Raft 是一个共识算法,用于取代 Paxos。Raft 的目标是提供更好理解的算法,而且证实能够提供与 Paxos 相同的容错性以及性能
    • Zab: Zookeeper atomic broadcast protocol,是 Zookeeper 内部用到的一致性协议。相比 Paxos,Zab 最大的特色是保证强一致性 (strong consistency,或叫线性一致性 linearizable consistency)

Reference

相关文章
相关标签/搜索