漫谈分布式系统(九):初探数据一致性

栏目: IT技术 · 发布时间: 5年前

内容简介:这是《漫谈分布式系统》系列的第 9 篇,预计会写 30 篇左右。每篇文末有为懒人准备的 TL;DR,还有给勤奋者的关联阅读。扫描文末二维码,关注公众号,听我娓娓道来。也欢迎转发朋友圈分享给更多人。

这是《漫谈分布式系统》系列的第 9 篇,预计会写 30 篇左右。每篇文末有为懒人准备的 TL;DR,还有给勤奋者的关联阅读。扫描文末二维码,关注公众号,听我娓娓道来。也欢迎转发朋友圈分享给更多人。

不得不重视的一致性问题

上一篇,我们终于引出分布式系统的第二个核心问题 -- 可用性。也提到了 replication 是高可用的唯一出路

上篇末尾还提到,replication 除了提供高可用外,也可能会带来严重的后果。

  • 比如,在异步复制数据的时候,可能由于网络抖动,导致数据没来得及复制到 slave replica 上。这个时候,如果有请求去读 slave replica,就读不到最新的数据。

  • 又比如,在 multi-master 的场景下,可能两个 master 同时接收到修改同一个数据的请求。这个时候,两个写操作成功返回客户端后,再复制到对方时,就可能由于数据冲突导致更新失败。

类似问题,可以统称为数据一致性问题(consistency problems)。

上一篇我们主要关注了 replication 的两个特性 -- 主从和时效性,二者组合起来,可能的数据一致性风险如下:

多主 一致性风险
single master
sync multi-master
async single master
multi-master

很明显,多主和异步,都可能带来数据一致性风险。 (leaderless 无主可以看做全主,有点物极必反的感觉。

  • 异步带来复制延迟( replication lag),导致数据同步不及时。

  • 多主带来并发写,导致数据冲突。

数据一致性一旦得不到保证,在系统内就像精神分裂一样;在系统外,则会带来很多现实问题 。比如:

  1. 用户刚付款买了张演唱会门票,然后刷新页面,由于请求被转发到的数据副本还没来得及更新,发现账号里显示没票。

  2. 用户收到推送,自己的文章有了新的留言,但点进去之后,由于访问到的副本还没更新,发现并没有新留言。

  3. 一个用户提了一个问题,另一个用户做了回答,但对某个数据副本来说,可能回答比问题先复制过来,第三个用户就会看到先有回答,后有问题的奇怪现象。

这些现实问题,使得系统在应用层,变得不可信。很显然,失去信任的代价是非常严重的。

因此,解决一致性问题,也就成了分布式系统的重大课题。

解决办法主要有两类:

  • 预防类, 极力避免一致性问题的产生,提供最强的一致性保证。

  • 先污染后治理类,允许不一致的产生,提供较弱的一致性保证。

从收敛性的角度看,第一类方法强制要求不一致数据的的实时收敛(convergence),而第二类方法,则允许不一致数据先发散(divergence),然后再逐渐收敛。

从消息顺序的角度看,预防类的强一致性,保证了对任意节点而言,不会因为 replication lag 等问题,导致先产生的数据被错误的存在后产生的数据后面。也就是说维持了整个系统对消息的全局线性(Linearizability)。而第二类方法,就属于 non-linerizable。(消息顺序非常重要,后面的文章会专门讲。)

预防类的一致性解法

正所谓防患于未然,从源头避免问题的产生,自然是最理想的目标。

尤其是数据一致性这么严重又不好解决的问题,能避免就应该避免。

所以我们就先来看第一类,预防类的一致性。

单主同步复制

最简单的办法,就是前面提到的 single leader + synchronous replication 的单主同步 模式。

  • single leader 保证了所有数据都只有单一的节点处理,避免了写冲突。

  • synchronous replication 保证了所有副本都更新完数据后,才返回给客户端,避免了单机故障导致的数据丢失。

这样,就达到了我们想要的强一致性(strong consistency),整个分布式系统看起来就像单机系统一样,仿佛没有副本。任何时候从任何地方访问系统,都能得到一致的体验。因此也有人把这种一致性叫做 single-copy consistency。

但深究一下,好像也还是有些 corner case。比如下面这个例子 A:

  1. master 收到客户端请求后,持久化,然后发送给 slave

  1. slave 收到转发的请求后,持久化,然后返回 ACK 给 master

  1. master 收到 slave 的 ACK 后,还没来得及返回给客户端,就挂了

这种情况下,客户端会认为系统没有成功处理请求,而实际上 master 和 slave 都已经持久化了数据, 客户端和服务端的认知就不一致了

再比如例子 B:

  1. 由于网络抖动,master 被误判为掉线后

  2. 系统 failover,slave 成为新的 master

  3. 网络恢复,原来的 master 又恢复正常了

这个时候,就会出现两个 master,即所谓的脑裂(split brain)现象, 连单主这个前提都被破坏了

这种情况下,系统就出乎意料的变成 multi-leader 了。仔细设计过的 multi-leader 系统尚且很难保证强一致性,更不用说这种异常陷入的情况了。

最后看一个例子 C:

  1. 在三副本的情况下,master 向另外两个副本同步数据,比如给一个账户扣款 1 元

  2. 其中一个副本成功了拿到数据并持久化到本地,然后发回 ACK 给 master

  3. 但另一个副本持久化结果后,发回的 ACK 却由于网络抖动丢失了

  4. master 没有收到第二个副本的 ACK,判定失败,于是重新发送

这样, 副本之间的数据就不一致了 ,第一个副本上的账户会扣掉 1 元,而第二个却会扣掉 2 元。

所以,单主同步的方法,也提供不了绝对的强一致性,只是在正常情况下 尽可能保证一致(best-effort guarantee) 而已。

(上面这几个 corner case,也和所谓 exactly once 问题有关,这个系列的后续文章会专门讲,这里不展开了。)

上面提到几个 corner case,比如脑裂的问题,似乎是很特殊的情况,但背后却可能藏着一个非常普遍的事实。

试想下,什么原因会导致节点被 误判 为死掉,从而导致脑裂?

  • 网络抖动

  • GC 导致程序停顿

  • ......

类似这些原因,导致了节点间通信不可达,至少是短期内看起来不可达。

或者用更专业的说法,叫做出现了网络分区(network partition),一个集群被分割成了几个网络不通的区域。

于是引出著名的 CAP 定理

一致性(Consistency)、可用性(Avaliabily)和 分区容忍性(Partition Tolerance)这三者,最多同时满足两个。

C 和 A 我们已经说了很多了,正因为我们想要 A,才引入了副本机制,然后导致了 C 危机。现在又多出个网络分区的可能需要处理。

而 CAP 定理居然告诉我们,不用处理,你处理不了的。

这么绝望吗?凭什么?我不信!

那就来推导看看。

  • 先要 C 和 A,这个时候如果发生网络分区,单主同步复制的方法,是无法成功完成数据复制的,所以拿不到 P。

  • 先要 C 和 P,如果发生网络分区,为了保证数据一致性,只能让其中一个分区正常工作,其他分区必须暂停服务,那这些分区就完全不可用,A 就丢了。

  • 先要 A 和 P,如果发生网络分区,并且每个分区都能正常工作,由于写数据时分区间无法同步数据,而等通信恢复后,又可能出现无法解决的数据冲突,也就是 C 丢了。

这样分析下来,确实三者无法兼顾。

另外,在刚才的推导过程中,每种情况的分析都是以「如果发生网络分区」作为初始条件,也揭示了它的与众不同。

事实上,C-A-P 三者并不是同一个层面的东西,C 和 A 是目标,而 P 呢,虽然 Partition-Tolerance 也是目标,但 partition 却是一个无法回避掉的前置条件。无数的生产事故已经告诉了我们,网络分区随时随地都可能发生。

所以,一个舍弃 P 的系统,是不具备真正意义的高可用性的。

而 CAP 落实到生产级分布式系统设计中,更多是在 P 的前提下,对 C 和 A 做取舍罢了。

TL;DR

我们为了高可用,引入了副本机制,但副本机制的副作用是会带来数据一致性问题。

  • replication lag 可能导致数据同步不及时。

  • 多主并发写可能导致数据冲突。

  • 数据一致性问题会带来应用层面很多现实的问题,使得系统对外变得不可信,因此必须解决。

  • 数据一致性问题的解法可以分为两类:预防类和先污染后治理类。

  • 预防类最基本的方法就是单主同步复制,但其实也只能做到 best effort gurantee,解决不了一些 corner case。

  • 在这些 corner case 背后,其实隐藏着一个更基本的难题,即所谓 CAP 定理。

对于上面的例子 C,我们换个角度理解,数据从 master 复制到多个 slave,可以看作几个往不同节点写数据的独立事件,正是这些事件的 部分 成功,导致了数据的不一致。

如果全部失败,大不了重试好了。但如果部分成功部分失败,重试就有重复的可能。

而避免多个事件部分成功,或者说要保持多个事件的原子性 -- 要么都成功,要么都失败,早已有了可靠的方案 -- 事务(你看,抓住问题的本质多么重要)。

只不过,我们需要的是 分布式事务(distributed transaction)

下一篇,我们就一起了解下分布式事务。

原创不易

关注/分享/赞赏

给我坚持的动力

漫谈分布式系统(九):初探数据一致性

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Spring in Action

Spring in Action

Craig Walls / Manning Publications / 2011-6-29 / USD 49.99

Spring in Action, Third Edition has been completely revised to reflect the latest features, tools, practices Spring offers to java developers. It begins by introducing the core concepts of Spring and......一起来看看 《Spring in Action》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具