0ae176c008b1f52aa802d12b77c89f4b
漫谈分布式系统(9) -- 初探数据一致性

这是《漫谈分布式系统》系列的第 9 篇,预计会写 30 篇左右。欢迎订阅,听我娓娓道来。也欢迎转发朋友圈分享给更多人。

不得不重视的一致性问题

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

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

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

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

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

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

同步 多主 一致性风险
sync single master
sync multi-master
async single master
async 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

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

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

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

再比如例子 B:

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

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

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

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

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

最后看一个例子 C:

top Created with Sketch.