这是《漫谈分布式系统》系列的第 9 篇,预计会写 30 篇左右。欢迎订阅,听我娓娓道来。也欢迎转发朋友圈分享给更多人。
不得不重视的一致性问题
上一篇,我们终于引出分布式系统的第二个核心问题 -- 可用性。也提到了 replication 是高可用的唯一出路。
上篇末尾还提到,replication 除了提供高可用外,也可能会带来严重的后果。
类似问题,可以统称为数据一致性问题(consistency problems)。
上一篇我们主要关注了 replication 的两个特性 -- 主从和时效性,二者组合起来,可能的数据一致性风险如下:
同步 |
多主 |
一致性风险 |
sync |
single master |
无 |
sync |
multi-master |
有 |
async |
single master |
有 |
async |
multi-master |
有 |
很明显,多主和异步,都可能带来数据一致性风险。(leaderless 无主可以看做全主,有点物极必反的感觉。)
而数据一致性一旦得不到保证,在系统内就像精神分裂一样;在系统外,则会带来很多现实问题。比如:
- 用户刚付款买了张演唱会门票,然后刷新页面,由于请求被转发到的数据副本还没来得及更新,发现账号里显示没票。
- 用户收到推送,自己的文章有了新的留言,但点进去之后,由于访问到的副本还没更新,发现并没有新留言。
- 一个用户提了一个问题,另一个用户做了回答,但对某个数据副本来说,可能回答比问题先复制过来,第三个用户就会看到先有回答,后有问题的奇怪现象。
这些现实问题,使得系统在应用层,变得不可信。很显然,失去信任的代价是非常严重的。
因此,解决一致性问题,也就成了分布式系统的重大课题。
解决办法主要有两类:
- 预防类, 极力避免一致性问题的产生,提供最强的一致性保证。
- 先污染后治理类,允许不一致的产生,提供较弱的一致性保证。
从收敛性的角度看,第一类方法强制要求不一致数据的的实时收敛(convergence),而第二类方法,则允许不一致先发散(divergence),然后再想办法解决。
从消息顺序的角度看,预防类的强一致性,保证了对任意节点而言,不会因为 replication lag 等问题,导致先产生的数据被错误的存在后产生的数据后面。也就是说维持了整个系统对消息的全局线性(Linearizability)。而第二类方法,就属于 non-linerizable。(消息顺序非常重要,后面的文章会专门讲。)
预防类的一致性解法
正所谓防患于未然,从源头避免问题的产生,自然是最理想的目标。
尤其是数据一致性这么严重又不好解决的问题,能避免就应该避免。
所以我们就先来看第一类,预防类的一致性。
单主同步复制
最简单的办法,就是前面提到的 single leader + synchronous replication 的单主同步 模式。
这样,就达到了我们想要的强一致性(strong consistency),整个分布式系统看起来就像单机系统一样,仿佛没有副本。任何时候从任何地方访问系统,都能得到一致的体验。因此也有人把这种一致性叫做 single-copy consistency。
但深究一下,好像也还是有些 corner case。比如下面这个例子 A:
master 收到客户端请求后,持久化,然后发送给 slave
slave 收到转发的请求后,持久化,然后返回 ACK 给 master
master 收到 slave 的 ACK 后,还没来得及返回给客户端,就挂了
这种情况下,客户端会认为系统没有成功处理请求,而实际上 master 和 slave 都已经持久化了数据,客户端和服务端的认知就不一致了。
再比如例子 B:
由于网络抖动,master 被误判为掉线后
系统 failover,slave 成为新的 master
网络恢复,原来的 master 又恢复正常了
这个时候,就会出现两个 master,即所谓的脑裂(split brain)现象,连单主这个前提都被破坏了。
这种情况下,系统就出乎意料的变成 multi-leader 了。仔细设计过的 multi-leader 系统尚且很难保证强一致性,更不用说这种异常陷入的情况了。
最后看一个例子 C:
这是《漫谈分布式系统》系列的第 9 篇,预计会写 30 篇左右。欢迎订阅,听我娓娓道来。也欢迎转发朋友圈分享给更多人。
不得不重视的一致性问题
上一篇,我们终于引出分布式系统的第二个核心问题 -- 可用性。也提到了 replication 是高可用的唯一出路。
上篇末尾还提到,replication 除了提供高可用外,也可能会带来严重的后果。
类似问题,可以统称为数据一致性问题(consistency problems)。
上一篇我们主要关注了 replication 的两个特性 -- 主从和时效性,二者组合起来,可能的数据一致性风险如下:
同步 |
多主 |
一致性风险 |
sync |
single master |
无 |
sync |
multi-master |
有 |
async |
single master |
有 |
async |
multi-master |
有 |
很明显,多主和异步,都可能带来数据一致性风险。(leaderless 无主可以看做全主,有点物极必反的感觉。)
而数据一致性一旦得不到保证,在系统内就像精神分裂一样;在系统外,则会带来很多现实问题。比如:
- 用户刚付款买了张演唱会门票,然后刷新页面,由于请求被转发到的数据副本还没来得及更新,发现账号里显示没票。
- 用户收到推送,自己的文章有了新的留言,但点进去之后,由于访问到的副本还没更新,发现并没有新留言。
- 一个用户提了一个问题,另一个用户做了回答,但对某个数据副本来说,可能回答比问题先复制过来,第三个用户就会看到先有回答,后有问题的奇怪现象。
这些现实问题,使得系统在应用层,变得不可信。很显然,失去信任的代价是非常严重的。
因此,解决一致性问题,也就成了分布式系统的重大课题。
解决办法主要有两类:
- 预防类, 极力避免一致性问题的产生,提供最强的一致性保证。
- 先污染后治理类,允许不一致的产生,提供较弱的一致性保证。
从收敛性的角度看,第一类方法强制要求不一致数据的的实时收敛(convergence),而第二类方法,则允许不一致先发散(divergence),然后再想办法解决。
从消息顺序的角度看,预防类的强一致性,保证了对任意节点而言,不会因为 replication lag 等问题,导致先产生的数据被错误的存在后产生的数据后面。也就是说维持了整个系统对消息的全局线性(Linearizability)。而第二类方法,就属于 non-linerizable。(消息顺序非常重要,后面的文章会专门讲。)
预防类的一致性解法
正所谓防患于未然,从源头避免问题的产生,自然是最理想的目标。
尤其是数据一致性这么严重又不好解决的问题,能避免就应该避免。
所以我们就先来看第一类,预防类的一致性。
单主同步复制
最简单的办法,就是前面提到的 single leader + synchronous replication 的单主同步 模式。
这样,就达到了我们想要的强一致性(strong consistency),整个分布式系统看起来就像单机系统一样,仿佛没有副本。任何时候从任何地方访问系统,都能得到一致的体验。因此也有人把这种一致性叫做 single-copy consistency。
但深究一下,好像也还是有些 corner case。比如下面这个例子 A:
master 收到客户端请求后,持久化,然后发送给 slave
slave 收到转发的请求后,持久化,然后返回 ACK 给 master
master 收到 slave 的 ACK 后,还没来得及返回给客户端,就挂了
这种情况下,客户端会认为系统没有成功处理请求,而实际上 master 和 slave 都已经持久化了数据,客户端和服务端的认知就不一致了。
再比如例子 B:
由于网络抖动,master 被误判为掉线后
系统 failover,slave 成为新的 master
网络恢复,原来的 master 又恢复正常了
这个时候,就会出现两个 master,即所谓的脑裂(split brain)现象,连单主这个前提都被破坏了。
这种情况下,系统就出乎意料的变成 multi-leader 了。仔细设计过的 multi-leader 系统尚且很难保证强一致性,更不用说这种异常陷入的情况了。
最后看一个例子 C:
这是《漫谈分布式系统》系列的第 9 篇,预计会写 30 篇左右。欢迎订阅,听我娓娓道来。也欢迎转发朋友圈分享给更多人。
不得不重视的一致性问题
上一篇,我们终于引出分布式系统的第二个核心问题 -- 可用性。也提到了 replication 是高可用的唯一出路。
上篇末尾还提到,replication 除了提供高可用外,也可能会带来严重的后果。
类似问题,可以统称为数据一致性问题(consistency problems)。
上一篇我们主要关注了 replication 的两个特性 -- 主从和时效性,二者组合起来,可能的数据一致性风险如下:
同步 |
多主 |
一致性风险 |
sync |
single master |
无 |
sync |
multi-master |
有 |
async |
single master |
有 |
async |
multi-master |
有 |
很明显,多主和异步,都可能带来数据一致性风险。(leaderless 无主可以看做全主,有点物极必反的感觉。)
而数据一致性一旦得不到保证,在系统内就像精神分裂一样;在系统外,则会带来很多现实问题。比如:
- 用户刚付款买了张演唱会门票,然后刷新页面,由于请求被转发到的数据副本还没来得及更新,发现账号里显示没票。
- 用户收到推送,自己的文章有了新的留言,但点进去之后,由于访问到的副本还没更新,发现并没有新留言。
- 一个用户提了一个问题,另一个用户做了回答,但对某个数据副本来说,可能回答比问题先复制过来,第三个用户就会看到先有回答,后有问题的奇怪现象。
这些现实问题,使得系统在应用层,变得不可信。很显然,失去信任的代价是非常严重的。
因此,解决一致性问题,也就成了分布式系统的重大课题。
解决办法主要有两类:
- 预防类, 极力避免一致性问题的产生,提供最强的一致性保证。
- 先污染后治理类,允许不一致的产生,提供较弱的一致性保证。
从收敛性的角度看,第一类方法强制要求不一致数据的的实时收敛(convergence),而第二类方法,则允许不一致先发散(divergence),然后再想办法解决。
从消息顺序的角度看,预防类的强一致性,保证了对任意节点而言,不会因为 replication lag 等问题,导致先产生的数据被错误的存在后产生的数据后面。也就是说维持了整个系统对消息的全局线性(Linearizability)。而第二类方法,就属于 non-linerizable。(消息顺序非常重要,后面的文章会专门讲。)
预防类的一致性解法
正所谓防患于未然,从源头避免问题的产生,自然是最理想的目标。
尤其是数据一致性这么严重又不好解决的问题,能避免就应该避免。
所以我们就先来看第一类,预防类的一致性。
单主同步复制
最简单的办法,就是前面提到的 single leader + synchronous replication 的单主同步 模式。
这样,就达到了我们想要的强一致性(strong consistency),整个分布式系统看起来就像单机系统一样,仿佛没有副本。任何时候从任何地方访问系统,都能得到一致的体验。因此也有人把这种一致性叫做 single-copy consistency。
但深究一下,好像也还是有些 corner case。比如下面这个例子 A:
master 收到客户端请求后,持久化,然后发送给 slave
slave 收到转发的请求后,持久化,然后返回 ACK 给 master
master 收到 slave 的 ACK 后,还没来得及返回给客户端,就挂了
这种情况下,客户端会认为系统没有成功处理请求,而实际上 master 和 slave 都已经持久化了数据,客户端和服务端的认知就不一致了。
再比如例子 B:
由于网络抖动,master 被误判为掉线后
系统 failover,slave 成为新的 master
网络恢复,原来的 master 又恢复正常了
这个时候,就会出现两个 master,即所谓的脑裂(split brain)现象,连单主这个前提都被破坏了。
这种情况下,系统就出乎意料的变成 multi-leader 了。仔细设计过的 multi-leader 系统尚且很难保证强一致性,更不用说这种异常陷入的情况了。
最后看一个例子 C: