分布式系统原理概述
基础概念
网络调用3态
网络RPC调用3种结果:成功,失败,超时。一旦发生超时后,无法确认此次调用是成功还是失败。
CAP
CAP 理论的定义比较简单,CAP 三个字母分别代表了分布式系统中三个相互矛盾的属性:
- Consistency:一致性,读操作总是能够读取之前完成的写操作结果,满足这个条件我们称之为强一致性系统。
- Availiablity:可用性,读写操作单台机器发生故障的情况下仍然能够正常执行,不需要等待发生故障的机器重启或者该机器上面的服务前移到其他机器。
- Tolerance to the partition of network:分区可容忍性,机器故障,网络故障,机房停电等异常情况下仍然满足一致性和可用性。
CAP理论指出:无法设计一种分布式协议,使得同时完全具备 CAP 三个属性,即 1.该种协议下的副本始终是强一致性;2.服务始终是可用的;3.协议可以容忍任何网络分区异常;分布式系统协议只能在 CAP 这三者间进行权衡。
CAP的C vs ACID的C
CAP和ACID虽然不是一个层面上的概念,前者说的是分布式系统中的普遍规律,后者说的传统关系型数据库内事务要符合的性质。但是笔者曾经混淆过两者之间的C,所以再复习下。
ACID(Atomic,Consistent,Isolated,Durable)的在维基百科是这样定义的:
- 原子性:一个事务(transaction)中的所有操作,要么全部完成,要么全部不完成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
- 一致性:在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表示写入的资料必须完全符合所有的默认规则,这包含资料的精确度、串联性以及后续数据库可以自发性地完成预定的工作。
- 隔离性:当两个或者多个事务并发访问(此处访问指查询和修改的操作)数据库的同一数据时所表现出的相互关系。事务隔离分为不同级别,包括读未提交(Read Uncommitted)、读提交(Read Committed)、可重复读(Repeatable Read)和串行化(Serializable)。
- 持久性:在事务完成以后,该事务对数据库所作的更改便持久地保存在数据库之中,并且是完全的。
从上面的定义可以看出,CAP的C强调机器之间的副本要一致,ACID的C强调数据库的完整性没有被破坏,写入的数据符合数据库的定义约束。
副本一致性
为了提升系统可能性,唯一的方法就是冗余。无论是计算,存储,网络等都需要使用一定程度的冗余策略。这里主要讲下存储的冗余,也就是我们通常所说的副本。
副本指在分布式系统中为数据或者服务提供的冗余。副本一致性是针对分布式系统而言的。备份和低一致性来提升可用性和网络分区容忍性。
根据一致性的级别,可以粗略分为如下几种:
- 强一致性(strong consistency):任何时刻任何用户或节点都可以读到最近一次成功更新的副本数据。强一致性是程度最高的一致性要求,也是实践中最难以实现的一致性。
- 单调一致性(monotonic consistency):任何时刻,任何用户一旦读到某个数据在某次更新后的值,这个用户不会再读到比这个值更旧的值。单调一致性是弱于强一致性却非常实用的一种一致性级别。因为通常来说,用户只关心从己方视角观察到的一致性,而不会关注其他用户的一致性情况。
- 最终一致性(eventual consistency):最终一致性要求一旦更新成功,各个副本上的数据最终将达到完全一致的状态,但达到完全一致状态所需要的时间不能保障。对于最终一致性系统而言,一个用户只要始终读取某一个副本的数据,则可以实现类似单调一致性的效果,但一旦用户更换读取的副本,则无法保障任何一致性。
- 弱一致性(week consistency):一旦某个更新成功,用户无法在一个确定时间内读到这次更新的值,且即使在某个副本上读到了新的值,也不能保证在其他副本上可以读到新的值。弱一致性系统一般很难在实际中使用,使用弱一致性系统需要应用方做更多的工作从而使得系统可用。
副本控制协议
从上个章节可以看到,多个副本之间存在数据一致性的问题。那么该如何解决呢?常用的方法就是使用中心化副本控制协议(primary-secondary或primary-backup)。在该协议中,副本被分成两大类:primary副本,非primary副本,简称为secondary副本。维护primary副本的节点作为中心节点,中心节点负责维护数据的更新,并发控制和协调副本的一致性。
要解决数据一致性问题,则需要解决如下几个问题:数据更新流程,数据读取流程,primary副本的确定和切换,数据同步。
1.数据更新流程
- 外部节点把更新操作发给primary节点
- primary节点确定并发更新操作的先后顺序
- primary节点将更新操作发送给secondary节点
- primary节点根据secondary节点的完成情况决定更新操作是否成功,并将结果返回给外部节点。
如果由primary直接同时想其他N个副本的发送数据的话,那么每个secondar的更新吞吐受限于primary出口带宽的限制。所以,有些系统会使用副本接力的方式,比如primary发送个第一个secondary副本数据,第一个secondary副本发送第二个secondary数据,依次类推。
2.数据读取流程数据
数据读取有两种不同思路:
- 确定primary副本,将数据分为数据段,以数据段为副本的基本单位,将副本分散到集群中。只要primary副本分散到集群中,即使只有primary副本提供读写服务,也可以充分利用集群机器资源。
- 主副本记录secondary是否可用。如果不可用,那么secondary副本可以继续尝试与primary副本同步数据,当完成数据同步时,primary副本将其标记位可用。
3.primary副本的确定和切换
在primary-secondary类型的协议中,另一个核心的问题是如何确定primary副本,尤其是在原primary副本所在机器出现宕机等异常时,需要有某种机制切换primary副本,使得某个secondary副本成为新的primary副本。 通常的,在primary-secondary类型的分布式系统中,哪个副本是 primary 这一信息都属于元信息,由专门的元数据服务器维护。执行更新操作时,首先查询元数据服务器获取副本的primary信息,从而进一步执行数据更新流程。
既然primary副本需要元数据服务器去维护,就会存在如下问题,如何确定节点的状态以及发现原primary节点异常;切换primary后,不能影响副本一致性。 而且由于primary切换会带来一定的停服时间。
通常可以使用Lease机制来确定节点状态
4.数据同步。
数据不一致的形式有3种:
- 备副本完全落后主副本数据(新增副本节点),
- 备副本部分落后主副本数据(网络分化等原因),
- 存在冗余副本数据(主副本没有更新,备副本反而进行了多余的修改操作)。
对应的3种解决方法分别是 :复制快照数据; 回放redo log;基于undo log 删除多余数据。
在选择副本复制时机时,又可以分为两种:强同步复制和异步复制,两者的区别在于用户的请求是否在数据复制到其他副本上才返回成功。
强同步复制:常见的做法是同步操作日志。主副本首先将操作日志同步到备副本,备副本回放操作日志,完成后通知主副本。接着,主副本修改本机,等到所有的操作都完成后再通知客户端写成功。比较类似于数据更新流程表达的内容。
正如CAP理论所讲,一致性和可用性是矛盾的。强同步的问题的当备副本出现故障时,可能阻塞存储系统的正常写服务,可用性较差。异步复制协议的可用性较好,但是一致性得不到保障,主副本出现故障是还有数据丢失的可能。
分布式原理
一致性哈希
在常见的分布式缓存系统中,有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均分发到每台服务器上,每台机器负责1/N的服务。
常用的简单算法是:
- 对机器从0到N-1进行编号
- 计算key的hashcode,然后对hashcode进行求余计算:i=key.hashcode mod N
- 然后客户端请求分发到编号为i的机器
但这样的算法方法存在严重问题:当服务器宕机或者增加新的机器后,大量缓存需要重新计算,分配到新的服务器上。这样会导致在缓存迁移过程中,缓存大量失效,请求压力会大量透传到后端业务系统中。可能导致业务系统不稳定。
一致哈希算法来避免这样的问题。 一致哈希尽可能使同一个资源映射到同一台缓存服务器。这种方式要求增加一台缓存服务器时,新的服务器尽量分担存储其他所有服务器的缓存资源。减少一台缓存服务器时,其他所有服务器也可以尽量分担存储它的缓存资源。
一致哈希算法的主要思想是将每个缓存服务器与一个或多个哈希值域区间关联起来,其中区间边界通过计算缓存服务器对应的哈希值来决定。如果一个缓存服务器被移除,则它会从对应的区间会被并入到邻近的区间,其他的缓存服务器不需要任何改变。
一致哈希算法的过程是:
- 求出每个服务节点的hash,并将其配置到一个0~232的圆环(continuum)区间上。
- 使用同样的方法求出你所需要存储的key的hash,也将其配置到这个圆环(continuum)上。
- 从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务节点上。如果超过232仍然找不到服务节点,就会保存到第一个服务节点上。
虚拟节点(virtual nodes): 之所以要引进虚拟节点是因为在服务器较少的情况下,通过hash(key)算出节点的哈希值在圆环上并不是均匀分布的(稀疏的),仍然会出现各节点负载不均衡的问题。比如说只有3台服务器,当加入一个后,在NODE0 和 NODE2中间加入一个NODE3节点;NODE0的原先的请求压力被NODE3分摊了一半,但是NODE1和NODE2的压力一点变化都没有。
为了解决圆环上节点不均匀的问题,可以将每台物理机对应一组虚拟机器。将虚拟机器的hash值放置到圆环上,key在 圆环上先找到虚拟机器节点,然后根据虚拟机器节点找到物理机器节点。
虚拟节点可以认为是实际节点的复制品(replicas),本质上与实际节点实际上是一样的(key并不相同)。引入虚拟节点后,通过将每个实际的服务器(节点)数按照一定的比例(例如200倍)扩大后并计算其hash(key)值以均匀分布到圆环上。
两阶段提交
两阶段提交:该协议通常用来实现分布式事务。系统一般包含两类节点:一类是协调者(coordinator),通常一个系统只有一个;另一类是事务参与者(participants),一般包含多个。协议中假设每个节点都会记录操作日志并持久化到非易失性存储介质。
它分成两个阶段,称之为准备阶段,提交阶段;也可以理解为投票,确认过程:
- 准备阶段(Prepare Phrase):
- 协调者会问所有的参与者结点,是否可以执行提交操作。
- 各个参与者开始事务执行的准备工作:如:为资源上锁,预留资源,写undo/redo log……。
- 参与者响应协调者,如果事务的准备工作成功,则回应“可以提交”,否则回应“拒绝提交”。
- 确认阶段(Confirm Phrase):
- 如果所有的参与者都回应“可以提交”,那么,协调者向所有的参与者发送“正式提交”的命令。
- 参与者完成正式提交,并释放所有资源,然后回应“完成”,
- 协调者收集各结点的“完成”回应后结束这个Global Transaction。
- 如果有一个参与者回应“拒绝提交”,那么,协调者向所有的参与者发送“回滚操作”,并释放所有资源.
- 参与者然后回应“回滚完成”,协调者收集各结点的“回滚”回应后,取消这个Global Transaction。
异常流程的话,可以参考酷壳的这篇文章分布式系统的事务处理以及《分布式系统原理介绍》电子书的相应章节介绍。
另外,需要补充说明的是,在一月份的《程序员》杂志中,介绍了阿里使用TCC(Try-Confirm-Cancel)机制来实现分布式事务。即使在极端情况下,也可以使用事务补充和人工介入的方式来完成事务一致性。
PAXOS
多个节点直接通过操作日志同步数据,如果只有一个节点称为主节点,就很容易在多个节点之间维护数据一致性。然后主节点可能出现故障,那么就需要选出主节点。Paxos协议就是用于解决多个节点之间的一致性问题。
PAXOS协议的角色分为 proposers,acceptors,和 learners(允许一个节点身兼数职)。proposers 提出提案,提案信息包括提案编号和提案内容(value);acceptor 收到提案后可以接受(accept)提案,若提案获得多数 acceptors 的接受,则称该提案被批准(chosen),被通过后的提案我们称之为决议;learners 只能学习到被批准的提案。
通过一个决议分为两个阶段:
- 准备阶段:
- proposer选择一个提案序号n并将prepare 请求发送给其他acceptors ;
- acceptor收到prepare消息后,如果提案的编号大于它已经回复的所有prepare消息,则acceptor将自己上次接受的提案回复给proposer,并承诺不再回复小于n的提案;
- 批准阶段:
- 当一个proposer收到了多数acceptors对prepare的回复后,就进入批准阶段。
- 如果在之前的prepare阶段acceptor回复了上次接受的提议,那么,proposer选择其中序号最大的提议值发送个acceptor批准;否则proposer生成一个新的提议值发给acceptors批注
- acceptor在不违背自己向其他proposer的承诺的前提下,acceptor在收到accept请求后即接受这个请求。
在上述prepare过程中,如果一个acceptor发现存在一个更高编号的提案,则需要通知proposer,提醒其中断这次提案。
其他
Lease:中文叫租约,通常定义为:颁发者在一定期限内給予持有者一定权利的协议。Lease 表达了颁发者在一定期限内的承诺,只要未过期颁发者必须严格遵守lease约定的承诺。Lease 的持有者在期限内使用颁发者的承诺,但lease一旦过期必须放弃使用或者重新和颁发者续约。打个比方,Lease就是租房时候的承诺。要续租的话,提前一个星期把房租打给房东。房东答应你在你租房的这段时间内,房子不会被别人适用。同时,你也不需要频繁的问房东,房子是否现在只有我一个人使用。详细介绍见Lease 机制在分布式系统中的应用
MVCC:Multiversion concurrency control,多版本并发控制。本质是使用COW+Version+CAS这个技术组合来提升系统并发性。详细介绍见多版本并发控制(MVCC)在分布式系统中的应用
参考
- 《深入分析Java Web 技术内幕》
- 《大规模分布式存储系统原理解析与架构实战》
- 《大型网站技术架构核心原理与案例分析》
- 《分布式系统原理介绍》
- 《大规模Web服务开发技术》
- 《面向模式的软件架构第4卷分布式计算的模式语言》
- 《程序员》 2014 第一期
- 分布式系统的事务处理
- Paxos算法
- Explanation of BASE terminology
- Lease 机制在分布式系统中的应用
- 详解Consistent Hashing算法
- 一致性hash算法伪码
blog comments powered by Disqus