简单聊聊分布式系统的一致性问题

记得有人说过,分布式系统的所有问题归根结底都是一致性问题。前面文章提到的数据复制,分区以及事务面临的问题都是如何保证数据一致。而分布式系统不同于单机,它不仅会面临着宕机,他们唯一的交流方式就是网络,而网络是不可靠的,也因此带来了时间的不可靠。在分布式系统里,与一致性类似的是consensus的概念,consensus用大白话来说,就是所有的节点都认可同一件事。

在之前的事务可以使得传统的数据库对应用代码隐藏了影响一致性的问题,但是对于分布式系统如何保证一致性呢?在不考虑延迟的情况下,无论是single-leader,multi-leader还是leaderless,在停止写入的后,所有节点的数据都会保持一样。换句话说,就是最终一致。不过,最终一致性是相当弱的一种一致性保证,它并不保证在读写并行时不会出问题(例如,在你写入时,立即读取数据,这时候返回的不一定就是你写入的数据),并且等待的时间在极端情况下,会变得不可忍受。那么我们再牺牲一点性能,该如何换取更强的一致性呢?

首先,我们先来看看linearizability。这是一种强一致性模型,换句话说,无论何时,我们读到的数据看起来就像出自同一个数据系统。这可能不会理解,我们可以看一个例子,假设有一场LOL比赛,WE vs RNG,有两个观赛者A和B,在最终鏖战之后,WE赢了,数据库插入了一条数据WE赢了,这时A向数据库发送请求,leader从1节点读取数据了数据WE赢了,A非常高兴,但是与此同时B也向数据库发送了请求,leader没有从1节点读取,而是2节点,2节点这时,因为网络延迟之类的延迟,WE赢了的这条数据还没更新到2节点,显然B没有读到WE赢了这条信息,于是A与B在对于同一个数据库,读取的数据不一样。

那么我们如何去实现linearizability呢?

single-leader天然就是linearizable的最好执行者,因为只有一个节点去处理读写数据,但是不是所有的single-leader都执行了linearizable,因为从设计上来说,使用了snapshot isolation或者并行编程的bug都一定程度上丧失了linearizable。

后面将会介绍到的consensus算法,例如paxos,可以保证linearizable

Multi-leader呢,基本上可以放弃linearizable,因为这必然会产生写冲突。

Leaderless呢?同样时写冲突和Sloppy quorums

当然有得比有失,在实现了linearizability之后,我们不能忍受网络的中断,甚至是很大的延迟,因为一旦节点间无法通信,意味着数据无法传输,也就不能保证linearizability。最出名的莫过于CAP理论了,

在讨论完linearizability,我们再看看如何保证传递信息的顺序不会被打乱。在讨论这个之前,我们要把顺序和因果这两个搞明白。什么是因果性呢?如果有两条信息,A是提问,B是延后的回答,那么AB这两条信息就是具有因果关系的,AB顺序混乱就会导致观察者困惑。AB的关系也可以称为happen-before关系,A如果发生在B之前,那么B可能会知道A的存在,也有可能依赖于A,亦或是建立在A之上。我们可以发现,这实际上喻含着某种顺序。

我们要理解因果顺序不代表着全局的顺序,lineariable代表的就是全局的顺序,而causality可能只是某些事件的顺序。显然linearizability更加严格。我们从简单的来看,为了实现causality,我们可以使用版本号去控制,在事务提交时,由数据库去判定是否是最新的数据。

尽管这样可以实现部分的因果顺序,但是为了追踪全局,我们可以使用序列号或者是时间戳。时间戳不一定要来自于物理时钟,也就是电脑自身的时钟,也可以使用逻辑时钟(使用某种算法计算出来的衡量信息的顺序的序列号)。显然,对于single-leader的系统,这个可以由leader生成。但是这个对单机的压力相当大了,并且对于multi-leader之类的系统,我们可以让1.每个节点生成自己独有的序列号,比如A节点产生奇数,B节点产生偶数。2.让每个操作都附带上流过节点的物理时间戳3.预先规划好每个节点产生的序列号范围。但是上面的这些都只能保持相对的顺序,并不能保证全局的顺序。

除了上面这些,最出名的莫过于lamporttimestamp。简单提下,在lamport timestamp中,每个节点都拥有着自己独有的id,并且都保存着每个他们已经处理过的操作的序列号。

在处理过程中,节点会随时更新自己保存的序列号,如果序列号发生冲突,就会根据id来确定先后顺序。

我们还可以思考下一种更为简单的方式,那就是全局广播顺序,由某个节点强制统一系统的信息顺序,不过缺点也很明显,太依赖于网络传达了

为了满足全局广播顺序,我们可以使用分布式事务。但是分布式事务饱受诟病的一点就是,性能问题。拿2PC(两阶段提交)为例,我们为了写数据,首先需要协调者发送写请求给所有节点,等待节点返回ok,然后协调者发送prepare,询问节点准备好了吗?等到ok后,再会发送commit信息,让各个节点commit。其中有一个挂掉,那整个事务都会被取消。从我们的描述当中,我们可以看出这个太依赖于协调者,并且写的效率并不高。

在后来的系统出现了zookeeper这种专门提供协调服务的软件,为了解决全局广播顺序这个问题。

在简略的掠过后,我们会发现一致性问题并不简单,在里面包含着人类的思考以及权衡。正如TCP协议做的一样,尽管有着不可靠的底层,但依然能通过各种方法换取一个稳定的系统。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180127G0OTNJ00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

同媒体快讯

扫码关注云+社区

领取腾讯云代金券