[转载]分布式系统设计一——基本概念

1 设计分布式系统需要考虑的异常

1.1 节点宕机
1.2 网络异常

消息丢失、消息乱序(可以通过增加序列号解决)以及数据错误

1.3 分布式系统三态

在单机,只要不宕机,一个函数执行要么成功、要么失败,但是分布式系统共下存在成功、失败和超时。下面解释一下超时的不确定性:

某个节点 A 向另一个节点 B 发送一个消息,如果请求 RPC 的节点 A 在给定的时间内没有收到执行 RPC 的节点 B 返回的消息,则认为该操作“超时”。对于超时的请求,我们无法获知该请求是否被节点 B 成功执行了。这是因为,如果超时是由于节点 A 发向节点 B 的请求消息丢失造成的,则该操作肯定没有被节点 B 成功执行;但如果节点 A 成功的向节点 B 发送了请求消息,且节点 B 也成功的执行了该请求,但节点 B 发向节点 A 的结果消息被网络丢失了或者节点 B 在执行完该操作后立刻宕机没有能够发出结果消息,从而造成从节点 A 看来请求超时。所以一旦发生超时,请求方是无法获知 RPC 的执行结果的。

超时解决方法:

A 当出现“超时”时,可以通过发起读取数据的操作以验证 RPC 是否成功 (例如银行系统的做法)。

B 设计分布式协议时将执行步骤设计为可重试的,即具有所谓的“幂等性”。例如覆盖写就是一种常见的幂等性操作,因为重复的覆盖写最终的结果都相等。如果使用 可重试的设计,当出现“失败”和“超时”时,一律重试操作直到“成功”。这样,即使超时的操作实际上已经成功了,重试操作也不会对正确性造成影响,从而简 化了设计

1.4 存储数据丢失

一般是硬盘机械故障。

2 数据分布方式

2.1 哈希方式

哈希方式是最常见的数据分布方式,其方法是按照数据的某一特征计算哈希值,并将哈希值与机器中的机器建立映射关系,从而将不同哈希值的数据分布到不同的机器上。

优点:

哈希方式需要记录的元信息也非常简单,任何时候,任何节点只需要知道哈希函数的计算方式及模的服务器总数就可以计算出处理具体数据的机器是哪台。

缺点:

A 突出表现为可扩展性不高,一旦集群规模需要扩展,则几乎所有的数据需要被迁移并重新分布

B 一旦某数据特征值的数据严重不均,容易出现“数据倾斜”(data skew)问题,只能重新选择需要哈希的数据特征,例如选择用户 id 与另一个数据维度的组合作为哈希函数的输入。

2.2 按数据范围分布

将数据按特征值的值域范围划分为不同的区间,使得集群中每台(组)服务器处理不同区间的数据。

优点:

使用范围分布数据的方式的最大优点就是可以灵活的根据数据量的具体情况拆分原有数据区间,拆分后的数据区间可以迁移到其他机器,一旦需要集群完成负 载均衡时,与哈希方式相比非常灵活。另外,当集群需要扩容时,可以随意添加机器,而不限为倍增的方式,只需将原机器上的部分数据分区迁移到新加入的机器上 就可以完成集群扩容。

缺点:

按数据范围分布数据需要记录所有的数据分布情况。一般的,往往需要使用专门的服务器在内存中维护数据分布信息,称这种数据的分布信息为一种元信息。

2.3 按数据量分布

优点:

由于与具体的数据内容无关,按数据量分布数据的方式一般没有数据倾斜的问题,数据总是被均匀切分并分布到集群中。当集群需要重新负载均衡时,只需通过迁移数据块即可完成。集群扩容也没有太大的限制,只需将部分数据库迁移到新加入的机器上即可以完成扩容。

缺点:

需要管理较为复杂的元信息,与按范围分布数据的方式类似,当集群规模较大时,元信息

的数据量也变得很大,高效的管理元信息成为新的课题。

2.4  一致性哈希

一致性哈希(consistent hashing)是另一个种在工程中使用较为广泛的数据分布方式。一致性哈希最初在 P2P 网络中作为分布式哈希表(DHT)的常用数据分布算法。一致性哈希的基本方式是使用一个哈希函数计算数据或数据特征的哈希值,令该哈希函数的输出值域为一 个封闭的环,即哈希函数输出的最大值是最小值的前序。将节点随机分布到这个环上,每个节点负责处理从自己开始顺时针至下一个节点的全部哈希值域上的数据。

例 2.1.3:某一致性哈希函数的值域为[0, 10),系统有三个节点 A、B、C,这三个节点处于的一致性哈希的位置分别为 1,4,9,则节点 A 负责的值域范围为[1,4),节点 B 负责的范围为[4, 9),节点 C 负责的范围为[9, 10)和[0, 1)。若某数据的哈希值为 3,则该数据应由节点 A 负责处理。 图  2-4给出了这个例子的示意图。

wps_clip_image-18051

哈希分布数据的方式在集群扩容时非常复杂,往往需要倍增节点个数,与此相比,一致性哈希的优点在于可以任意动态添加、删除节点,每次添加、删除一个节点仅影响一致性哈希环上相邻的节点。

例 2.1.4:假设需要在例 2.1.3 中增加一个新节点 D,为 D 分配的哈希位置为 3,则首先将节点A 中[3, 4)的数据从节点 A 中拷贝到节点 D,然后加入节点 D 即可

优点:

使用一致性哈希的方式需要将节点在一致性哈希环上的位置作为元信息加以管理,这点比直接使用哈希分布数据的方式要复杂。然而,节点的位置信息只于集群中的机器规模相关,其元信息的量通常比按数据范围分布数据和按数据量分布数据的元信息量要小很多。

缺点:

随机分布节点的方式使得很难均匀的分布哈希值域,尤其在动态增加节点后,即使原先的分布均匀也很难保证继续均匀,由此带来的另一个较为严重的缺点是,当一个节点异常时,该节点的压力全部转移到相邻的一个节点,当加入一个新节点时只能为一个相邻节点分摊压力。

2.5  数据分布方式的选择

在实际工程实践中,可以根据需求及实施复杂度合理选择数据分布方式。另外,上述数据分布方式是如果可以灵活组合使用,往往可以兼备各种方式的优点,收到较好的综合效果。

2.6  工程投影

wps_clip_image-3230

 
 

3 副本

3.1 概念

副本(replica/copy) 指在分布式系统中为数据或服务提供的冗余。对于数据副本指在不同的节点上持久化同一份数据,当出现某一个节点的存储的数据丢失时,可以从副本上读到数据。 数据副本是分布式系统解决数据丢失异常的唯一手段。另一类副本是服务副本,指数个节点提供某种相同的服务,这种服务一般并不依赖于节点的本地存储,其所需 数据一般来自其他节点。

3.2  基本副本协议

3.2.1  中心化副本控制协议

中心化副本控制协议的基本思路是由一个中心节点协调副本数据的更新、维护副本之间的一致性。图  2-7 给 出了中心化副本协议的通用架构。中心化副本控制协议的优点是协议相对较为简单,所有的副本相关的控制交由中心节点完成。并发控制将由中心节点完成,从而使 得一个分布式并发控制问题,简化为一个单机并发控制问题。所谓并发控制,即多个节点同时需要修改副本数据时,需要解决“写写”、“读写”等并发冲突。单机 系统上常用加锁等方式进行并发控制。对于分布式并发控制,加锁也是一个常用的方法,但如果没有中心节点统一进行锁管理,就需要完全分布式化的锁系统,会使 得协议非常复杂。中心化副本控制协议的缺点是系统的可用性依赖于中心化节点,当中心节点异常或与中心节点通信中断时,系统将失去某些服务(通常至少失去更 新服务),所以中心化副本控制协议的缺点正是存在一定的停服务时间。

 

3.2.2  primary-secondary 协议

在 primary-secondary 类型的协议中,副本被分为两大类,其中有且仅有一个副本作为 primary 副本,除 primary 以外的副本都作为 secondary 副本。维护 primary 副本的节点作为中心节点,中心节点负责维护数据的更新、并发控制、协调副本的一致性。

Primary-secondary 类型的协议一般要解决四大类问题:数据更新流程、数据读取方式、Primary副本的确定和切换、数据同步(reconcile)。

数据更新基本流程

Primary-secondary 协议的数据更新流程

1.  数据更新都由 primary 节点协调完成。

2.  外部节点将更新操作发给 primary 节点

3.  primary 节点进行并发控制即确定并发更新操作的先后顺序

4.  primary 节点将更新操作发送给 secondary 节点

5.  primary 根据 secondary 节点的完成情况决定更新是否成功并将结果返回外部节点

其中第 4 步 primary 节点将更新操作发送到 secondary 节点时,往往发送的也是更新的数据。在工程实践中,如果由 primary 直接同时发送给其他 N 个副本发送数据,则每个secondary 的更新吞吐受限于 primary 总的出口网络带宽,最大为 primary 网络出口带宽的 1/N。为了解决这个问题,有些系统(例如,GFS),使用接力的方式同步数据,即 primary 将更新发送给第一个 secondary 副本,第一个 secondary 副本发送给第二 secondary 副本,依次类推。由于异常,第 4步可能在有些副本上成功,有些副本上失败,在有些副本上超时。不同的副本控制协议对于第 4 步异常的处理都不一样。

数据读取方式

数据读取方式是 primary-secondary 类 协议需要解决的第二个问题。与数据更新流程类似,读取方式也与一致性高度相关。如果只需要最终一致性,则读取任何副本都可以满足需求。如果需要会话一致 性,则可以为副本设置版本号,每次更新后递增版本号,用户读取副本时验证版本号,从而保证用户读到的数据在会话范围内单调递增。使用 primary-secondary 比较困难的是实现强一致性。

这里简单讨论 primary-secondary 实现强一致性的几种思路。

第一、由于数据的更新流程都是由 primary 控制的,primary 副本上的数据一定是最新的,所以如果始终只读 primary 副本的数据,可以实现强一致性。

第二、由 primary 控制节点 secondary 节点的可用性。当 primary 更新某个 secondary 副本不成功时,primary 将该 secondary 副本标记为不可用,从而用户不再读取该不可用的副本。不可用的secondary 副本可以继续尝试与 primary 同步数据,当与 primary 完成数据同步后,primary 可以副本标记为可用。这种方式使得所有的可用的副本,无论是 primary 还是 secondary 都是可读的,且在一个确定的时间内,某 secondary 副本要么更新到与 primary 一致的最新状态,要么被标记为不可用,从而符合较高的一致性要求。这种方式依赖于一个中心元数据管理系统,用于记录哪些副本可用,哪些副本不可用。某种意义上,该方式通过降低系统的可用性来提高系统的一致性。

第三、基于 Quorum 机制,后面详细说quorum机制。

primary 副本的确定与切换

在 primary-secondary 类型的协议中,另一个核心的问题是如何确定 primary 副本,尤其是在原primary 副本所在机器出现宕机等异常时,需要有某种机制切换 primary 副本,使得某个 secondary副本成为新的 primary 副本。

这边提出两个问题:

第一、如何确定节点的状态以发现原 primary 节点异常

第二、切换 primary后,不能影响副本的一致性。尤其是提供较强一致性服务的系统,切换 primary 的影响更是需要控制。要达到这个目的,一种直观的方式是切换的新 primary 的副本数据必须与原 primary 的副本一致。然而在原 primary 已经发送宕机等异常时,如何确定一个 secondary 副本使得该副本上的数据与原primary 一致又成为新的问题。

数据同步

Primary-secondary 型协议一般都会遇到 secondary 副本与 primary 不一致的问题。此时,不一致的 secondary 副本需要与 primary 进行同步(reconcile)。

通常不一致的形式有三种:一、由于网络分化等异常,secondary 上的数据落后于 primary 上的数据。二、在某些协议下,secondary 上的数据有可能是脏数据,需要被丢弃。所谓脏数据是由于primary 副本没有进行某一更新操作,而 secondary 副本上反而进行的多余的修改操作,从而造成secondary 副本数据错误。三、secondary 是一个新增加的副本,完全没有数据,需要从其他副本上拷贝数据。

对于第一种 secondary 数据落后的情况,常见的同步方式是回放 primary 上的操作日志(通常是redo 日志),从而追上 primary 的更新进度。对于脏数据的情况,较好的做法是设计的分布式协议不产生脏数据。如果协议一定有产生脏数据的可能,则也应该使得产生脏数据的概率降到非常低得情况,从而一旦发生脏数据的情况可以简单的直接丢弃有脏数据的

副本,这样相当于副本没有数据。另外,也可以设计一些基于 undo 日志的方式从而可以删除脏数据。如果 secondary 副本完全没有数据,则常见的做法是直接拷贝 primary 副本的数据,这种方法往往比回放日志追更新进度的方法快很多。但拷贝数据时 primary 副本需要能够继续提供更新服务,这就要求 primary 副本支持快照(snapshot)功能。即对某一刻的副本数据形成快照,然后拷贝快照,拷贝完成后使用回放日志的方式追快照形成后的更新操作。

3.2.3 去中心化副本控制协议

去中心化副本控制是另一类较为复杂的副本控制协议。与中心化副本系统协议最大的不同是,去中心化副本控制协议没有中心节点,协议中所有的节点都是完全对等的,节点之间通过平等协商达到一致。从而去中心化协议没有因为中心化节点异常而带来的停服务等问题。

然而,没有什么事情是完美的,去中心化协议的最大的缺点是协议过程通常比较复杂。尤其当去中心化协议需要实现强一致性时,协议流程变得复杂且不容易理解。由于流程的复杂,去中心化协议的效率或者性能一般也较中心化协议低。一个不恰当的比方就是,中心化副本控制协议类似专制制度,系统效率高但高度依赖于中心节点,一旦中心节点异常,系统受到的影响较大;去中心化副本控制协议类似民主制度,节点集体协商,效率低下,但个别节点的异常不会对系统总体造成太大影响。

3.3 工程中的应用

GFS

GFS 系统的副本控制协议是典型的 Primary-Secondary 型协议,Primary 副本由 Master 指定,Primary 副本决定并发更新操作的顺序。虽然在 GFS 中,更新操作的数据由客户端提交,并在各个副本之间流式的传输,及由上一个副本传递到下一个副本,每个副本都即接受其他副本的更新,也向下更新另一个副本,但是数据的更新过程完全是由 primary 控制的,所以也可以认为数据是由primary 副本同步到 secondary 副本的

Chubby/Zookeeper

Chubby和 Zookeeper 使用了基于 Paxos 的去中心化协议选出 primary 节点,但完成 primary节点的选举后,这两个系统都转为中心化的副本控制协议,即由 primary 节点负责同步更新操作到secondary 节点。

 

 

转载请注明出处 jacksu
开源文档:  redis源码解析
开源库:  github链接
个人小站:  jack's blog

posted @ 2015-10-29 14:12  Jingle Guo  阅读(214)  评论(0编辑  收藏  举报