这是我在国外的一个网站上看到的关于理解CAP理论的小故事,内容上有些地方进行了修改。
章节一:“Rememberance Inc”我的新事业
昨晚,当我的配偶感谢我为她纪念生日并且送给她礼物,一个奇怪的主意打动了我。人们很难记住事情,但是我很擅长,那么为什么不创办一个发挥我才能的事业呢?我越想越喜欢这个主意。实际上,我甚至想用报纸广告来解释我的想法。以下是广告内容:
Remembrance Inc!-即使你不记得,也永远不会忘记 当你忘记了很多东西的时候会感到难过吗?不用担心,只需要一通电话帮助您
当你需要记住一些事情的时候,只需要拨打12345678901,并且告诉我你需要记住的事情。
举个栗子:打电话给我,并且告诉我们你老板的电话,然后忘记你老板的电话。当你需要知道你老板电话的时候,
只需要打回给我,然后我就会告诉你,你老板是什么电话号码。 服务费:每条记录一毛钱
所以,每次的通话记录就像以下这样:
客人:你好,能帮我记一下我邻居的生日吗?
我:当然!请问是什么时候?
客人:三月十四
我:(在记事本上写下这条信息)已经保存好了,随时欢迎打电话过来查询您邻居的生日!
客人:谢谢。
我:没问题,我将会在您的账户中扣除一毛钱服务费。
个人理解:
就好像每个系统最开始的雏形,单体系统。
章节二:我想扩大规模
我的公司收到了投资人的投资。我的主意非常的简单,除了电话和记事本不需要任何东西,并且对人们非常的有用,以至于越来越多人想用。现在每天都会有几百个电话会打进来。
但是问题也随即而来。我观察到越来越多的客人为了打通我的电话在等待队列之中。很多人因为等待太久了而挂断了电话。除此之外,如果我生病了无法上班,那么就会丢失了整一天的业务。更不要说会让客户感觉不满意,如果刚好那天想要获取信息。所以我认为是时候要让我的妻子来帮助我扩大规模了。
我开始了一个简单的计划:
1、 我和我的妻子两个人都获得一个电话分机
2、 客户依旧拨打12345678901
3、 当我和我的妻子哪个是空闲的时候将会收到这个电话
个人理解:
当用户的访问量已经超过了单体系统可以承受的TPS,那么超出的那部分用户请求就会被丢进等待队列之中等待,而且如果服务器宕机了,那么将永远无法响应客户的请求,这样就造成了用户方面不良服务体验。
为了提高系统响应速度与系统的可用性,这里使用了负载均衡,仍然统一的入口,增加了一台服务器,通过服务器负载率来选择哪个服务器响应这一个客户请求,一方面增加了整个系统的TPS,另一方面增加了可用性,一台宕了还有另一台。
章节三:我得到了第一个差评
在使用了新的规矩两天后,我的一个老客户打来了电话。对话如下:
老客户:你好
我:很高兴您能打电话过来,请问我有什么可以帮助您?
老客户:你能告诉我,我啥时候要飞过去新德里吗?
我:当然!请稍等一下哦,亲。
(我开始在我的笔记本里面查找)
(oh my god!这里没有记录这位客户的飞行日期呀!)
我:对不起先生,这里发现您没有告诉过我们您飞去新德里的时间呀。。
老客户:什么鬼?我昨天打电话给你们的。
这里面发生了什么呢?老客户在撒谎吗?我思考了一下子,突然想到了原因了。
难道昨天接待老客户的是我老婆?然后我跑去老婆的桌子上查看她的记事本,果然在这里。
我把这个问题告诉给我老婆,她也意识到这个问题。
多么可怕的缺陷,在我的分布式设计之中!我的分布式系统不是一致的!这样就总是会发生如果客人把一个信息告诉了我或者我老婆,如果下次打电话是另外一个人接听,就会查不到客人需要记录的信息。
个人理解:
这里出现了数据一致性的问题,因为两台服务器中的数据没有通过任何机制达到统一。假设一开始由A服务器接收用户的请求,数据就会存放在A服务器上,当这个用户再次访问的时候,根据负载均衡算法,由B服务器响应客户的请求,因为两个服务器的数据没有达到一致,B服务器就会查询不到这个用户的任何信息。
章节四:我修复了这个一致性问题
很好,我的竞争者可能会忽略这个小错误,但是我不会。当我的妻子正在睡觉的时候,我一直在想有什么解决的办法,终于想到了一个漂亮的主意。我把我的老婆叫了起来:
“老婆,这个我们现在开始要做的操作。”
1、 不管什么时候,只要我们中任何一个人接到客户的电话(当客户想要我们记住某些事情的时候),在挂掉电话之前,我们要通知对方,客户需要我们记住什么。
2、 用这种方式,我们两个人就记录下任何更新了。
3、 当我们接到查询电话的时候,我们就不用互相询问对方了。因为我们两个人笔记本里面的信息都是最新的。
但是这里有一个问题,当我们其中一个人要进行记录的时候,另外一个人不得不停下手上的工作一起进行记录,那么我们在那段时间内就无法并行工作了。打个比方:当我要记录客户信息的时候,我老婆也要一起记录,那么我老婆就没办法接听在那个时间打进来的电话了。但是那也是OK的,因为我们大部分接到的电话都是“查询”电话(一个客人记录一次询问多次)。此外,不惜任何代价,我们不能给客户错误的信息。
“厉害呀”我的老婆说,“但是这里面有一个你没有想到的更大的缺陷,如果我们其中一个人休息了,那么我们就没办法进行客户的记录操作了,因为另外一个人无法进行记录!我们会有可用性的问题。
个人理解:
这个时候,为了满足一致性要求,多个服务器之间完成某种一致性协调机制。
1、 A服务器接收到记录数据的请求。
2、 A服务器对数据持久化操作进入prepare状态。
3、 A服务器把数据副本发到B服务器中。
4、 B服务器接收到数据副本。
5、 对数据持久化操作进入prepare状态,并返回信号给A服务器。
6、 A服务器完成数据持久化操作,并发送信号给B服务器。
7、 B服务器接收到A服务器发送过来的信号,同时也完成数据持久化操作。
这个机制可以保证数据一致性的,但是会出现以下两个问题:
1、 当A服务器接收到记录数据请求,B服务器也要同时记录数据,那么B服务器在这段时间内为不可用状态,无法接收客户任何的请求。
2、 当A服务器接收到记录数据请求,B服务器宕机了,那么永远也无法进入到第3步,数据也不会被持久化,导致A服务器的数据持久化操作也不可用。
在这个系统中,虽然保证了数据的一致性,但是可用性上面大打折扣。
章节五:我想到了一个更好的点子
我开始意识到,一个分布式系统不是如我开始想象的那么容易。想出一个既有“可用性”又有“一致性”的解决方案真的那么难吗?对于其他人来说,可能很难,但是对于我来说,不一定。
在下一个早晨,我又想到了一个解决方案,这速度连我的竞争者做梦也没有想到!然后我又把妻子早早的叫了起床。
我对着我的妻子说:“如果这样做,我们就又能兼顾一致性,又能兼顾可用性。”和我昨天说的大部分相似:
1、 不管什么时候,我们任何一个人收到客户要记录消息的电话,在完成记录之前,如果另外一个人是在线的,那么就告诉另外一个人。那么我们两个人都记下了这条记录。
2、 但是,如果另外一个人不在线,那么就发关于这个记录的邮件给另外一个人。
3、 当另外一个人休息了几天,上班之后。先浏览所有消息邮件,在接听客户电话之前,相应地把之前的记录添加到自己的笔记本上面。
天啊!我的妻子说的!在这个系统里面我找不到缺陷。让我们把这些规则运用起来,那么我的公司就可以保持一致性和可用性了。
个人理解:
根据这一章节的内容,对前面一章提出的数据一致性机制进行了修改。
在发送数据副本到B服务器之前,对B服务器的状态进行检测。
可用:按照原本约定好的机制进行。
不可用:发送数据副本到一个存储容器中。当B服务器状态更新为可用前,先检测这个存储容器,如果发现有数据副本,先把数据副本更新到本地,再把服务器状态改为可用。
机制的修改修复了上章节中可用性问题的第二个,允许服务器发生宕机的情况,极大地提高了系统可用性,这个时候可以说这个系统拥有了CA两种属性。
章节六:我的妻子生气了
在一段时间内,任何事情都在有序地进行。我的系统是一致性的。即使我们其中一个人没有工作,我的系统也是可用的。但是如果我和我的妻子两个人都在工作中,我妻子不更新我这边接听到的用户信息呢?记得有些天,我每天都很早把我的妻子叫起床,因为我想把我想到的好点子告诉她,可能是太早了导致她生我气,所以她决定一天都不会更新我这边收到的客户信息,我之前想到的办法就完全失效了。到目前为止,我的系统符合一致性和可用性,但是不符合分区容忍性,一旦我妻子不想和我进行交流,那么整个系统就会出现错误。
我可以在我妻子生气不更新我这边用户信息地时候选择不接听电话,以此来修复分区容忍性,但是在这段时间内,我这个节点将会不可用。
**个人理解:
这章节所提及的是P(Partition Tolerance)的问题,当A系统与B系统都是可用的,但是由于某些原因,导致两个系统之间无法通信,也无法获取存储容器内的数据副本,那么就会发生如下问题:
- 客户把数据记录的请求发给A系统
- A系统接收到数据记录的请求,然后尝试与B系统通信。
- 发现B系统无法通信,那么把B系统认定为不可用。
- 随后把数据副本丢到存储容器,更新本地记录。
因为A系统与B系统只是无法相互通信而已,各自都是可用的,所以当这个客户下次发出查询请求,刚好响应的是B系统的时候,就会查询不到客户需要的数据。如果这个时候这个客户又把同样的数据记录请求发给B服务器,那么整个系统在通信恢复之后,就会有双份的在分区时候产生的数据。
这个章节中也提及到了其中的一种解决方案,牺牲A来换取P。**
章节七:结论
现在我们从新来看CAP理论。它陈述了以下内容,当你去设计一个分布式系统的时候,你不可能让一个系统同时有一致性、可用性、分区容忍性。你只能同时获取两个:
- Consistency(一致性):客人在我这里记录了信息后,不管以多快地速度重新打进来,不管是我或者我的老婆,都能把客人刚才记录地信息找出来。
- Availability(可用性):直到我或者我老婆报告用户记录信息的之前,电话总是可以打进来的。
- Partition ToLerance(分区容忍):即使我和我的老婆不进行沟通,电话也可以打进来。
最终一致性
这是另外一个需要思考的地方。我可以请一个业务员,当我或者我老婆的笔记本更新的时候,
让他去更新另外一个人的笔记本。这样有一个很大的好处,因为他是在后台工作的,所以当我或者我妻子更新笔记本的时候,不需要等待另外一个人也同时记录后,才可以接听打入的电话。很多NoSql数据库都是这样子操作的,一个节点更新本地的数据,然后一个后台程序相应地同步其它节点。但是这里会有个问题,各个节点在一段时间之内将会丧失一致性。例如:一个客人打电话进来,我记录了某些事情,业务员还没来得及同时给我老婆笔记本记上,那个客人又打电话进来,是我老婆接的电话,询问刚才记录的事情,那么就会发生错误。但是说起来,这么奇葩的个例也是少见的,因为人们不会这么快忘记一个事情。
当我们去设计一个分布式系统的时候,要按照实际的情况去选择AP、CP还是CA。
故事来自:http://ksat.me/a-plain-english-introduction-to-cap-theorem