分布式系统入门笔记(一):分布式系统基本概念和两三阶段提交

  现在觉得分布式系统比较的有意思,不是说这个概念流行时髦可以装逼,而是像Lamport爷爷在其论文中所描述的那样,分布式系统更像是一个场景,一个民主性团体协作的过程。在这个分布式的群体中,任何一个角色都有自己的行为,都可能出错,都可能无法跟别人通信,都有可能网络分区“脑裂”,也都有可能会死掉,分布式系统让我们的眼界从原先的单个人,扩展到一个社会团体了。
  大名鼎鼎的分布式锁Google Chubby的作者Mike Burrows说过:“这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品!”Paxos在当前的生产环境中获的了巨大的成功,或许真的学了他就不用学别的分布式协议了,不过整个分布式系统中的概念、观念和涉及到的问题还是要了解一下的。
  同时,后续Paxos的学习打算从微信团队的PhxPaxos入手,主要考虑到的是:他们声称基于Paxos Made Simple进行工程化,不进行任何算法变种,所以代码学习研究会比较的原汁原味;资料比较多比较详细,偶尔还会有相关更新资料放出;通过微信的生产环境进行验证,说明比较肯定稳定、可靠和高效;其实后面看了写些Google Chubby信息,感觉PhxPaxos工程实现中遇到的问题和解决的思路跟Chubby大部分比较相似,所以这些共性的东西跟谁学都没有太大的差异。

一、基础预备知识

1.1 事务极其ACID特性

  分布式系统中的事务(Transaction)是对系统中数据进行访问和更新操作所组成的程序执行逻辑单元,事务的应当具有四个特征ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。
  原子性保证事务是一个不可分割的单位,事务中的操作要么全部成功执行,要么都不执行;一致性保证数据库中只能包含成功的事务提交的结果,从一个成功状态变更到另外一个成功状态;隔离性用于控制并发环境中事务之间的干扰关联程度;持久性一般是数据落盘,在提交后其修改应该是永久的,在宕机或者重启后数据能重新被恢复到成功结束的状态。
  对于隔离性,标准SQL规范中定义了四种事务隔离级别:
  (1). 未授权读取:也称为读未提交(Read Uncommitted),该隔离允许脏读,可以读到别的事务执行中任何可能的未提交中间状态,事务可以读取未提交的数据,这也被称为脏读。未提交读从性能上不会比其他级别好太多,但是缺乏其他级别的很多好处,在实际应用中一般很少使用;
  (2). 授权读取:也称为读已提交(Read Committed),一个事务开始时只能“看见”已经提交的事务所做的修改,授权读取允许不可重复读取,因为两次执行同样的查询,可能会得到不一样的结果。这是除MySQL之外大多数数据库(SQL Server, Oracle)的默认隔离级别;
  (3). 可重复读取:(Repeatable Read)保证在一个事务处理的过程中如果多次读取同一个数据时,其值都跟事务开始时候是一致的,该事务级别禁止了不可重复读和脏读,但是可能会出现幻读(Phantom Read)。不可重复读和幻读还是有一定差异的,一般前者是update修改数据体现的差异,后者是在insert、delete时候体现出来的差异,比如范围查询的记录数在别的事务插入或者删除记录的时候会变得不一致;
  (4). 串行化:(Serializable)最高的隔离级别,其要求所有的事务都串行化执行,可以避免幻读问题。其会极大影响数据库的并发性能。

1.2 CAP理论和BASE理论

1.2.1 CAP理论(布鲁尔定理)

  一个分布式系统不可能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三个基本要求,最多只能同时满足其中的两项。
  (1). 一致性:指的在分布式环境下数据在多个副本之间是否能够保持一致性,如果针对数据项的更改操作执行成功后,所有的用户都可以读到其最新值,那么这个系统被认为是强一致性的。
  (2). 可用性:对于用户的每一个操作请求,都能够在有限的时间内返回结果。
  (3). 分区容错性(容忍网络分区):分布式系统在遇到任何网络分区故 障的时候,仍然能够保证对外提供满足一致性和可用性的服务,除非整个网络环境都发生了故障。
  对于CAP理论的实践中,既然是分布式系统肯定要满足P,所以很多分布式系统的架构设计通常是根据业务类型是在A可用性和C一致性上面做出平衡的结果,这里的C一致性不是说放弃一致性,因为放弃一致性数据错误了整个系统就没有意义了,而是说放弃实时的强一致性,但是保证数据的最终一致性,需要到达最终一致性需要一个时间窗口,用于各个数据副本之间同步。

1.2.2 BASE理论

  BASE理论是CAP中一致性和可用性互相权衡的结果,代表着:Basically Available(基本可用)、Soft State(软状态)、Eventually Consistent(最终一致性),表明分布式系统即使无法做到强一致性,但是每个应用都可以根据业务的特点,采用适当的方式来使系统达到最终一致性。
  (1). 基本可用(Basically Availble):当出现不可预知的故障的时候,允许在响应时间、或者功能上有所损失,核心功能仍然可用;
  (2). 弱状态(Weak State):也称为软状态,指的允许系统中的数据存在中间状态,并认为该中间状态不会影响到系统整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时;
  (3). 最终一致性(Eventual Consistency):强调系统中所有的数据副本,在经过一段时间的同步之后,最终总能够达到一个一致性的状态,其不需要实时保证系统数据的强一致性;在工程实践中最终一致性存在以下变种,他们可以按照需要自由组合搭配,以设计实现出最终一致性的分布式系统:
  a. 因果一致性(Causal Consistency):如果进程A在更新完某个数据项之后通知了进程B,那么进程B之后对数据项的访问都应该能够获取到进程A更新后的最新值,并且如果进程B对数据项进行更新的话,也是基于A更新后的最新值,不会发生丢失更新的状况。此外,与进程A无因果关系的进程C访问该数据没有任何限制;
  b. 读己之所写(Read Your Writes):进程A更新一个数据项之后,它自己总能够访问到更新后的最新值;
  c. 会话一致性(Session Consistency):会话一致性将对系统数据的访问框定在一个会话当中,系统能够保证在同一个有效的会话中实现“读己之所写”的一致性,执行更新操作之后客户端能够在同一个会话中始终读到该数据项的最新值;
  d. 单调读一致性(Monotonic Read Consistency):如果进程从系统中读出一个数据项的某个值之后,那么系统对于该进程后续的任何数据操作都不应该返回更旧的值;
  e. 单调写一致性(Monotonic Write Consistency):一个系统需要能够保证来自同一个进程的写操作都是被顺序执行的。

二、两阶段提交

  分布式系统中最经典的就是二阶段提交协议、三阶段提交协议和Paxos算法了。设计出这些协议的主要原因,就是在分布式系统中,每个节点可以知道自己在事务操作的过程中是成功还是失败,但是无法知道其他分布式节点的操作结果。传统上就是产生一个协调者Coordinator来统一调度各个分布式节点的执行逻辑,其负责调度参与者的行为,并决定这些参与者是否要把事务进行提交。
  目前大部分的数据库都是采用二阶段提交协议来完成分布式事务处理的。

2.1 事务执行过程

2.1.1 阶段一:提交事务请求(Commit request)

  (1). 事务询问:协调者向所有参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待所有参与者的回应;
  (2). 执行事务:各参与者执行事务操作直到可以提交的点后暂停,并将Undo和Redo信息记入事务日志中;
  (3). 各参与者向协调者反馈事务询问的结果:如果参与者成功执行了事务操作,则反馈给协调者YES响应,表示事务可以执行,如果参与者没有成功执行事务,那么反馈给协调者NO响应,表示事务不可以执行;

2.1.2 阶段二:执行事务提交(Commit phase)

  协调者会根据参与者反馈的情况来决定最终是否可以执行事务提交操作,正常情况包括如下两种情况:
  (1). 执行事务提交:假如协调者从所有的参与者获得的反馈都是YES响应
  a. 发送提交请求;协调者向所有参与者发出Commit信息;
  b. 事务提交:参与者收到Commit请求后,会正式执行事务提交操作,并在完成提交后释放在整个事务执行期间占用的锁和其他事务资源;
  c. 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack消息;
  d. 完成事务:协调者收到所有Ack消息后,完成事务。
  (2). 中断事务:任何一个参与者向协调者反馈了NO响应,或者在等待超时之后,协调者尚无接收到所有参与者的反馈响应,那么就会中断事务
  a. 发送回滚请求:协调者向所有参与者节点发送Rollback请求;
  b. 事务回滚:参与者收到Rollback请求后,会利用在阶段一种记录的Undo日志信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用锁和其他事务资源;
  c. 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息;
  d. 中断事务:协调者收到所有参与者反馈的Ack消息后,完成事务中断;

2.2 2PC的优缺点

  (1). 优点:原理简单、实现方便,而且是强一致性算法。
  (2). 缺点:同步阻塞、单点问题、脑裂、太过保守。
  a. 同步阻塞:最严重的问题,会极大限制分布式系统的性能,当参与者向协调者发送投票信息后,必须阻塞等待直到收到commit/rollback命令;
  b. 单点问题:一旦协调者出现问题,整个2PC都无法运转,而且如果协调者是在阶段二中出现问题的话,那么其他参与者将会一直处于锁定事务资源的状态当中;
  c. 数据不一致:当协调者向所有参与者发送Commit请求后如果部分协调者因为问题没有执行Commit,那么就会使得整个分布式系统出现数据不一致的情况;
  d. 太过保守:没有完善的容错机制,任意一个参与者节点的问题都会导致整个事务的失败;

三、三阶段提交

  将原先的事务拆分成canCommit、preCommit 和DoCommit三个阶段组成。

3.1 事务执行过程

3PC

3.1.1 阶段一:CanCommit

  (1). 事务询问:协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务的提交操作,并开始等待各参与者的响应;
  (2). 各参与者向协调者反馈事务询问的响应:参与者收到canCommit请求后,正常情况下如果其自身认为可以顺利执行事务,则反馈给协调者YES响应,并进入预备状态,否则反馈给协调者NO响应;

3.1.2 阶段二:PreCommit

  协调者会根据各参与者的反馈情况决定是否可以执行事务的preCommit操作,正常情况下,可能包含两种情况
  (1). 执行事务提交:协调者从所有的参与者获得的反馈都是YES响应
  a. 发送预提交请求:协调向所有参与者节点发出preCommit请求,并进入Prepared阶段;
  b. 事务预提交:参与者收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中;
  c. 各参与者向协调者反馈事务执行的响应:如果参与者成功执行了事务操纵,那么就反馈协调者ACK响应,同时等待最终指令:Commit或者Abort;
  (2). 中断事务:任何一个参与者反馈了NO响应,或者在等待超时之后,协调者尚不能收到所有参与者的反馈响应,就会中断事务
  a. 发送中断请求:协调者向所有参与者发出Abort请求;
  b. 中断事务:无论收到来自协调者的Abort请求,或者在等待协调者请求过程中出现超时,参与者都会中断事务;

3.1.3 阶段三:doCommit

  该阶段是真正的事务提交,有两种情况
  (1). 执行提交
  a. 发送提交请求:假设协调者处于正常状态,并且收到了来自所有参与者的ACK响应,那么它将从预提交状态切换到提交状态,并向所有的参与者发送doCommit请求;
  b. 事务提交:参与者收到doCommit请求之后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的锁和其他事务资源;
  c. 反馈事务提交结果:参与者完成事务提交之后,向协调者发送ACK消息;
  d. 完成事务:协调者收到所有参与者Ack消息后,事务结束。
  (2). 中断事务:假设协调者处于正常工作状态,并且有任意一个参与者向协调者发送了NO响应,或者在等待超时之后协调和尚无法收到所有参与者的反馈响应,那么就会中断事务
  a. 发送中断请求:协调者向所有参与者发送Abort请求;
  b. 事务回滚:参与者接收到Abort请求后,会利用其在阶段二中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的锁和其他事务资源;
  c. 反馈事务回滚结果:参与者在事务回滚之后,向协调者发送Ack信息;
  d. 中断事务:协调者收到所有参与者反馈的Ack消息后,中断事务。
  需要注意的是,一旦进入阶段三,可能会出现两种故障:协调者故障或者协调者和参与者之间的网络通信出现故障,无论如何都会导致参与者无法及时收到来自协调者的doCommit或Abort请求,针对这种情况,只要参与者收到了preCommit命令,在参与者在等待超时之后如果还没有收到协调者的指令,会默认继续进行事务提交。

3.2 3PC的优缺点

  3PC的针对2PC的主要优化有:事先不申请资源的情况下进行preCommit演练,此处成功后续事务成功执行的可能性也会比较大;3PC是非阻塞的协议,在第三阶段如果参与者在时间窗口之内没有收到协调者的命令,会默认进行提交;
  (1). 优点:降低了参与者的阻塞范围,能够在出现单点故障后继续达成一致;
  (2). 缺点:在解决2PC的阻塞问题时候引入了新的问题,就是在参与者收到preCommit之后如果网络出现分化,此时协调者所在的节点和参与者无法进行正常的网络通信,此时参与者依然会进行事务的提交,很有可能会出现数据不一致的情况。同时3PC需要进行三次的消息传递,性能会有所影响。

参考