分布式系统演进之路(从单点到分布式)
分布式系统演进之路(从单点到分布式)可是我家有两个小孩,老是生病,三天两头的要跑医院,老是关门也不是办法。所以,在老婆有事的时候,我就去开店。我和我老婆就是「主备关系」。假设我老婆开了家饭店。因为没钱,早期客户也不是很多,所以就一个人张罗。这就是一个单点。因为如果我老婆有什么事情,那就没法开店了。即当这唯一的一个Server挂掉以后,Client就无法获得响应了。那怎么解决单点问题呢?非常简单,冗余。冗余的方法有两种:主从和集群!下面举个例子来说明,主从、集群和分布式的区别!
本文梳理从单点到分布式遇到的概念及问题,包括:
- 单点问题
- 主从
- 集群
- 负载均衡
- 分布式
- 分布式理论:CAP,BASE
- 一致性:2PC,3PC,TCC,消息事务,Paxos,Raft
在架构风格:万金油CS与分层中提到了CS架构风格。可以说CS架构风格是分布式架构的起点,所以我们从CS架构风格开始。
最简单的CS架构就是一个Client 一个Server!Client和Server之间建立连接,然后Server对Client的请求进行响应。
对于这个最简单的CS架构,有一个很明显的问题:「单点问题」
即当这唯一的一个Server挂掉以后,Client就无法获得响应了。那怎么解决单点问题呢?非常简单,冗余。
冗余的方法有两种:主从和集群!
注意:分布式并不是为了解决单点问题的,而是为了解决单台服务器无法支撑整体业务的问题。主从、集群和分布式下面举个例子来说明,主从、集群和分布式的区别!
假设我老婆开了家饭店。因为没钱,早期客户也不是很多,所以就一个人张罗。这就是一个单点。因为如果我老婆有什么事情,那就没法开店了。
可是我家有两个小孩,老是生病,三天两头的要跑医院,老是关门也不是办法。所以,在老婆有事的时候,我就去开店。我和我老婆就是「主备关系」。
但是呢,我也要上班,我不可能老是去开店。所以我们都没空的时候,就让我丈母娘帮忙开店。现在我、我老婆和丈母娘之间的关系还是「主备关系」!为什么?因为我们没有同时开店,只是在其他人都有事的时候才来开店的。也就是说同时只有一个服务对外提供服务。
一段时间后,饭店生意很好。老婆一个人有点忙不过来了。于是我就干脆辞职和老婆一起开店。我们做的事情相同。于是我们就组成了「集群」。我们同时对外提供相同的服务。
此时如果我老婆有事,那我又变成了一个单点服务。
再后来,我们两个人也忙不过来了。我们就又招了个服务员。服务员主要工作就是帮忙点菜、收拾碗筷之类的。我们就专心的做饭、收钱。现在我和我老婆之间还是集群,因为我们的工作一样。而我、我老婆和服务员就组成了分布式。我们三个人组成了完整的服务。
但是呢,现在服务员出现了单点问题。当这个服务员请假的话,我们又忙不过来了。于是我们又招了一个服务员。这两个服务员之间又组成了集群。
最后,饭店越开越大。我们招了服务员、收银、厨师、洗碗工、清洁员.....组成了一个大的分布式系统。
而我和我老婆则出去浪去了。
三者的区别从上面的例子,我们可以看出,主从、集群和分布式之间的区别:
- 主从:同时只有一台服务对外提供服务。当这台服务器挂掉以后,另外一个服务器对外提供服务。
- 集群:所有服务器同时对外提供服务。有一台或几台服务器挂掉以后,可能有部分客户端会受到影响(看负载均衡算法)
- 分布式:所有的服务组合成一个完成的系统对外提供服务。如果有一个或多个服务挂掉,那对应的那部分功能就没法使用。每个服务都可以通过集群来确保可用性。
虽然主从和集群解决了单点的问题。但是从主从到集群、再到分布式,系统间通信成指数级增长,也引入了各种其它问题。
在单点CS架构下,只有Client和Server之间存在网络通信,Server内部并不存在网络通信。而变成分布式以后,Server间也需要通过网络通信。由于网络的不稳定,可能会出现各种问题。同时由于节点数的增加,Server本身出问题的几率也就提高了,主要可能出现:
- 通信异常:由于网络的不稳定性,可能导致服务间的通信出现异常
- 网络分区:由于「通信异常」,可能导致服务间的数据不同步,或者部分服务无法对外服务。就是说,可能客户端访问到的响应有差异。
- 响应超时:一般来说,正常的调用会返回成功或失败的状态,但是网络通信可能出现超时。超时分为
- 发送超时:请求并没有到达服务端
- 响应超时:请求到达了服务端,但是服务端的响应并没有返回到客户端
- 处理方式:如果请求是幂等的,那可以再次请求。如果不是幂等的,那需要一些补偿措施。
- 节点故障:服务节点宕机,无法对外提供服务。
- 节点选择:即Client到哪个节点去获取数据。这就涉及到负载均衡算法,一般的负载均衡算法有(这里不展开)
- 随机
- 轮询
- 加权随机
- 加权轮询
- 源地址Hash
- 一致性Hash
实际上在分布式系统中,要解决的问题是:「在网络不可靠的情况下,如何保证数据一致性」?
处理方式有两种:
- 想办法保证强一致性
- 不保证强一致性,但保证最终一致性
保证强一致性的思路其实很简单,就是「假设网络在大部分情况下都是正常的,批量的去操作这一批节点,要么全部成功,要么全部失败」!
操作方式有:
- 2PC(两阶段提交)
- 事务补偿TCC(可以说是业务层面的2PC)
- 3PC(三阶段提交)
- Sagas:将分布式长事务拆分成多个短事务,由Sagas引擎来协调,具体没有研究。可参考最后的参考资料。
2PC和3PC都引入了一个「协调者」来协调各个节点(参与者):
- 客户端提交操作到协调者
- 协调者与参与者通信,确保各个参与者的操作
- 如果所有参与者操作成功,协调者返回客户端成功
- 否则协调者返回客户端失败
具体流程见下文。
2PC流程:
- 提交请求阶段(commit-request phase) :
- 协调者向所有参与者询问是否可以执行提交操作,然后开始等待各参与者的响应
- 参与者直接就开始执行事务操作,并将Undo信息和Redo信息写入日志
- 如果参与者的事务操作执行成功,则返回一个「同意」消息;否则返回一个「终止」消息
- 提交阶段(commit phase) :
- 当协调者从所有参与者获得的响应消息都为「同意」:
- 协调者向所有参与者发出「正式提交」的请求
- 参与者提交事务,并释放事务占用的资源
- 参与者向协调者发送「完成」消息
- 协调者收到所有参与者反馈的「完成」消息后,完成事务
- 如果任一参与者在第一阶段返回「终止」消息,或者协调者在第一阶段超时之前无法获取所有参与者的响应消息:
- 协调者向所有参与者发出「回滚操作」的请求
- 参与者利用之前写入的Undo信息执行回滚操作,并释放在整个事务期间占用的资源
- 参与者向协调者发送「回滚完成」消息
- 协调者收到所有参与者反馈的「回滚完成」消息后,取消事务
举例:
假设两个Server(s1 s2)向三个注册中心节点注册(c1 c2 c3),如果是2PC,流程如下:
- s1向c1 c2 c3发送「提交请求」,c1 c2 c3接收到了消息,将s1信息保存,并写入Undo和Redo日志,返回「成功」
- 此时s2向c1 c2 c3发送「提交请求」,但是c1 c2 c3目前被锁定了,所以只能等待
- s1收到所有的「成功」消息,向c1 c2 c3发送「确认提交」请求,c1 c2 c3提交事务,返回「完成」,s1事务结束
- s2超时没等到所有的「成功」消息,事务回滚。
问题:
二阶段提交的问题很明显:
- 节点阻塞:在请求阶段,节点就执行了事务操作,并进入阻塞状态。这会导致几个连锁问题:
- 节点本身阻塞,在这个事务结束之前,这个节点不响应其它请求(上面的例子就是这种情况)
- 如果节点占用了公共资源,其它想要访问公共资源的第三方节点也会进入阻塞状态
- 如果在第二阶段出现网络故障,则节点会一直阻塞(可使用超时机制)
- 数据不一致:在第二阶段提交事务时,可能网络故障了,只有部分节点提交了数据。这就导致了数据不一致。
- 超时失败:第二阶段,参与者执行完成了,并全部返回完成,但是协调者没有接收到,导致执行了回滚操作
- 所有参与者都回滚了,事务执行失败(实际事务都执行成功了,但是没有接收到响应)
- 部分参与者回滚,又导致了数据不一致
所以2PC并不能保证真正的一致性。
XA是基于2PC制定的分布式事务规范。JTA是基于XA实现的Java事务接口。事务补偿TCCTCC将整个业务逻辑分为三块:Try、Confirm和Cancel三个操作:
- Try:对业务系统做检测及资源预留。类似2PC的阶段一
- Confirm:对业务系统做确认提交。默认只要Try成功,Confirm一定成功。
- Cancel:在业务执行错误时,执行的业务取消,释放预留资源
举例:
还是假设两个Server(s1 s2)向三个注册中心节点注册(c1 c2 c3),如果是TCC,流程如下:
- s1向c1 c2 c3发送「Try请求」,c1 c2 c3接收到了消息,锁定服务列表信息,返回「成功」
- 此时s2向c1 c2 c3发送「Try请求」,锁定失败,可以等待或重试
- s1收到所有的「成功」消息,向c1 c2 c3发送「Confirm提交」请求,c1 c2 c3提交事务,返回「完成」,s1事务结束
问题:
TCC的主要问题是适用性问题,因为是业务层的事务管理,所以需要针对具体的业务进行具体的Try/Confirm/Cancel的实现。
3PC为了解决2PC的阻塞问题,就有了三阶段提交。三阶段提交将二阶段提交的「请求阶段」拆分为「CanCommit阶段」和「PreCommit阶段」。具体流程如下:
- CanCommit阶段: 协调者向参与者发送提交请求,参与者如果可以提交就返回「是」,否则返回「否」。(注意,这里并不会执行事务,所以也不会阻塞)
- PreCommit阶段:
- 如果参与者都返回「是」,则进行事务的预执行:
- 协调者向参与者发送PreCommit请求,并进入Prepared阶段
- 参与者接收到PreCommit请求后,执行事务操作,并将undo和redo信息记录到事务日志中
- 如果参与者执行成功,则返回「完成」消息,并等待最终指令
- 假如有任何一个参与者向协调者发送了「否」,或者等待超时,那么就中断事务:
- 协调者向所有参与者发送终止请求
- 参与者收到终止请求后(或超时仍未收到协调者的请求),则终止事务
- DoCommit阶段:该阶段提交事务,也分为两种情况:
- 如果协调者接收到所有参与者返回的「完成」,则从PreCommit状态进入DoCommit状态,同时向所有参与者发送doCommit请求
- 参与者接收到doCommit请求之后,提交事务,并在完成之后释放所有的资源
- 事务提交完之后,向协调者发送「提交完成」响应
- 协调者接收到所有参与者的「提交完成」响应之后,完成事务
- 如果协调者没有接收到所有参与者发送的「提交完成」响应(参与者返回的不是「提交完成」响应或者超时没有返回任何信息),则终止事务
举例:
依然假设两个Server(s1 s2)向三个注册中心节点注册(c1 c2 c3),如果是3PC,流程如下:
- s1向c1 c2 c3发送「CanCommit请求」,c1 c2 c3接收到了消息,返回「OK」
- 此时s2向c1 c2 c3发送「CanCommit请求」,c1 c2 c3接收到了消息,也返回「OK」
- s1收到所有的「成功」消息,向c1 c2 c3发送「PreCommit请求」,c1 c2 c3将s1信息保存,并写入Undo和Redo日志,返回「成功」
- s2收到所有的「成功」消息,向c1 c2 c3发送「PreCommit请求」,c1 c2 c3返回「失败」
- s1收到所有的「成功」消息,向c1 c2 c3发送「DoCommit请求」,c1 c2 c3提交事务,释放资源,返回成功。事务完成
- s2收到「失败」消息,事务回滚
问题:
3PC同样会出现数据不一致的情况,在第三阶段协调者终止事务,但是参与者没有接收到,则可能导致数据不一致。
最终一致性可以看到,强一致性在任何一个节点出现问题后,事务都会失败。在事务要求不是很高的情况下,并不一定要保证强一致性,只要保证最终一致性就可以了。保证最终一致性的思路是基于CAP定理和BASE理论。
CAP定理在《JDBC的架构设计》中提到了本地事务的ACID特性:
- 原子性(Atomicity):事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
- 一致性(Consistency):事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致状态的含义是数据库中的数据应满足完整性约束
- 隔离性(Isolation):多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性(Durability):已被提交的事务对数据库的修改应该永久保存在数据库中
CAP里也有一个A和一个C。但是实际两者之间并没有什么关系。CAP中的C、A、P分别是:
- 一致性(Consistency):这里的一致性指的是数据在多个副本之间保持强一致
- 可用性(Availability):指系统可以对外提供服务,这是一个架构属性。关于架构属性可参考之前的文章《什么是架构属性》
- 分区容错性(Partition tolerance):这里的分区指的是「网络分区」。指的是除非整个网络环境都发生了故障,否则系统在遇到任何网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务
CAP定理,说的是「对于一个分布式系统来说,不可能同时满足一致性、可用性和分区容错性。最多只能满足其中两项」!
那我们应该满足哪两项呢?这个都知道了,就是「分区容错性」和「可用性」,同时保证「最终一致性」!那为什么呢?
我们来假设:
- 假如放弃可用性:即系统遇到任何问题,就对外不可用。上面说了,分布式系统网络间通信数量大大增加,如果网络一波动,系统就不可用。那实际可用性还不如单点应用。那还要搞分布式干嘛?
- 假如放弃分区容错性:避免网络分区的方法就是将数据集中管理,又变成了单点应用了。
- 假如放弃强一致性:强一致性指的是像数据库事务那样,操作完了以后,所有的数据都是一致的。但是在分布式系统里面,如果出现了网络分区或者网络延时,那么是无法保证强一致性的。虽然放弃了强一致性,但是可以使用各种手段来保证最终一致性。就是说当网络故障恢复后,或者网络通信结束后,保证各个副本的数据一致。
BASE理论是CAP的实践理论。其核心思想是:「根据CAP定理,我们知道系统是无法做到强一致性的,但系统可以根据自身的业务特点,采用适当的方式来使系统达到最终一致。」
BASE包括:
- 基本可用(Basically Available):在系统出现故障时,相对于正常的系统来说,可能有某些服务降级,但是还是能正常对外提供服务。服务降级包括:
- 响应时间:即比正常系统响应时间慢
- 功能:某些功能可能不可用。比如18年双11,淘宝的地址服务就挂了,但是不影响用户败家,只是不能修改地址而已。
- 软状态(Soft State):指系统在最终一致之前的中间状态(即数据在一部分节点中,但还没有同步到所有的节点),该状态不影响系统的整体可用性。
- 最终一致性(Eventually Consistent):系统从软状态中,经过一段时间,最终需要达到数据一致。这个「时间」取决于网络延时、系统负载、数据复制方案等因素。
可以看出,现在的主要问题就是「如何保证最终一致性」?
最终一致性方案保证最终一致性的方法有:
- 消息事务
- Paxos算法
- Raft
消息事务的原理很简单,通过消息队列来保障最终一致性,大致流程为:
- 在提交事务时,需要提交一份数据到消息队列。两者要么全部成功,要么全部失败
- 消息队列消费端消费消息,执行后续的任务
- 如果执行成功则结束
- 如果因为网络原因执行失败,则重试。如果因为业务原因执行失败,则可以写入一个补偿消息。消息生产方进行数据回滚
举例:
还是假设Server(s1 s2)向三个注册中心节点注册(c1 c2 c3),消息事务流程如下:
- s1向c1和消息队列提交注册信息,提交成功
- s2向c1和消息队列提交注册信息,提交成功
- c2 c3监听到消息队列的消息,依次执行注册信息
- 执行成功则c1 c2 c3的服务列表最终都是一致的
- 如果c2执行s2失败,则需要向消息队列发送一个撤销操作,c1 c2接收到消息后,撤销s2的信息,s2接收到消息后,事务失败。
普遍认为Paxos比较难理解,即使Lamport不使用任何公式写的《Paxos Made Simple》论文也都比较难以理解。
我觉得《Paxos Made Simple》难以理解的原因有两个:
- 论文中没有明确「提案」和「Value」之间的关系,前面说「Value」,后面又说「提案」。实际上「提案」包括了「提案编号」和「提案值」(也就是Value),提案编号是一个全序ID,即全局唯一且是递增的。
- 论文里面描述的Paxos算法分为两个阶段,但是这里的两个阶段和2PC里所说的两阶段意义是不同的。2PC中的两个阶段是它的完整流程就是两个阶段,而Paxos里的两个阶段是交替执行的,直到达到最终一致。也就是说,对一次Paxos计算来说,可能要经过多次的阶段一、阶段二,才会达到最终一致
论文中对Paxos算法的流程描述如下:
阶段一:
- Proposer选择一个提案号n,向大部分的Acceptor发送携带了提案号n的prepare请求
- 如果Acceptor接收到了prepare请求,请求携带的提案号n大于它所响应的prepare请求所携带的提案号,那么它会返回一个响应,这个响应包含了它所通过的最大编号的提案(如果存在的话)
- ,并保证以后都不会再接收提案号小于n的请求了
- 阶段二:
- 如果Proposer接收到了大部分Acceptor的响应,然后Proposer就发送accept请求给Acceptor,这个accept请求包含了提案号n和值v,这个v是前面Acceptor返回的最大编号的提案里的值,如果Acceptor没有返回任何提案,则v由Proposer自定义
- 如果Acceptor接收到了提案号为n的accept请求,如果它没有响应任何提案号大于n的prepare请求,它就接收这个提案
它主要保证了,在一次提案提交过程中(也就是一个Paxos计算中):
- 只有提案被提交了,那么它里面的值才能被选中
- 最终只能有一个值被选中
- 如果某个进程获取到了某个值,那么这个值一定是被选中的那个值
举例
下面我还是以注册中心的流程,来阐述BasicPaxos算法(MultiPaxos这里不讨论)!
在这里Server可以说是Proposer,注册中心则是Acceptor,Client则是Learner。
假设有三个Server P1[192.168.1.1] P2[192.168.1.2] P3[192.168.1.3]。五个注册中心节点A1 A2 A3 A4 A5。三个客户端L1 L2 L3。现在P1 P2都是第一次注册到注册中心(第一次计算):
- P1 P2从全局ID生成器那里分别获取一个提案编号,P1[100] P2[101]
- P1携带提案编号100向A1 A3 A5发送prepare请求。但是因为网络不太好 A1 A3成功接收,但是A5发送失败了。A1 A3接收到了提案,记录下提案编号100,并保证以后不会再同意编号小于100的提案。因为是第一次接收提案,所以A1 A3都没有值返回。
- P2携带提案编号101向A1 A2 A5发送prepare请求。都发送成功了。A2 A5是第一次接收到提案,所以直接记录下提案编号101,并保证以后不会再同意编号小于101的提案。因为是第一次接收提案,所以A2 A5都没有值返回。对于A1来说,因为已经接收到P1的提案,但是101大于100,所以记录下101,并保证以后不会再同意编号小于101的提案。虽然A1接收了P1的prepare请求,但是并没有接收到任何值,所以也没有返回。
- P1接收到了A1 A3的响应,但是没有满足大多数。所以它重新获取了一个提案编号120,准备重新发送。
- 在此之前,P2接收到了A1 A2 A5的响应,将自己的IP作为值,并携带编号101一起[101 [192.168.1.2]],向A1 A2 A5发送accept请求。A1 A2 A5都接收了这个提案,并记录了下来。
- P1重新向A1 A3 A5发送prepare请求[120]。因为120大于A1 A3 A5所记录的提案号,所以A1 A3 A5都记录下来这个提案编号。由于A1 A5已经记录了P2的值,所以返回P2的值给P1。A3没有返回。
- P1接收到了A1 A3 A5的响应,开始发送accept请求,但是A1 A5返回了P2的值,所以P1只能发送[120 [192.168.1.2]]到A1 A3 A5。
- 第一次计算结束,目前A1 A3 A5记录的是[120 [192.168.1.2]]。A2记录的是[101 [192.168.1.2]],虽然编号不一样,但是值是一样的。A4则可以通过学习得到最终的服务列表。
但是P1明显没有注册上去,所以它又开始了第二次注册尝试(第二次Paxos计算),这时呢,P3也开始注册了:
- P1 P3从全局ID生成器那里分别获取一个提案编号,P1[210] P3[211]
- P1携带提案编号210向A1 A3 A5发送prepare请求。A1 A3 A5接收到了提案,记录下提案编号210,并保证以后不会再同意编号小于210的提案。因为这里是新的一次Paxos计算,所以这里实际还是第一次接收数据,所以不会返回提案值。
- P3携带提案编号211向A2 A4 A5发送prepare请求。因为211大于210,所以A2 A4 A5接收到了提案,记录下提案编号211,并保证以后不会再同意编号小于211的提案。这里也不会返回提案值。
- P1接收到了响应,开始发送access请求[210 [192.168.1.1]]到A1 A3 A5。A1 A3接收了这个提案,但是A5的提案号现在是211,它不接收这个提案。
- P3接收到了响应,开始发送access请求[211 [192.168.1.3]]到A2 A4 A5。A2 A4 A5接收了这个提案。
- P1重新获取了新的ID220,再次向A1 A3 A5发送prepare请求,A1 A3 A5都同意了。A1 A3返回[210 [192.168.1.1]],A5返回[211 [192.168.1.3]]
- P1接收到响应,只能发送[220 [192.168.1.3]]的prepare请求到A1 A3 A5。A1 A3 A5全部同意。
- 至此第二次计算结束,目前A1 A3 A5记录的是[220 [192.168.1.3]]。P3 A2 A4记录的是[211 [192.168.1.3]],虽然编号不一样,但是值是一样的。P2则通过学习可以得到最终的服务列表。
现在P1还是没注册上去,所以再来第三次注册尝试(第二次Paxos计算),现在就它一个注册了:
- P1从全局ID生成器那里获取一个提案编号P1[310]
- P1携带提案编号310向A1 A3 A5发送prepare请求。A1 A3 A5接收到了提案,记录下提案编号310,并保证以后不会再同意编号小于310的提案。因为这里是新的一次Paxos计算,所以这里实际还是第一次接收数据,所以不会返回提案值。
- P1接收到了响应,开始发送access请求[310 [192.168.1.1]]到A1 A3 A5。A1 A3 A5接收了这个提案。
- 其它节点可以通过学习,获得最终的服务列表。
Raft中也有三个角色:
- Leader(领袖):处理客户端交互,一般一次只有一个Leader(如果出现网络分区,可能会出现多个Leader,但不影响最终一致性)
- Follower(群众):默认情况下都是Follower,Leader从Follower中选举出来
- Candidate(候选人):将要被选为Leader的Follower
Raft也分为两个阶段:
- 选举阶段
- 由选举出来的Learder负责和客户端交互,同步各个Follower
整个过程和美国大选类似:
- 选举阶段(选总统)
- Follower在随机等待一段时间后,转换为Candidate,立即先给自己投一票,然后向其它的Follower拉票,Follower进行投票,投票多的那个就是Leader
- 如果出现了两个Candidate票数相同的,那么就再等待一段时间,由这两个Candidate再次拉票,票数多的就是Leader
- 选出来的Leader通过心跳机制和Follower确立leader地位。如果Leader挂了,则重新选出新的Leader(这个Leader的数据必须是最新的)
- Leader阶段(总统处理国事,并公告)
- Leader接收到Client的消息后,此时为Uncommitted状态
- Leader向所有的Follower复制这个消息,并等待Follower的响应
- 当大多数的Follower返回后,Leader返回Client成功响应。此时为Committed状态
- Leader告知Follower,该数据已提交
举例:
还是假设Server(s1 s2)向三个注册中心节点注册(c1 c2 c3),Raft流程如下:
- c1 c2 c3先选举出一个Leader,假设c1胜出
- c1和c2 c3建立心跳,并等待Server注册
- s1发送注册信息,c1接收,然后复制给c2和c3
- s2发送注册信息,c1接收,然后复制给c2和c3。这里相当于已经退化成了单CS架构了
- c1接收到c2 c3成功接收s1消息的响应,告知s1,事务成功
- 但是c2接收s2消息失败,此时可以尝试重试,或者回滚事务
「强一致性」较「最终一致性」可靠性和性能都较差(Paxos由于逻辑复杂性能也不行),但是实现简单,「最终一致性」则反之。方案的选择,视具体情况而定:
- 对于需要强一致性的业务来说,则放弃部分性能,使用强一致性。比如转账业务
- 对于性能要求高,但是数据一致性要求并不是太强的业务,可以使用最终一致性。比如大V发微博
- 《PaxosMadeSimple》http://lamport.azurewebsites.net/pubs/pubs.html#paxos-simple
- 《Base: An Acid Alternative》https://queue.acm.org/detail.cfm?id=1394128
- Wiki二阶段提交https://zh.wikipedia.org/wiki/二阶段提交
- Wiki三阶段提交https://zh.wikipedia.org/wiki/三阶段提交
- Wiki XAhttps://zh.wikipedia.org/wiki/X/Open_XA
- Sagashttps://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf
- CONSENSUS: BRIDGING THEORY AND PRACTICEhttps://ramcloud.stanford.edu/~ongaro/thesis.pdf