分布式集群

分布式理论基础总结

2018-08-16  本文已影响0人  TheLudlows_

从在校大二开始到如今参加工作,接触了不少关于分布式的东西。但总是感觉分布式基础理论知识很含糊,不清晰。打算在这一周里梳理下相关的知识线路。

CAP理论

CAP理论又称CAP定理,指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。


CAP

三个特性进行了如下归纳:

CAP原理告诉我们,这三个因素最多只能满足两个,不可能三者兼顾。对于分布式系统来说,分区容错是基本要求,所以必然要放弃一致性。对于大型网站来说,分区容错和可用性的要求更高,所以一般都会选择适当放弃一致性。对应CAP理论,NoSQL追求的是AP,而传统数据库追求的是CA,这也可以解释为什么传统数据库的扩展能力有限的原因。

在CAP三者中,“可扩展性”是分布式系统的特有性质。分布式系统的设计初衷就是利用集群多机的能力处理单机无法解决的问题。当需要扩展系统性能时,一种做法是优化系统的性能或者升级硬件(scale up),一种做法就是“简单”的增加机器来扩展系统的规模(scale out)。好的分布式系统总在追求”线性扩展性”,即性能可以随集群数量增长而线性增长。

CAP定律其实也是衡量分布式系统的重要指标,另一个重要的指标是性能。

BASE理论

BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写,BASE是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的结论,是基于CAP定理逐步演化而来的,其核心思想是即使无法做到强一致性(Strong consistency),但每个应用都可以根据自身的业务特点,采用适当的方式来使系统达到最终一致性(Eventual consistency)。

一致性模型

数据一致性来说,简单说有三种类型,当然,如果细分的话,还有很多一致性模型,如:顺序一致性,FIFO一致性,会话一致性,单读一致性,单写一致性等。下面介绍主要的三种一致性模型:

从这三种一致型的模型上来说,我们可以看到,Weak和Eventually一般来说是异步冗余的,而Strong一般来说是同步冗余的(多写),异步的通常意味着更好的性能,但也意味着更复杂的状态控制。同步意味着简单,但也意味着性能下降。

以及其他变体:

Quorum W+R>N和vector clock

这三个因素决定了可用性,一致性和分区容错性。W+R>N可以保证数据的一致性(C),W越大数据一致性越高。这个NWR模型把CAP的选择权交给了用户,让用户自己在功能,性能和成本效益之间进行权衡。

配置的时候要求W+R > N。 因为W+R > N, 所以 R > N-W 这个是什么意思呢?就是读取的份数一定要比总备份数减去确保写成功的备份的差值要大。

对于一个分布式系统来说,N通常都大于3,也就说同一份数据需要保存在三个以上不同的节点上,以防止单点故障。W是成功写操作的最小节点数,这里的写成功可以理解为“同步”写,比如N=3,W=1,那么只要写成功一个节点就可以了,另外的两份数据是通过异步的方式复制的。R是成功读操作的最小节点数,读操作为什么要读多份数据呢?在分布式系统中,数据在不同的节点上可能存在着不一致的情况,继续往下看。

NWR模型的一些设置会造成脏数据的问题,因为这很明显不是像Paxos一样是一个强一致的东西,所以,可能每次的读写操作都不在同一个结点上,于是会出现一些结点上的数据并不是最新版本,但却进行了最新的操作。也就是说,如果你读出来数据的版本是v1,当你计算完成后要回填数据后,却发现数据的版本号已经被人更新成了v2,那么服务器就会拒绝你,这就是版本冲突问题。

于是,Amazon的Dynamo引入了Vector Clock(矢量钟)这个设计。这个设计让每个结点各自记录自己的版本信息,也就是说,对于同一个数据,需要记录两个事:1、谁更新的我,2、我的版本号是什么

看一个操作序列:

  1. 对节点A的D数据进行写操作。数据D会增加一个版本信息(A,1)。我们把这个时候的数据记做D1(A,1)。

  2. 然后继续对数据节点A的D数据进行写操作处,于是对节点A的D数据记录为D2(A,2)。这个时候,D2是可以覆盖D1的,不会有冲突产生。

  3. 现在我们假设D2传播到了所有节点(B和C),B和C收到的数据不是从客户产生的,而是别人复制给他们的,所以他们不产生新的版本信息,所以现在B和C所持有的数据还是D2(A,2)。于是A,B,C上的数据及其版本号都是一样的。

  4. 如果我们有一个新的写请求到了B结点上,于是B结点生成数据D3(A,2; B,1),意思是:数据D全局版本号为3,A升了两新,B升了一次。这就是所谓的代码版本的log

  5. 如果D3没有传播到C的时候又一个请求被C处理了,于是,以C结点上的数据是D4(A,2; C,1)

  6. 最精彩的事情来了:如果这个时候来了一个读请求,我们要记得,我们的W=1 那么R=N=3,所以R会从所有三个节点上读,此时,他会读到三个版本:

    • A结点:D2(A,2)
    • B结点:D3(A,2; B,1);
    • C结点:D4(A,2; C,1)
  7. 这个时候可以判断出,D2已经是旧版本(已经包含在D3/D4中),可以舍弃。

  8. 但是D3和D4是明显的版本冲突。于是,交给调用方自己去做版本冲突处理。就像源代码版本管理一样。

Dynamo的配置用的是CAP里的A和P。Vector Clock(Version Vector)只能用于发现数据冲突,但是想要解决数据冲突还要留给用户去定夺(就好比git commit出现conflicts,需要手工解决一样),当然也可以设置某种策略来直接解决冲突(保留最新或集群内多数表决)。

lease机制

一般描述如下:

通俗解释一下:

即Lease是一种带期限的契约,在此期限内拥有Lease的节点有权利操作一些预设好的对象。从更深 层次上来看,Lease就是一把带有超时机制的分布式锁,如果没有Lease,分布式环境中的锁可能会因为锁拥有者的失败而导致死锁,有了lease死锁会被控制在超时时间之内。

一般的应用:

双主问题

如果有3副本A、B、C,并通过中心结点M来管理。A B C互主副本。其中A节点为primary,且同一时刻只能有一个primary节点处理方法是在每个副本与中心节点M中维护一个心跳,期望通过心跳是否存在而判断对方是否依旧存活。心跳方法其实根本无法解决分布式下的这个问题。考虑如下场景:

lease(租期、承诺)机制就是用来解决这类问题的:

由中心节点向M其他节点发送lease,若某个节点持有有效的lease,则认为该节点正常可以提供服务。节点 A、B、C 依然周期性的发送heart beat报告自身状态,节点M收到heart beat后发送一个lease,表示节点M确认了节点 A、B、C 的状态,并允许节点在 lease 有效期内正常工作。节点M可以给 primary节点一个特殊的lease,表示节点可以作为primary工作。一旦节点M希望切换新的primary,则只需等前一个primary的lease过期,则就可以安全的颁发新的lease给新的primary节点,从而不会出现“双主”问题。实际工程实现时要考虑primary的lease过期的时间。

在实际系统中,若用一个中心节点发送lease也有很大的风险,一旦该中心节点宕机或网络异常,则所有的节点没有lease,从而造成系统高度不可用。为此,实际系统总是使用多个中心节点互为副本,成为一个小的集群,该小集群具有高可用性,对外颁发lease的功能。

读锁/写锁(分布式锁)

master和slave模型缓存系统中,其中master负责少量的读、所有的写和同步操作,slave负责读操作,如何保证读到缓存的一致性?ps(这种情况不适用于强一致性的应用)

当读请求到来时,m节点在向各s节点发送数据时同时向节点颁发一个lease,一旦真实时间超过这个时间点,则lease过期失效。在lease的有效期内,s保证不会修改对应数据的值。因此,节点收到数据和lease后,将数据加入本地Cache,一旦对应的lease超时,节点将对应的本地cache数据删除。m在修改数据时,首先阻塞所有写的读请求,并等待之前为该数据发出的所有lease超时过期,然后修改数据的值。之后重复上面的工作。

上述等lease失效的过程中,可能有新的请求请求到达,这时s又会继续颁发新的lease,使得lease一直不结束,形成“活锁”,即修改请求等待lease失效,而又源源不断颁发新lease而一直无法完成。此问题形成了“活锁”

一致性哈希

传统hash(x) % N算法的弊端:不利于架构的伸缩性,我们为每个节点都增加一个备用节点,当某个节点失效时,就自动切换到备用节点上,类似于数据库的master和slave。但是依然无法解决增加或删除节点后,需要做hash重分布的问题,也就是无法动态增删节点。

这时就引入了一致性hash的概念 ,将所有的节点分布到一个hash环上,每个请求都落在这个hash环上的某个位置,只需要按照顺时针方向找到的第一个节点,就是自己需要的服务节点。当某个节点发生故障时,只需要在环上找到下一个可用节点即可。


Hash 环

虚拟节点:每个虚拟节点都有一个对应的物理节点,而每个物理节点可以对应若干个虚拟节点。并且这些虚拟节点连续的分配在环上,这样就能抑制分布不均匀,最大限度地减小服务器增减时的缓存重新分布。

一篇不错的一致性哈希文章

敬请期待系列文章

上一篇 下一篇

猜你喜欢

热点阅读