本文意译
自Kaushik Sathupadi博客中的一篇博文,该博文通俗易懂的介绍了CAP定理。
原文:A plain english introduction to CAP Theorem
翻译:kibazen
你经常会听说CAP定理,该定理指明了在设计分布式系统时的一些上限限制。与我大多数其他的教程一样,让我们通过真实世界中的例子来理解CAP定理。
译注:CAP指:一致性(Consistency)、可用性(Availability)、分区容忍性(Partition tolerance)。
一、新的事业”记忆公司”
昨晚,当你的妻子感谢你记得她的生日,并给她买了礼物时,你的脑海中突然冒出一个奇怪的想法。大家都不擅长记忆,但是你却擅长记忆。所以,为什么不用自己的记忆天赋来创业呢?心动不如行动,于是你z在报纸上打了一个广告:
记忆公司 —— 即使你不记得,都不会忘记
是否还在为忘记很多事而烦恼?不用担心,只需一个电话就可以解决!
当你需要记住某事时,拨打电话555-55-REMEN告诉我们你想记住的事。比如:打电话告诉我们你上司的电话号码,然后就可以忘记它了。当拨打同样的电话号码555-55-REMEN,我们就会告诉你上司的电话号码。
收费:每次服务仅需0.1元哦!
然后,你与客户典型的一次对话内容就会像下面这样:
- 客户:你好,麻烦帮我记一下我邻居的生日?
- 你:好的,请问是多少?
- 客户:1月2日
- 你:(记在笔记本上)已经记录好了。当你需要时请再联系我们!
- 客户:谢谢!
- 你:没关系!本次服务收费0.1元!
二、业务增长
因为你的想法很简单,只需要笔记本和电话就能高效的完成业务,公司发展的很顺利,也收到了YCombinator的投资。公司的业务越来越多,每天都会接到上千个电话。
但是问题也开始出现了,客户需要等待越来越久电话才会接通。一些客户甚至听烦了服务电话的铃声,直接在等待接通的过程中就挂断了电话。同时,当你生病无法工作时,就会白白的丢失了一天业务。这对当天需要服务的客户来说,体验也很不好。所以,是时候扩大公司的规模了,让你的妻子也来帮忙。
译注:随着业务的增长,单点的服务已经不能满足业务的需求。
一个简单的计划:
- 你和你的妻子同时接听客户的电话。
- 对客户来说,还是只需要记住
555-55-REMEN
这个电话。 - 客户的服务电话会平均且高效的分配给你们。
三、服务第一次出现问题
实施新计划的两天后,你接到了老客户John的电话。下面是当时的通话记录:
- John:你好!
- 你:欢迎拨打记忆公司热线,有什么可以帮到你?
- John:请告诉我到新泽西的飞机是几点的?
- 你:好的,请稍等……(翻了一下自己的笔记本,没找到相关的内容)
- 你:你好,你好像并没有告诉我你飞机的信息,我这里找不到相关的信息。
- John:什么?我昨天不是告诉你们了吗?(电话就被挂断了)
怎么回事呢?John说谎了吗?你思考了一会,然后发现了问题的所在:是不是昨天是你的妻子接到的电话?然后你看妻子的笔记本上面确实有相关信息,这时你和你的妻子才认识到出问题了。
这个电话分配计划设计的有问题啊!分配系统没有保证一致性
!客户每次打来的电话都不一定是同一个人接到,那么客户得到的回答就可能是不对的。
译注:一致性:同样的请求,每次处理的结果应当一致。
四、解决一致性问题
你的竞争对手可能会忽略这个问题,但是你不会。于是你思考整夜,终于在早上想出了一个不错的计划:
- 当接到客户要求我们记录事情的电话时,在挂电话之前,都要先通知另外的人新记录事情的内容。
- 所有人同时将新纪录事情的内容
更新
到各自的笔记本上。 - 因为所有人的本子上都有最新的所有已记录的事情,所以,当客户再来问之前记录的内容时,就不用问其他人,因为自己的笔记本上就可以查到了。
但是这个计划有一个问题,第2步”更新
“会影响所有人,这样大家就不能同时工作了。比如:你接到一个记录事情的电话,然后马上告诉我也需要记录这个事,这时我就没办法接听其他客户的电话了。但是这可以接受的,因为大多数客户打来电话都是查询之前的内容(记一次,问多次)。同时,这种方法也解决了之前记了事情但是查不到的问题。
“这个系统还有一个问题你没考虑到。”你的妻子说。如果我们中有一人某天没有工作时就会出问题,我们不能再记录任何新的事情,因为无法更新那个没工作的人的笔记本。这样就会出现可用性
的问题。比如:如果你今天没工作,而此时我又接到一个更新电话,那我就没法完成更新的工作,因为我不能更新你的笔记本。
译注:可用性:即使系统内部某些子系统出现问题,整个系统依然能正常的对外提供服务。
五、更好的解决方法
你终于意识到了为什么分布式系统并没一开始想的那么简单了。难道就找不到一个既能保证一致性
,同时也能保证可用性
的解决方法了吗?于是你经过彻夜的思考,在早上又想出了一个和之前的类似,但是更好的计划:
- 当接到客户要求我们记录事情的电话时,在挂电话之前,都要先通知另外的人新记录事情的内容。
- 如果其他人在正常的工作的话,那么就同时将新纪录事情的内容
更新
到各自的笔记本上。如果有人没有正常工作的话,就需要给他发邮件提醒他更新。等到他们开始正常工作时,首先需要根据所有收到的更新邮件来更新笔记本,更新完成后,才能开始接电话,为客户提供服务。 - 因为所有人的本子上都有最新的所有已记录的事情,所以,当客户再来问之前记录的内容时,就不用问其他人,因为自己的笔记本上就可以查到了。
太好了!这下”记忆公司”提供的服务就能同时保证一致性
和可用性
了。
六、妻子怒了
有了上面的计划,现在公司的事情进展的很顺利。但是如果你和你的妻子都在上班,而其中一个人接收到更新电话时并没有通知其他人更新呢?比如:你惹你妻子不高兴了,她决定今天一天都不理你了,也不通知你更新笔记本了。那么今天你妻子的笔记本的所有更新你笔记本上都没有。所以,上面的计划只是保证一致性
和可用性
,并没有保证分区容忍性
。
你也可以选择在哄好妻子之前,不接任何客户的电话来避免这个问题。但是在你哄好妻子前的这段时间,系统又是不可用的,这样又没法保证可用性
了。
分区容忍性:即使各个系统暂时无法正常的通信,整个系统依然能正常的对外提供服务。
七、结论
好了,让我们来看看CAP定理到底在说什么。CAP定理说的是:当你设计分布式系统时,无法同时让系统满足一致性
、可用性
和分区容忍性
。顶多只能满足其中的两个:
一致性
:对你的客户来说,一旦他们更新要记住的事。无论何时,他们总是能获取到最新的记录内容。可用性
:只要你和你的妻子中有人在上班,公司就能正常且正确运行。分区容忍性
:即使你和你的妻子有一段时间不沟通了,公司依然能正常且正确的运行。
额外奖励: 请一个可以来回跑的秘书来保证最终结果的一致性
这里还有一个其他的解决方法。可以请一个秘书,当你或你的妻子笔记本更新时,她可以帮你们自动的更新其他人的笔记本上。这么做的最大好处就是:在有记录更新时,就不用等待其他人的笔记本更新完了才能接下一个电话了。这也是很多NoSQL系统的工作原理:数据库节点本地更新自己,然后一个后台进程再将这些修改同步到其他的数据库节点。这种方法的唯一问题就在于会在短时间内丢失一致性。比如:你的妻子接到了客户的更新电话,在秘书把你妻子笔记本上的这个变化同步到你的笔记本上之前,这个客户又打电话来问,但是是你接到了这个电话,这时他就会得到一个错误的回答。话虽如此,这种情况还是比较少的。比如:假如你的客户不会在5分钟内就来打电话问呢。
这个就是CAP和最终一致性,本文到此结束。
CA 架构不可选
分布式系统理论上不可能选择 CA 架构,而必须选择 CP 或 AP 架构。
- 若发生分区现象,为了保证 C,系统需要禁止写入,此时就与 A 发生冲突;
- 如果为了保证 A,则会出现正常的分区可以写入数据,有故障的分区不能写入数据,则与 C 冲突了。
Redis 满足的 CAP
单机版的 Redis 满足 CP 。
Redis 能够保证所有用户看到相同的数据(一致性,因为 Redis 不自动冗余数据)和网络通信出问题时,暂时隔离开的子系统能继续运行(分区容忍性,因为 Master 之间没有直接关系,不需要通信),但是不保证某些结点故障时,所有请求都能被响应(可用性,某个 Master 结点挂了的话,那么它上面分片的数据就无法访问了)。
集群版的 Redis 满足 AP 。
通过自动分片和冗余数据,Redis 具有了真正的分布式能力,某个结点挂了的话,因为数据在其他结点上有备份,所以其他结点顶上来就可以继续提供服务,保证了 Availability 。然而,也正因为这一点,Redis 无法保证曾经的强一致性了。