分布式系统中的CAP理论
前言
- 2020-02-06: 初版
- 2020-04-07: 阅读从0开始学架构-想成为架构师,你必须知道CAP理论后觉得还有不足之处, 于是再次修改.
一个分布式系统, 我们要关注的是它的可用性、一致性、分区容错性
- Consistency 一致性
- Availability 可用性
- Partition tolerance 分区容错性
什么样的系统适合CAP理论
在了解什么是CAP
之前, 我们先来看看什么样的系统适合CAP理论.
从参考资料里的所有英文文章得知
Silverback
提到A distributed system (generally running in a datacenter)
.Wikipedia
提到A distributed data store
.IBM
只提到了a distributed computer system
.Robert Greiner
一开始提到any distributed system
, 两个月后重新定义为In a distributed system (a collection of interconnected nodes that share data.)
大家都认为, 分布式系统才适合CAP
理论, 但是, 不是所有分布式系统都适合.Robert Greiner
强调了这个分布式系统是interconnected
互联和share data
共享数据的.
举一个反例, 比如Memcache
集群.Memcache
的各个节点之间是不互联和共享数据的, 客户端根据路由规则, 自行决定存储数据到哪个节点上.
这确实是一个分布式系统, 但是却不适用于CAP
理论.
举一个正例, 比如MySQL
集群.MySQL
我们熟, 主从复制, 就是interconnected
和share data
.
In a distributed system (a collection of interconnected nodes that share data.)
所以这里还是以Robert Greiner
的文章为准, 一个互联且共享数据的节点集合, 才是适用CAP
的分布式系统.
什么是CAP
我们假设一个分布式系统有4
个终端, 数据库DB1
和DB2
以及应用端App1
和App2
.
假设有数据a=1
C 一致性
从参考资料里的所有英文文章得知
Silverback
提到all nodes have access to the same data simultaneously
.Wikipedia
提到Every read receives the most recent write or an error
.IBM
提到where all nodes see the same data at the same time
.Robert Greiner
一开始提到All nodes see the same data at the same time.
, 两个月后重新定义为A read is guaranteed to return the most recent write for a given client.
在客户端看来, 每次读都要能读到最新写入的数据.
举个例子, 如果App1
往DB1
写入数据a=2
, 然后从APP2
读DB2
得到数据a=2
, 对于客户端来说, DB1
和DB2
的数据保持一致.
但是实际情况下, 对于这个最新写入的理解, 是不同的.
同一时刻, 不同节点可能拥有不同的最新数据.
我举个例子, 在事务执行过程中, 不同的节点的数据并不完全一致.
App1 |
App2 |
|
---|---|---|
start transaction |
||
update t set a = 2 |
||
t1 | select a from t (a=1) |
|
t2 | select a from t (a=2) |
|
commit |
||
t3 | select a from t (a=2) |
t1
和t2
时刻, App1
能读到最新写入的a=2
的数据. App2
能读到最新写入的a=1
的数据.
此时, App1
的事务还没有提交, 对于App2
来说, App1
的写入的数据是感知不到的, App2
的最新数据还是a=1
.
等到t3
时刻, App1
提交事务后, App2
才能读到最新的a=2
.
如果App1
的事务回滚了, 那么App2
是不知道a=2
这个事件发生过的.
A 可用性
从参考资料里的所有英文文章得知
Silverback
提到a promise that every request receives a response, at minimum whether the request succeeded or failed
.Wikipedia
提到Every request receives a (non-error) response, without the guarantee that it contains the most recent write
.IBM
提到which guarantees that every request receives a response about whether it succeeded or failed.
.Robert Greiner
一开始提到Every request gets a response on success/failure.
, 两个月后重新定义为A non-failing node will return a reasonable response within a reasonable amount of time (no error or timeout).
Silverback
、IBM
和Robert Greiner
都认为只要每个请求能收到响应, 无论是成功还是失败, 就算满足可用性.Robert Greiner
的第二篇文章和Wikipedia
则认为请求要返回合理的成功的响应, 无论数据对错. 也就是说, 可用性的定义更严格一点.
我个人赞同第二种观点, 成功和失败的定义太广泛. 请求超时、错误也算失败的响应, 但这算可用吗? 我认为是不可用的.
比如App1
读取DB1
的数据
- 得到正确的数据
a=1
,DB1
是可用的. - 得到错误的数据
a=111
,DB1
是可用的. - 得到
connection timeout
,DB1
是不可用的.
P 分区容错性
从参考资料里的所有英文文章得知
Silverback
提到the system will continue to work even if some arbitrary node goes offline or can’t communicate
.Wikipedia
提到The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes
.IBM
提到where the system continues to operate even if any one part of the system is lost or fails.
.Robert Greiner
一开始提到System continues to work despite message loss or partial failure.
, 两个月后重新定义为The system will continue to function when network partitions occur.
分区容错性就是当网络发生分区的时候, 集群能够继续完成工作, 返回合理的成功的响应.
网络分区是一种现象, 举个例子, 就是DB1
和DB2
之间的通信断了, 主从复制失败.
总结
C
: 对客户端来说, 读操作保证能够返回最新的写操作结果. 这就是一致性.A
: 对客户端来说, 非故障的节点在合理的时间内返回合理的响应, 不是错误和超时的响应. 这就是可用性P
: 对集群来说, 当出现网络分区后, 系统能够继续返回合理的响应. 这就是分区容错性
那么CAP
能同时做到吗?
正常情况下是可以的, 我们说的只能三选二的情况一般都是网络故障的时候, 才会进行取舍.
还是以这张图为例
因为网络的不可靠性, **P
分区容错性是一定要保证的.
那么当DB1
和DB2
之间的网络发生故障, 此时就要对AC
**进行取舍.
- 保障**
A
**,App
能正常读写DB1
和DB2
, 但是一旦进行了写入操作,DB1
和DB2
是不能进行通信做主从复制的, 换句话说, 就会导致数据不一致的情况. - 保障**
C
**, 那么为了保证数据一致性,App
就应该等待DB1
和DB2
之间的网络恢复, 这样就不能访问数据库了, 舍弃了可用性.
那有人问, 能不能只保证**AC
?
可以啊, 我们把两个数据库都放在一台服务器上, 这样就不会因为网络分区导致AC
**二选一的问题了, 因为根本没有网络.
但是这还能叫做分布式吗?