3

分布式系统中的一致性问题

 3 years ago
source link: https://www.zenlife.tk/consistent.md
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

2014-07-07

前几天有个面试问到分布式存储中的一致性问题,没答上来。记得很久以前看过一些相关的,现在复习整理一下。

一致性定义

首先,是关于一致性定义。

强一致性,这个是要求最苛刻的一项,很好理解,就是任何条件下去读,读到的都应该是最后成功写入的记录。这个举个例子,没有副本,只能从一个地方读,那么就是强一致性的。

用户一致性,比强一致性弱一些,只要求每个用户看到的是一致的。也就是说,对每个用户,他每次读到的数据版本不会比它上次读到的更旧,但用户之间可以不一致。这个可以举个例子,每个用户从固定的某个副本上读,可以保证用户一致性。比如说A一直读的都是primary,B一直读的是secondary,那么A和B之间不能保证一致(primary和secondary同步需要时间)。但是分别对A和B用户,他们读到数据版本,不会比上一次的旧,这就是用户一致性。

会话一致性,比用户一致性弱一些,它跟用户一致性的区别是,只要求对每个用户,在某个session内的一致性。

最终一致性,这是一个较弱的一致性要求,但属于工业上比较常用的一个一致性标准。就是说客户端只要保持一直的读,在未来某个时刻,最终是能够读到一个一致的数据的。这个举个例子,就好比每个用户随便从primary读或是从secondary上读,只要保持一直读,由于最终primary是会跟secondary同步的,用户最终也能读到某个一致的数据。

弱一致性,概念忘记了。弱一致性好像不怎么实用,因为它太弱啦,弱暴了。

CAP理论,P必须要保障,那么C跟A的冲突。副本使得可用性提高,但也是引入不一致的根本原因。

常用的是三副本。最简单的例子,比如一个master,二个slaver。这就是三份副本,只不过这里master起主导作用,slaver在master挂掉的时候才顶上去。

如果按类型分,可以分为提供数据和提供CPU。提供CPU类型副本的一般是不带状态,所以挂掉后可以直接切换。而提供数据类型副本的,是带状态的,处理起来比较复杂。

至于数据类型副本,也可以再具体划分。如果是机器级别的副本,就好比A是primary,B是secondary,B上的数据跟A上是完全一致的,B是作为A的一个副本。这种数据一般是实现起来比较简单粗暴,但是也有一些问题。首先是数据的利用率低,像A和B这样相当于只有50%的利用率。另一个问题是恢复速度,如果A挂了,那么数据要从B恢复到A,恢复速度慢,B如果停机下来恢复A,服务就不可用了。

另一种是按数据划分的副本,多个机器上都有某块数据的一部分,但每个机器上都不是完整的数据。这样做的优点是,存储利用率高很多,可靠性也提高,但是缺点是,实现起来比机器级的副本要麻烦。

讲到最关键的部分了。分布的存储架构,有两种:中心化和去中心化,以Dynamo和BigTable为代表。

真正做到去中心化实现非常复杂,而且性能低于中心化方案。所以很多情况下,即使去中心化方案中,都是使用paxos协议选主,然后转为中心化方案,用gossip协议维护集群信息。

主副本比如说叫primary,其它副本叫secondary/third等。策略的不同,非primary可以提供服务或者不提供服务。如果是仅有primary能提供服务,就是一种典型的主从结构。而如果非primary也能提供服务,可能就会采用NRW。

先说主从吧,仅有primary提供服务,其它的副本不提供服务,采取一定的措施可以达到强一致性。比如,写入的时候,写所有的secondary/third等副本都成功的条件下,才返回。而如果写其中某个失败,怎么办呢?我们可以将失败的那个打上标记,从副本中移除,然后返回用户请求;等失败的那一个恢复并跟primary完全同步之后,解除标记,再次将它纳入primary的副本管理中。这种做法是能够保证强一致性的,当然,副本处于失败恢复期间系统可靠性降低了,也就是牺牲了可用性A换取一致性C。

接下来说NRM。要是写全部副本成功才返回给用户成功,这种一致性是保证了,但是响应时间长,可用性低了。那么应该如何取折中呢?Quorum就是一种折中方式,我直接把它叫NRW吧,比较好理解。N是指副本的数量,R是成功读副本数量,W是成功写副本数量。业界N一般取3,我们就以N=3为例。NRW中要求R+W>N。这里取R=2 W=2,表示的是,用户写请求,必须至少写成功两份副本,才会返回成功给用户。而用户读的时候,必须要读取两份副本。为什么是R+W>N呢?因为如果发生了一不致,用户能知道出现了副本不同版本差异。比如说写的时候primary成功了,secondary成功了,而third失败了。读取的时候,读到的是secondary和third两个副本,这里用户就能感知到secondary和third版本的不同,secondary版本较新,然后用户决定取舍。总之,是把处理权交给了用户。Dymano中是用了一个叫向量版本的东西。

引起不一致的原因,主要几种:

  • 更新时差,导致secondary落后于primary
  • 脏数据,这种在一定的策略下可能发生
  • 新加入的结点,由于down机恢复或者是加入新机器等

如果这个世界没有错误没有异常发生,那么分布式就不是问题了,是hello world。但是分布式环境下,错误是必然的,所以分布式编程才不好做。举一个常见的问题。

副本切换这边,切换primary不能影响副本一致性,尤其是强一致性。primary切换时保证一致性是一个难点。比如说primary挂了,那么secondary要顶上,可能要充当primary身份。但是也有可能只是primary和secondary之间网络割裂之类的原因,primary并不是真的挂了,然后就会出现双主,问题就大了。顺便说一下,如何发现primary异常呢?一般是使用lease。

再比如说数据同步这边,同步期间系统还是要提供服务,我们要记录下同步点。等同步点完成了,但同步过程中会有新收到的数据,所以还是一个不同步的。继续同步这些数据,然后在这期间又有新来的数据...然后,变成鸡生蛋蛋生鸡无限循环了。

总结是:我懂得太少,说的都只一些皮毛,看完请鄙视的“呵呵”一下就行了...


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK