<commit_before>---
title: 用最简单的话介绍 CAP 理论
category: ['分布式']
tags: ['分布式']
author: 景阳
email: <EMAIL>
description: 用最简单的话介绍 CAP 理论
---

原文来自[A plain english introduction to CAP Theorem](http://ksat.me/a-plain-english-introduction-to-cap-theorem/)。

---

你可能经常听说 CAP 理论，它在分布式系统的设计中指明了一些上限。和其他的一些教程不同，我尝试用现实世界的一些情形来解释 CAP 理论。

### 第一章：“记忆有限公司”──你的新冒险

昨晚因为你记得老婆的生日并送给她一个生日礼物，你老婆大大地赞赏了你，也因此一个奇怪的念头出现在了你的脑海里：很多人都记不住东西，但是我非常擅长记忆，为什么我不利用这个优势来创业呢？你想得越多，就越喜欢这个主意。事实上你都已经想好了一个报纸广告来推广你的想法：

> 记忆有限公司──不用记住也不会忘记！
>
> 你还在因为记不住东西而苦恼吗？不要担心，现在你只需一个电话！
>
> 当你要记住东西时，拨打 555--55-REMEM 把要记住的信息告诉我们。比如，如果你忘了老板的电话，只要你曾经打电话让我们记住，现在只要拨打 555--55-REMEM，我们会告诉你！
>
> 收费：每次仅一毛！

所以，一个典型的电话对话就像这样：
> * 客户：你好，帮我把邻居的生日记住。
> * 你：好的，他的生日是？
> * 客户：1月2号。
> * 你（把生日记在你的笔记本里）：记住了。在您需要您邻居生日的时候给我们打电话！
> * 客户：谢谢。
> * 你：不客气！本次收费 0.1 元。

### 第二章：扩大业务

你得到了 YCombinator 的投资。你的*创意*如此简单，只需要一个笔记本一部电话，所以你的生意像野火一样在扩张。现在你每天要接几百个电话。

现在你遇到了问题：越来越多的客户要等很久才能接通你的电话。很多人在厌倦了等待后甚至挂断了电话。更有甚者，如果你因为生病一天没上班的话，你就会错过一整天的生意──更不用提那些因此得不到信息而不满的老客户。

你觉得是时候扩大生意了，你的老婆必须帮你一把。

你开始了一个简单的计划：
1. 你和你老婆每人一部电话。
2. 客户们仍然只需拨打 (555)–55-REMEM 一个电话。
3. 电话交换机会把电话平均地分给你和你老婆。

### 第三章：你有了第一个“糟糕的服务”

在新系统上线两天后，你接到了一个老客户 Jhon 的电话：

> * Jhon：你好。
> * 你：感谢致电！有什么可以帮您的？
> *  Jhon：麻烦告诉我到新德里的航班时间。
> *  你：好的，请稍等。
>   （你翻找笔记本，上面没有记录 Jhon 的航班时间！）
> * 你：先生，有点问题。您没有告诉过我您的航班时间。
> *  Jhon：什么？我昨天刚告诉你们！（挂断了电话）

怎么会这样？是 Jhon 在说谎吗？你想了一会，一个想法突然冒了出来：Jhon 是不是昨天给你老婆打了电话？你跑去找你老婆──确实是这样。你把这事告诉了你老婆，她也发现了这个问题。

你的分布式设计出现了一个巨大的漏洞！你的分布式系统是不*一致*的！总有一种可能，客户在一个人那里更新了一些信息，但是在查询的时候电话被另一个人接到。

### 第四章：你修正了一致性错误

你的竞争者也许会无视这个糟糕的服务，但是你不能。在你老婆睡觉时你想了一整晚，终于在早上想到一个完美计划。你叫醒了老婆：亲爱的，我们以后这样做：

> * 无论我们谁收到一条更新信息（客户让我们记住一些东西），在通话结束前我们都必须告诉对方。
> * 这样我们都能记下所有更新。
> * 当有一个查询电话的时候（客户查询让我们记住的信息），我们就无需互相询问──因为我们都已经记住了最新的信息。

“现在只剩下一个问题”，你说，“我们俩都必须参与到一个「更新」请求中，所以我们不能在那段时间内并行工作了。比如，你收到一个更新请求然后告诉我去记住更新，这时我就不能接其他电话了。但这是可以接受的，因为大部分请求都是「查询」（客户更新一次但是会查询多次）。另外，无论如何我们都不能给出错误信息。”

“不错”，你老婆说，“但是你刚才想出来的这个新系统还是有一个缺陷：如果我们俩有一个人有一天不能来上班怎么办？那天，我们不能接受任何「更新」电话，因为另一个人不能做更新操作！这样我们就有了*可用性*问题，比如，我接到了一个更新电话，但是我结束不了那通电话，因为即使我已经在笔记本上记住了更新操作，但是你做不了更新操作！”

### 第五章：你想到了一个最伟大的解决方案

你开始意识到分布式系统并不像一开始你想的那么简单。一个*一致并且可用*的解决方案有这么难吗？对别人来说也许是，但是你不一样！第二天一早你想出了一个你的竞争对手做梦也想不到的方案！于是你又把你老婆叫醒了。

“你看”，你说，“这样做我们就能即*一致*又*可用*”。这个方案和你昨天的方案很类似：

> * 当我们接到一个更新请求时（客户让我们记住一些东西），在结束通话之前，如果对方在办公室，我们直接告诉对方，这样我们都能记录所有更新。
> * 但是如果对方不在（没来工作），我们给对方发一封关于本次更新的邮件。
> * 第二天当对方上班的时候，在接客户电话之前他的第一件事是查看邮件并记录更新。

“天才！”，你老婆说，“这个系统我找不到任何缺陷，马上开始执行！记忆有限公司现在是*一致并且可用*。”

### 第六章：老婆发火了

在一段时间里一切正常。你的系统是一致的，甚至你们俩有一个没来工作时也一切正常。但是如果你们*都*来了而有个人没有提醒对方更新呢？想想你最近做的──经常早早把老婆叫醒说你的那些伟大想法的废话。如果更新的电话是你老婆接到，但是她因为生你的气没有叫你一起更新怎么办。你的创意就完蛋了！现在你的想法是*一致*和*可用*的，但是不能保证*分区容错*！

你为了保证*分区容错*可以在和你老婆和好之前不接任何电话──但是你的系统又是不*可用*的了。

### 第七章：结论

现在再让我们看看 CAP 理论。它阐述的是在你设计一个分布式系统时，你不能同时满足*一致性*、*可用性*和*分区容错性*：

* 一致性：一旦你的客户更新的信息，他们打电话来查询时总能得到最新的信息──无论他们多快打来查询电话。
* 可用性：只要你和你老婆有一个人来上班，记忆有限公司总能提供信息。
* 分区容错性：如果你和你老婆的联系断了记忆有限公司还是能正常工作。

### 彩蛋：雇佣一个跑腿的职员来保证最终一致性

你的想法还有一个补充。你可以雇佣一个跑腿的职员，当一个人的笔记本更新时，他会更新另一个人的笔记本。这个方法最大的好处在于，跑腿职员可以在后台工作，如果你或你老婆接到一个更新请求时，你们俩不会阻塞在等待对方更新上。这是很多 NoSQL 系统工作的方式：一个节点在它本地更新，一个后台进程同步其他节点。唯一的问题是会在一段时间内失去系统*一致性*。比如，你老婆接到了一个更新请求，在跑腿职员更新你的笔记本之前，你接到了同一客户的查询请求，所以客户就得不到*一致*的响应。但是如果能限制这种场景的话，这并不是一个坏主意──比如，假设客户不会非常快地忘记事情，他们只会在5分钟之后来电。

这就是用最简单的话解释 CAP 理论和最终一致性：）。<commit_msg>redis<commit_after>---
title: 用最简单的语言介绍 CAP 理论
category: ['分布式']
tags: ['分布式']
author: 景阳
email: <EMAIL>
description: CAP
---

原文来自[A plain english introduction to CAP Theorem](http://ksat.me/a-plain-english-introduction-to-cap-theorem/)。

---

你可能经常听说 CAP 理论，它在分布式系统的设计中指明了一些上限。和其他的一些教程不同，我尝试用现实世界的一些情形来解释 CAP 理论。

### 第一章：“记忆有限公司”──你的新冒险

昨晚因为你记得老婆的生日并送给她一个生日礼物，你老婆大大地赞赏了你，也因此一个奇怪的念头出现在了你的脑海里：很多人都记不住东西，但是我非常擅长记忆，为什么我不利用这个优势来创业呢？你想得越多，就越喜欢这个主意。事实上你都已经想好了一个报纸广告来推广你的想法：

> 记忆有限公司──不用记住也不会忘记！
>
> 你还在因为记不住东西而苦恼吗？不要担心，现在你只需一个电话！
>
> 当你要记住东西时，拨打 555--55-REMEM 把要记住的信息告诉我们。比如，如果你忘了老板的电话，只要你曾经打电话让我们记住，现在只要拨打 555--55-REMEM，我们会告诉你！
>
> 收费：每次仅一毛！

所以，一个典型的电话对话就像这样：
> * 客户：你好，帮我把邻居的生日记住。
> * 你：好的，他的生日是？
> * 客户：1月2号。
> * 你（把生日记在你的笔记本里）：记住了。在您需要您邻居生日的时候给我们打电话！
> * 客户：谢谢。
> * 你：不客气！本次收费 0.1 元。

### 第二章：扩大业务

你得到了 YCombinator 的投资。你的*创意*如此简单，只需要一个笔记本一部电话，所以你的生意像野火一样在扩张。现在你每天要接几百个电话。

现在你遇到了问题：越来越多的客户要等很久才能接通你的电话。很多人在厌倦了等待后甚至挂断了电话。更有甚者，如果你因为生病一天没上班的话，你就会错过一整天的生意──更不用提那些因此得不到信息而不满的老客户。

你觉得是时候扩大生意了，你的老婆必须帮你一把。

你开始了一个简单的计划：
1. 你和你老婆每人一部电话。
2. 客户们仍然只需拨打 (555)–55-REMEM 一个电话。
3. 电话交换机会把电话平均地分给你和你老婆。

### 第三章：你有了第一个“糟糕的服务”

在新系统上线两天后，你接到了一个老客户 Jhon 的电话：

> * Jhon：你好。
> * 你：感谢致电！有什么可以帮您的？
> *  Jhon：麻烦告诉我到新德里的航班时间。
> *  你：好的，请稍等。
>   （你翻找笔记本，上面没有记录 Jhon 的航班时间！）
> * 你：先生，有点问题。您没有告诉过我您的航班时间。
> *  Jhon：什么？我昨天刚告诉你们！（挂断了电话）

怎么会这样？是 Jhon 在说谎吗？你想了一会，一个想法突然冒了出来：Jhon 是不是昨天给你老婆打了电话？你跑去找你老婆──确实是这样。你把这事告诉了你老婆，她也发现了这个问题。

你的分布式设计出现了一个巨大的漏洞！你的分布式系统是不*一致*的！总有一种可能，客户在一个人那里更新了一些信息，但是在查询的时候电话被另一个人接到。

### 第四章：你修正了一致性错误

你的竞争者也许会无视这个糟糕的服务，但是你不能。在你老婆睡觉时你想了一整晚，终于在早上想到一个完美计划。你叫醒了老婆：亲爱的，我们以后这样做：

> * 无论我们谁收到一条更新信息（客户让我们记住一些东西），在通话结束前我们都必须告诉对方。
> * 这样我们都能记下所有更新。
> * 当有一个查询电话的时候（客户查询让我们记住的信息），我们就无需互相询问──因为我们都已经记住了最新的信息。

“现在只剩下一个问题”，你说，“我们俩都必须参与到一个「更新」请求中，所以我们不能在那段时间内并行工作了。比如，你收到一个更新请求然后告诉我去记住更新，这时我就不能接其他电话了。但这是可以接受的，因为大部分请求都是「查询」（客户更新一次但是会查询多次）。另外，无论如何我们都不能给出错误信息。”

“不错”，你老婆说，“但是你刚才想出来的这个新系统还是有一个缺陷：如果我们俩有一个人有一天不能来上班怎么办？那天，我们不能接受任何「更新」电话，因为另一个人不能做更新操作！这样我们就有了*可用性*问题，比如，我接到了一个更新电话，但是我结束不了那通电话，因为即使我已经在笔记本上记住了更新操作，但是你做不了更新操作！”

### 第五章：你想到了一个最伟大的解决方案

你开始意识到分布式系统并不像一开始你想的那么简单。一个*一致并且可用*的解决方案有这么难吗？对别人来说也许是，但是你不一样！第二天一早你想出了一个你的竞争对手做梦也想不到的方案！于是你又把你老婆叫醒了。

“你看”，你说，“这样做我们就能即*一致*又*可用*”。这个方案和你昨天的方案很类似：

> * 当我们接到一个更新请求时（客户让我们记住一些东西），在结束通话之前，如果对方在办公室，我们直接告诉对方，这样我们都能记录所有更新。
> * 但是如果对方不在（没来工作），我们给对方发一封关于本次更新的邮件。
> * 第二天当对方上班的时候，在接客户电话之前他的第一件事是查看邮件并记录更新。

“天才！”，你老婆说，“这个系统我找不到任何缺陷，马上开始执行！记忆有限公司现在是*一致并且可用*。”

### 第六章：老婆发火了

在一段时间里一切正常。你的系统是一致的，甚至你们俩有一个没来工作时也一切正常。但是如果你们*都*来了而有个人没有提醒对方更新呢？想想你最近做的──经常早早把老婆叫醒说你的那些伟大想法的废话。如果更新的电话是你老婆接到，但是她因为生你的气没有叫你一起更新怎么办。你的创意就完蛋了！现在你的想法是*一致*和*可用*的，但是不能保证*分区容错*！

你为了保证*分区容错*可以在和你老婆和好之前不接任何电话──但是你的系统又是不*可用*的了。

### 第七章：结论

现在再让我们看看 CAP 理论。它阐述的是在你设计一个分布式系统时，你不能同时满足*一致性*、*可用性*和*分区容错性*：

* 一致性：一旦你的客户更新的信息，他们打电话来查询时总能得到最新的信息──无论他们多快打来查询电话。
* 可用性：只要你和你老婆有一个人来上班，记忆有限公司总能提供信息。
* 分区容错性：如果你和你老婆的联系断了记忆有限公司还是能正常工作。

### 彩蛋：雇佣一个跑腿的职员来保证最终一致性

你的想法还有一个补充。你可以雇佣一个跑腿的职员，当一个人的笔记本更新时，他会更新另一个人的笔记本。这个方法最大的好处在于，跑腿职员可以在后台工作，如果你或你老婆接到一个更新请求时，你们俩不会阻塞在等待对方更新上。这是很多 NoSQL 系统工作的方式：一个节点在它本地更新，一个后台进程同步其他节点。唯一的问题是会在一段时间内失去系统*一致性*。比如，你老婆接到了一个更新请求，在跑腿职员更新你的笔记本之前，你接到了同一客户的查询请求，所以客户就得不到*一致*的响应。但是如果能限制这种场景的话，这并不是一个坏主意──比如，假设客户不会非常快地忘记事情，他们只会在5分钟之后来电。

这就是用最简单的语言解释 CAP 理论和最终一致性：）。
{'fraction_non_alphanumeric': 0.13776796973518285, 'fraction_numerical': 0.007250945775535939, 'mean_word_length': 12.30188679245283, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 57, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7452429', 'n_tokens_mistral': 6134, 'n_tokens_neox': 7106, 'n_words': 219}