mirror of
https://github.com/Vonng/ddia.git
synced 2024-12-06 15:20:12 +08:00
remove unexpected leading whitespaces in quoted contents
This commit is contained in:
parent
911577135c
commit
feb7bd3ea6
6
ch1.md
6
ch1.md
@ -238,11 +238,11 @@
|
||||
|
||||
> #### 实践中的百分位点
|
||||
>
|
||||
> 在多重调用的后端服务里,高百分位数变得特别重要。即使并行调用,最终用户请求仍然需要等待最慢的并行调用完成。如[图1-5](img/fig1-5.png)所示,只需要一个缓慢的调用就可以使整个最终用户请求变慢。即使只有一小部分后端调用速度较慢,如果最终用户请求需要多个后端调用,则获得较慢调用的机会也会增加,因此较高比例的最终用户请求速度会变慢(效果称为尾部延迟放大【24】)。
|
||||
> 在多重调用的后端服务里,高百分位数变得特别重要。即使并行调用,最终用户请求仍然需要等待最慢的并行调用完成。如[图1-5](img/fig1-5.png)所示,只需要一个缓慢的调用就可以使整个最终用户请求变慢。即使只有一小部分后端调用速度较慢,如果最终用户请求需要多个后端调用,则获得较慢调用的机会也会增加,因此较高比例的最终用户请求速度会变慢(效果称为尾部延迟放大【24】)。
|
||||
>
|
||||
> 如果你想将响应时间百分点添加到你的服务的监视仪表板,则需要持续有效地计算它们。例如,你可能希望在最近10分钟内保持请求响应时间的滚动窗口。每一分钟,你都会计算出该窗口中的中值和各种百分数,并将这些度量值绘制在图上。
|
||||
> 如果你想将响应时间百分点添加到你的服务的监视仪表板,则需要持续有效地计算它们。例如,你可能希望在最近10分钟内保持请求响应时间的滚动窗口。每一分钟,你都会计算出该窗口中的中值和各种百分数,并将这些度量值绘制在图上。
|
||||
>
|
||||
> 简单的实现是在时间窗口内保存所有请求的响应时间列表,并且每分钟对列表进行排序。如果对你来说效率太低,那么有一些算法能够以最小的CPU和内存成本(如前向衰减【25】,t-digest【26】或HdrHistogram 【27】)来计算百分位数的近似值。请注意,平均百分比(例如,减少时间分辨率或合并来自多台机器的数据)在数学上没有意义 - 聚合响应时间数据的正确方法是添加直方图【28】。
|
||||
> 简单的实现是在时间窗口内保存所有请求的响应时间列表,并且每分钟对列表进行排序。如果对你来说效率太低,那么有一些算法能够以最小的CPU和内存成本(如前向衰减【25】,t-digest【26】或HdrHistogram 【27】)来计算百分位数的近似值。请注意,平均百分比(例如,减少时间分辨率或合并来自多台机器的数据)在数学上没有意义 - 聚合响应时间数据的正确方法是添加直方图【28】。
|
||||
|
||||
![](img/fig1-5.png)
|
||||
|
||||
|
12
ch10.md
12
ch10.md
@ -4,7 +4,7 @@
|
||||
|
||||
> 带有太强个人色彩的系统无法成功。当最初的设计完成并且相对稳定时,不同的人们以自己的方式进行测试,真正的考验才开始。
|
||||
>
|
||||
> ——高德纳
|
||||
> —— 高德纳
|
||||
|
||||
---------------
|
||||
|
||||
@ -65,11 +65,11 @@ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36"
|
||||
|
||||
```bash
|
||||
cat /var/log/nginx/access.log | #1
|
||||
awk '{print $7}' | #2
|
||||
sort | #3
|
||||
uniq -c | #4
|
||||
sort -r -n | #5
|
||||
head -n 5 #6
|
||||
awk '{print $7}' | #2
|
||||
sort | #3
|
||||
uniq -c | #4
|
||||
sort -r -n | #5
|
||||
head -n 5 #6
|
||||
```
|
||||
|
||||
1. 读取日志文件
|
||||
|
2
ch11.md
2
ch11.md
@ -4,7 +4,7 @@
|
||||
|
||||
> 有效的复杂系统总是从简单的系统演化而来。 反之亦然:从零设计的复杂系统没一个能有效工作的。
|
||||
>
|
||||
> ——约翰·加尔,Systemantics(1975)
|
||||
> —— 约翰·加尔,Systemantics(1975)
|
||||
|
||||
---------------
|
||||
|
||||
|
14
ch12.md
14
ch12.md
@ -4,7 +4,7 @@
|
||||
|
||||
> 如果船长的终极目标是保护船只,他应该永远待在港口。
|
||||
>
|
||||
> ——圣托马斯·阿奎那《神学大全》(1265-1274)
|
||||
> —— 圣托马斯·阿奎那《神学大全》(1265-1274)
|
||||
|
||||
---------------
|
||||
|
||||
@ -117,11 +117,11 @@ Spark在批处理引擎上执行流处理,将流分解为**微批次(microba
|
||||
|
||||
> ### 铁路上的模式迁移
|
||||
>
|
||||
> 大规模的“模式迁移”也发生在非计算机系统中。例如,在19世纪英国铁路建设初期,轨距(两轨之间的距离)就有了各种各样的竞争标准。为一种轨距而建的列车不能在另一种轨距的轨道上运行,这限制了火车网络中可能的相互连接【9】。
|
||||
> 大规模的“模式迁移”也发生在非计算机系统中。例如,在19世纪英国铁路建设初期,轨距(两轨之间的距离)就有了各种各样的竞争标准。为一种轨距而建的列车不能在另一种轨距的轨道上运行,这限制了火车网络中可能的相互连接【9】。
|
||||
>
|
||||
> 在1846年最终确定了一个标准轨距之后,其他轨距的轨道必须转换 —— 但是如何在不停运火车线路的情况下进行数月甚至数年的迁移?解决的办法是首先通过添加第三条轨道将轨道转换为**双轨距(dual guage)** 或**混合轨距**。这种转换可以逐渐完成,当完成时,两种轨距的列车都可以在线路上跑,使用三条轨道中的两条。事实上,一旦所有的列车都转换成标准轨距,那么可以移除提供非标准轨距的轨道。
|
||||
> 在1846年最终确定了一个标准轨距之后,其他轨距的轨道必须转换 —— 但是如何在不停运火车线路的情况下进行数月甚至数年的迁移?解决的办法是首先通过添加第三条轨道将轨道转换为**双轨距(dual guage)** 或**混合轨距**。这种转换可以逐渐完成,当完成时,两种轨距的列车都可以在线路上跑,使用三条轨道中的两条。事实上,一旦所有的列车都转换成标准轨距,那么可以移除提供非标准轨距的轨道。
|
||||
>
|
||||
> 以这种方式“再加工”现有的轨道,让新旧版本并存,可以在几年的时间内逐渐改变轨距。然而,这是一项昂贵的事业,这就是今天非标准轨距仍然存在的原因。例如,旧金山湾区的BART系统使用了与美国大部分地区不同的轨距。
|
||||
> 以这种方式“再加工”现有的轨道,让新旧版本并存,可以在几年的时间内逐渐改变轨距。然而,这是一项昂贵的事业,这就是今天非标准轨距仍然存在的原因。例如,旧金山湾区的BART系统使用了与美国大部分地区不同的轨距。
|
||||
|
||||
衍生视图允许**渐进演化(gradual evolution)**。如果你想重新构建数据集,不需要执行突然切换式的迁移。取而代之的是,你可以将旧架构和新架构并排维护为相同基础数据上的两个独立衍生视图。然后可以开始将少量用户转移到新视图,以测试其性能并发现任何错误,而大多数用户仍然会被路由到旧视图。你可以逐渐地增加访问新视图的用户比例,最终可以删除旧视图【10】。
|
||||
|
||||
@ -490,7 +490,7 @@ COMMIT;
|
||||
|
||||
抑制重复事务的这种情况只是一个更普遍的原则的一个例子,这个原则被称为**端到端原则(end-to-end argument)**,它在1984年由Saltzer,Reed和Clark阐述【55】:
|
||||
|
||||
> 只有在通信系统两端应用的知识与帮助下,所讨论的功能才能完全地正确地实现。因而将这种被质疑的功能作为通信系统本身的功能是不可能的。 (有时,通信系统可以提供这种功能的不完备版本,可能有助于提高性能。)
|
||||
> 只有在通信系统两端应用的知识与帮助下,所讨论的功能才能完全地正确地实现。因而将这种被质疑的功能作为通信系统本身的功能是不可能的。 (有时,通信系统可以提供这种功能的不完备版本,可能有助于提高性能。)
|
||||
>
|
||||
|
||||
在我们的例子中**所讨论的功能**是重复抑制。我们看到TCP在TCP连接层次抑制了重复的数据包,一些流处理器在消息处理层次提供了所谓的恰好一次语义,但这些都无法阻止当一个请求超时时,用户亲自提交重复的请求。TCP,数据库事务,以及流处理器本身并不能完全排除这些重复。解决这个问题需要一个端到端的解决方案:从终端用户的客户端一路传递到数据库的事务标识符。
|
||||
@ -846,9 +846,9 @@ ACID意义下的一致性(请参阅“[一致性](ch7.md#一致性)”)基
|
||||
|
||||
就像工业革命有着黑暗面需要应对一样,我们转向信息时代的过程中,也有需要应对与解决的重大问题。我相信数据的收集与使用就是其中一个问题。用Bruce Schneier的话来说【96】:
|
||||
|
||||
> 数据是信息时代的污染问题,保护隐私是环境挑战。几乎所有的电脑都能生产信息。它堆积在周围,开始溃烂。我们如何处理它 —— 我们如何控制它,以及如何摆脱它 —— 是信息经济健康发展的核心议题。正如我们今天回顾工业时代的早期年代,并想知道我们的祖先在忙于建设工业世界的过程时怎么能忽略污染问题;我们的孙辈在回望信息时代的早期年代时,将会就我们如何应对数据收集和滥用的挑战来评断我们。
|
||||
> 数据是信息时代的污染问题,保护隐私是环境挑战。几乎所有的电脑都能生产信息。它堆积在周围,开始溃烂。我们如何处理它 —— 我们如何控制它,以及如何摆脱它 —— 是信息经济健康发展的核心议题。正如我们今天回顾工业时代的早期年代,并想知道我们的祖先在忙于建设工业世界的过程时怎么能忽略污染问题;我们的孙辈在回望信息时代的早期年代时,将会就我们如何应对数据收集和滥用的挑战来评断我们。
|
||||
>
|
||||
> 我们应该设法让他们感到骄傲。
|
||||
> 我们应该设法让他们感到骄傲。
|
||||
|
||||
#### 立法与自律
|
||||
|
||||
|
8
ch2.md
8
ch2.md
@ -45,7 +45,7 @@
|
||||
|
||||
### NoSQL的诞生
|
||||
|
||||
现在 - 2010年代,NoSQL开始了最新一轮尝试,试图推翻关系模型的统治地位。“NoSQL”这个名字让人遗憾,因为实际上它并没有涉及到任何特定的技术。最初它只是作为一个醒目的Twitter标签,用在2009年一个关于分布式,非关系数据库上的开源聚会上。无论如何,这个术语触动了某些神经,并迅速在网络创业社区内外传播开来。好些有趣的数据库系统现在都与*#NoSQL#*标签相关联,并且NoSQL被追溯性地重新解释为**不仅是SQL(Not Only SQL)** 【4】。
|
||||
现在 - 2010年代,NoSQL开始了最新一轮尝试,试图推翻关系模型的统治地位。“NoSQL”这个名字让人遗憾,因为实际上它并没有涉及到任何特定的技术。最初它只是作为一个醒目的Twitter标签,用在2009年一个关于分布式,非关系数据库上的开源聚会上。无论如何,这个术语触动了某些神经,并迅速在网络创业社区内外传播开来。好些有趣的数据库系统现在都与 *#NoSQL* 标签相关联,并且NoSQL被追溯性地重新解释为**不仅是SQL(Not Only SQL)** 【4】。
|
||||
|
||||
采用NoSQL数据库的背后有几个驱动因素,其中包括:
|
||||
|
||||
@ -257,8 +257,8 @@ if (user && user.name && !user.first_name) {
|
||||
|
||||
```sql
|
||||
ALTER TABLE users ADD COLUMN first_name text;
|
||||
UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL
|
||||
UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
|
||||
UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL
|
||||
UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
|
||||
```
|
||||
|
||||
模式变更的速度很慢,而且要求停运。它的这种坏名誉并不是完全应得的:大多数关系数据库系统可在几毫秒内执行`ALTER TABLE`语句。MySQL是一个值得注意的例外,它执行`ALTER TABLE`时会复制整个表,这可能意味着在更改一个大型表时会花费几分钟甚至几个小时的停机时间,尽管存在各种工具来解决这个限制【24,25,26】。
|
||||
@ -412,7 +412,7 @@ for (var i = 0; i < liElements.length; i++) {
|
||||
|
||||
在Web浏览器中,使用声明式CSS样式比使用JavaScript命令式地操作样式要好得多。类似地,在数据库中,使用像SQL这样的声明式查询语言比使用命令式查询API要好得多[^vi]。
|
||||
|
||||
[^vi]: vi IMS和CODASYL都使用命令式API。应用程序通常使用COBOL代码遍历数据库中的记录,一次一条记录【2,16】。
|
||||
[^vi]: IMS和CODASYL都使用命令式API。应用程序通常使用COBOL代码遍历数据库中的记录,一次一条记录【2,16】。
|
||||
|
||||
### MapReduce查询
|
||||
|
||||
|
2
ch4.md
2
ch4.md
@ -4,7 +4,7 @@
|
||||
|
||||
> 唯变所适
|
||||
>
|
||||
> ——以弗所的赫拉克利特,为柏拉图所引(公元前360年)
|
||||
> —— 以弗所的赫拉克利特,为柏拉图所引(公元前360年)
|
||||
>
|
||||
|
||||
-------------------
|
||||
|
32
ch5.md
32
ch5.md
@ -4,7 +4,7 @@
|
||||
|
||||
> 与可能出错的东西比,'不可能'出错的东西最显著的特点就是:一旦真的出错,通常就彻底玩完了。
|
||||
>
|
||||
> ——道格拉斯·亚当斯(1992)
|
||||
> —— 道格拉斯·亚当斯(1992)
|
||||
|
||||
------
|
||||
|
||||
@ -249,21 +249,21 @@ PostgreSQL和Oracle等使用这种复制方法【16】。主要缺点是日志
|
||||
|
||||
第三个复制延迟例子违反了因果律。 想象一下Poons先生和Cake夫人之间的以下简短对话:
|
||||
|
||||
> *Mr. Poons*
|
||||
> Mrs. Cake,你能看到多远的未来?
|
||||
>
|
||||
> *Mrs. Cake*
|
||||
> 通常约十秒钟,Mr. Poons.
|
||||
*Mr. Poons*
|
||||
> Mrs. Cake,你能看到多远的未来?
|
||||
|
||||
*Mrs. Cake*
|
||||
> 通常约十秒钟,Mr. Poons.
|
||||
|
||||
这两句话之间有因果关系:Cake夫人听到了Poons先生的问题并回答了这个问题。
|
||||
|
||||
现在,想象第三个人正在通过从库来听这个对话。 Cake夫人说的内容是从一个延迟很低的从库读取的,但Poons先生所说的内容,从库的延迟要大的多(见[图5-5](img/fig5-5.png))。 于是,这个观察者会听到以下内容:
|
||||
|
||||
> *Mrs. Cake*
|
||||
> 通常约十秒钟,Mr. Poons.
|
||||
>
|
||||
> *Mr. Poons*
|
||||
> Mrs. Cake,你能看到多远的未来?
|
||||
*Mrs. Cake*
|
||||
> 通常约十秒钟,Mr. Poons.
|
||||
|
||||
*Mr. Poons*
|
||||
> Mrs. Cake,你能看到多远的未来?
|
||||
|
||||
对于观察者来说,看起来好像Cake夫人在Poons先生提问前就回答了这个问题。
|
||||
这种超能力让人印象深刻,但也会把人搞糊涂。【25】。
|
||||
@ -410,7 +410,7 @@ PostgreSQL和Oracle等使用这种复制方法【16】。主要缺点是日志
|
||||
|
||||
> #### 自动冲突解决
|
||||
>
|
||||
> 冲突解决规则可能很快变得复杂,并且自定义代码可能容易出错。亚马逊是一个经常被引用的例子,由于冲突解决处理程序令人意外的效果:一段时间以来,购物车上的冲突解决逻辑将保留添加到购物车的物品,但不包括从购物车中移除的物品。因此,顾客有时会看到物品重新出现在他们的购物车中,即使他们之前已经被移走【37】。
|
||||
> 冲突解决规则可能很快变得复杂,并且自定义代码可能容易出错。亚马逊是一个经常被引用的例子,由于冲突解决处理程序令人意外的效果:一段时间以来,购物车上的冲突解决逻辑将保留添加到购物车的物品,但不包括从购物车中移除的物品。因此,顾客有时会看到物品重新出现在他们的购物车中,即使他们之前已经被移走【37】。
|
||||
>
|
||||
> 已经有一些有趣的研究来自动解决由于数据修改引起的冲突。有几行研究值得一提:
|
||||
>
|
||||
@ -642,11 +642,11 @@ LWW实现了最终收敛的目标,但以**持久性**为代价:如果同一
|
||||
|
||||
> #### 并发性,时间和相对性
|
||||
>
|
||||
> 如果两个操作 **“同时”** 发生,似乎应该称为并发——但事实上,它们在字面时间上重叠与否并不重要。由于分布式系统中的时钟问题,现实中是很难判断两个事件是否**同时**发生的,这个问题我们将在[第八章](ch8.md)中详细讨论。
|
||||
> 如果两个操作 **“同时”** 发生,似乎应该称为并发——但事实上,它们在字面时间上重叠与否并不重要。由于分布式系统中的时钟问题,现实中是很难判断两个事件是否**同时**发生的,这个问题我们将在[第八章](ch8.md)中详细讨论。
|
||||
>
|
||||
> 为了定义并发性,确切的时间并不重要:如果两个操作都意识不到对方的存在,就称这两个操作**并发**,而不管它们发生的物理时间。人们有时把这个原理和狭义相对论的物理学联系起来【54】,它引入了信息不能比光速更快的思想。因此,如果两个事件发生的时间差小于光通过它们之间的距离所需要的时间,那么这两个事件不可能相互影响。
|
||||
> 为了定义并发性,确切的时间并不重要:如果两个操作都意识不到对方的存在,就称这两个操作**并发**,而不管它们发生的物理时间。人们有时把这个原理和狭义相对论的物理学联系起来【54】,它引入了信息不能比光速更快的思想。因此,如果两个事件发生的时间差小于光通过它们之间的距离所需要的时间,那么这两个事件不可能相互影响。
|
||||
>
|
||||
> 在计算机系统中,即使光速原则上允许一个操作影响另一个操作,但两个操作也可能是**并行的**。例如,如果网络缓慢或中断,两个操作间可能会出现一段时间间隔,但仍然是并发的,因为网络问题阻止一个操作意识到另一个操作的存在。
|
||||
> 在计算机系统中,即使光速原则上允许一个操作影响另一个操作,但两个操作也可能是**并行的**。例如,如果网络缓慢或中断,两个操作间可能会出现一段时间间隔,但仍然是并发的,因为网络问题阻止一个操作意识到另一个操作的存在。
|
||||
|
||||
|
||||
#### 捕获"此前发生"关系
|
||||
@ -706,7 +706,7 @@ LWW实现了最终收敛的目标,但以**持久性**为代价:如果同一
|
||||
|
||||
> #### 版本向量和向量时钟
|
||||
>
|
||||
> 版本向量有时也被称为矢量时钟,即使它们不完全相同。 差别很微妙——细节请参阅参考资料【57,60,61】。 简而言之,在比较副本的状态时,版本向量是正确的数据结构。
|
||||
> 版本向量有时也被称为矢量时钟,即使它们不完全相同。 差别很微妙——细节请参阅参考资料【57,60,61】。 简而言之,在比较副本的状态时,版本向量是正确的数据结构。
|
||||
>
|
||||
|
||||
## 本章小结
|
||||
|
6
ch6.md
6
ch6.md
@ -17,7 +17,7 @@
|
||||
|
||||
> #### 术语澄清
|
||||
>
|
||||
> 上文中的**分区(partition)**,在MongoDB,Elasticsearch和Solr Cloud中被称为**分片(shard)**,在HBase中称之为**区域(Region)**,Bigtable中则是 **表块(tablet)**,Cassandra和Riak中是**虚节点(vnode)**,Couchbase中叫做**虚桶(vBucket)**。但是**分区(partitioning)** 是最约定俗成的叫法。
|
||||
> 上文中的**分区(partition)**,在MongoDB,Elasticsearch和Solr Cloud中被称为**分片(shard)**,在HBase中称之为**区域(Region)**,Bigtable中则是 **表块(tablet)**,Cassandra和Riak中是**虚节点(vnode)**,Couchbase中叫做**虚桶(vBucket)**。但是**分区(partitioning)** 是最约定俗成的叫法。
|
||||
>
|
||||
|
||||
通常情况下,每条数据(每条记录,每行或每个文档)属于且仅属于一个分区。有很多方法可以实现这一点,本章将进行深入讨论。实际上,每个分区都是自己的小型数据库,尽管数据库可能支持同时进行多个分区的操作。
|
||||
@ -89,9 +89,9 @@
|
||||
|
||||
> #### 一致性哈希
|
||||
>
|
||||
> 一致性哈希由Karger等人定义。【7】 用于跨互联网级别的缓存系统,例如CDN中,是一种能均匀分配负载的方法。它使用随机选择的 **分区边界(partition boundaries)** 来避免中央控制或分布式共识的需要。 请注意,这里的一致性与复制一致性(请参阅[第五章](ch5.md))或ACID一致性(请参阅[第七章](ch7.md))无关,而只是描述了一种重新平衡(reblancing)的特定方法。
|
||||
> 一致性哈希由Karger等人定义。【7】 用于跨互联网级别的缓存系统,例如CDN中,是一种能均匀分配负载的方法。它使用随机选择的 **分区边界(partition boundaries)** 来避免中央控制或分布式共识的需要。 请注意,这里的一致性与复制一致性(请参阅[第五章](ch5.md))或ACID一致性(请参阅[第七章](ch7.md))无关,而只是描述了一种重新平衡(reblancing)的特定方法。
|
||||
>
|
||||
> 正如我们将在“[分区再平衡](#分区再平衡)”中所看到的,这种特殊的方法对于数据库实际上并不是很好,所以在实际中很少使用(某些数据库的文档仍然会使用一致性哈希的说法,但是它往往是不准确的)。 因为有可能产生混淆,所以最好避免使用一致性哈希这个术语,而只是把它称为**散列分区(hash partitioning)**。
|
||||
> 正如我们将在“[分区再平衡](#分区再平衡)”中所看到的,这种特殊的方法对于数据库实际上并不是很好,所以在实际中很少使用(某些数据库的文档仍然会使用一致性哈希的说法,但是它往往是不准确的)。 因为有可能产生混淆,所以最好避免使用一致性哈希这个术语,而只是把它称为**散列分区(hash partitioning)**。
|
||||
|
||||
不幸的是,通过使用键散列进行分区,我们失去了键范围分区的一个很好的属性:高效执行范围查询的能力。曾经相邻的键现在分散在所有分区中,所以它们之间的顺序就丢失了。在MongoDB中,如果你使用了基于散列的分区模式,则任何范围查询都必须发送到所有分区【4】。Riak 【9】,Couchbase 【10】或Voldemort不支持主键上的范围查询。
|
||||
|
||||
|
4
ch7.md
4
ch7.md
@ -2,9 +2,9 @@
|
||||
|
||||
![](img/ch7.png)
|
||||
|
||||
> 一些作者声称,支持通用的两阶段提交代价太大,会带来性能与可用性的问题。让程序员来处理过度使用事务导致的性能问题,总比缺少事务编程好得多。
|
||||
> 一些作者声称,支持通用的两阶段提交代价太大,会带来性能与可用性的问题。让程序员来处理过度使用事务导致的性能问题,总比缺少事务编程好得多。
|
||||
>
|
||||
> ——James Corbett等人,Spanner:Google的全球分布式数据库(2012)
|
||||
> —— James Corbett等人,Spanner:Google的全球分布式数据库(2012)
|
||||
|
||||
------
|
||||
|
||||
|
48
ch8.md
48
ch8.md
@ -10,7 +10,7 @@
|
||||
>
|
||||
> 无食我数
|
||||
>
|
||||
> —— Kyle Kingsbury, Carly Rae Jepsen 《网络分区的危害》(2013年)[^译著1]
|
||||
> —— Kyle Kingsbury, Carly Rae Jepsen 《网络分区的危害》(2013年)[^译著1]
|
||||
|
||||
---------
|
||||
|
||||
@ -39,9 +39,9 @@
|
||||
|
||||
当你编写运行在多台计算机上的软件时,情况有本质上的区别。在分布式系统中,我们不再处于理想化的系统模型中,我们别无选择,只能面对现实世界的混乱现实。而在现实世界中,各种各样的事情都可能会出现问题【4】,如下面的轶事所述:
|
||||
|
||||
> 在我有限的经验中,我已经和很多东西打过交道:单个**数据中心(DC)** 中长期存在的网络分区,配电单元PDU故障,交换机故障,整个机架的意外重启,整个数据中心主干网络故障,整个数据中心的电源故障,以及一个低血糖的司机把他的福特皮卡撞在数据中心的HVAC(加热,通风和空调)系统上。而且我甚至不是一个运维。
|
||||
> 在我有限的经验中,我已经和很多东西打过交道:单个**数据中心(DC)** 中长期存在的网络分区,配电单元PDU故障,交换机故障,整个机架的意外重启,整个数据中心主干网络故障,整个数据中心的电源故障,以及一个低血糖的司机把他的福特皮卡撞在数据中心的HVAC(加热,通风和空调)系统上。而且我甚至不是一个运维。
|
||||
>
|
||||
> ——柯达黑尔
|
||||
> —— 柯达黑尔
|
||||
|
||||
在分布式系统中,尽管系统的其他部分工作正常,但系统的某些部分可能会以某种不可预知的方式被破坏。这被称为**部分失效(partial failure)**。难点在于部分失效是**不确定性的(nonderterministic)**:如果你试图做任何涉及多个节点和网络的事情,它有时可能会工作,有时会出现不可预知的失败。正如我们将要看到的,你甚至不知道是否成功了,因为消息通过网络传播的时间也是不确定的!
|
||||
|
||||
@ -122,7 +122,7 @@
|
||||
|
||||
> #### 网络分区
|
||||
>
|
||||
> 当网络的一部分由于网络故障而被切断时,有时称为**网络分区(network partition)** 或**网络断裂(netsplit)**。在本书中,我们通常会坚持使用更一般的术语**网络故障(network fault)**,以避免与[第六章](ch6.md)讨论的存储系统的分区(分片)相混淆。
|
||||
> 当网络的一部分由于网络故障而被切断时,有时称为**网络分区(network partition)** 或**网络断裂(netsplit)**。在本书中,我们通常会坚持使用更一般的术语**网络故障(network fault)**,以避免与[第六章](ch6.md)讨论的存储系统的分区(分片)相混淆。
|
||||
|
||||
即使网络故障在你的环境中非常罕见,故障可能发生的事实,意味着你的软件需要能够处理它们。无论何时通过网络进行通信,都可能会失败,这是无法避免的。
|
||||
|
||||
@ -180,9 +180,9 @@
|
||||
|
||||
> #### TCP与UDP
|
||||
>
|
||||
> 一些对延迟敏感的应用程序,比如视频会议和IP语音(VoIP),使用了UDP而不是TCP。这是在可靠性和和延迟变化之间的折衷:由于UDP不执行流量控制并且不重传丢失的分组,所以避免了网络延迟变化的一些原因(尽管它仍然易受切换队列和调度延迟的影响)。
|
||||
> 一些对延迟敏感的应用程序,比如视频会议和IP语音(VoIP),使用了UDP而不是TCP。这是在可靠性和和延迟变化之间的折衷:由于UDP不执行流量控制并且不重传丢失的分组,所以避免了网络延迟变化的一些原因(尽管它仍然易受切换队列和调度延迟的影响)。
|
||||
>
|
||||
> 在延迟数据毫无价值的情况下,UDP是一个不错的选择。例如,在VoIP电话呼叫中,可能没有足够的时间重新发送丢失的数据包,并在扬声器上播放数据。在这种情况下,重发数据包没有意义——应用程序必须使用静音填充丢失数据包的时隙(导致声音短暂中断),然后在数据流中继续。重试发生在人类层。 (“你能再说一遍吗?声音刚刚断了一会儿。“)
|
||||
> 在延迟数据毫无价值的情况下,UDP是一个不错的选择。例如,在VoIP电话呼叫中,可能没有足够的时间重新发送丢失的数据包,并在扬声器上播放数据。在这种情况下,重发数据包没有意义——应用程序必须使用静音填充丢失数据包的时隙(导致声音短暂中断),然后在数据流中继续。重试发生在人类层。 (“你能再说一遍吗?声音刚刚断了一会儿。“)
|
||||
|
||||
所有这些因素都会造成网络延迟的变化。当系统接近其最大容量时,排队延迟的变化范围特别大:拥有足够备用容量的系统可以轻松排空队列,而在高利用率的系统中,很快就能积累很长的队列。
|
||||
|
||||
@ -224,17 +224,17 @@
|
||||
|
||||
> ### 延迟和资源利用
|
||||
>
|
||||
> 更一般地说,可以将**延迟变化**视为**动态资源分区**的结果。
|
||||
> 更一般地说,可以将**延迟变化**视为**动态资源分区**的结果。
|
||||
>
|
||||
> 假设两台电话交换机之间有一条线路,可以同时进行10,000个呼叫。通过此线路切换的每个电路都占用其中一个呼叫插槽。因此,你可以将线路视为可由多达10,000个并发用户共享的资源。资源以静态方式分配:即使你现在是电话上唯一的电话,并且所有其他9,999个插槽都未使用,你的电路仍将分配与导线充分利用时相同的固定数量的带宽。
|
||||
> 假设两台电话交换机之间有一条线路,可以同时进行10,000个呼叫。通过此线路切换的每个电路都占用其中一个呼叫插槽。因此,你可以将线路视为可由多达10,000个并发用户共享的资源。资源以静态方式分配:即使你现在是电话上唯一的电话,并且所有其他9,999个插槽都未使用,你的电路仍将分配与导线充分利用时相同的固定数量的带宽。
|
||||
>
|
||||
> 相比之下,互联网动态分享网络带宽。发送者互相推挤和争夺,以让他们的数据包尽可能快地通过网络,并且网络交换机决定从一个时刻到另一个时刻发送哪个分组(即,带宽分配)。这种方法有排队的缺点,但其优点是它最大限度地利用了电线。电线固定成本,所以如果你更好地利用它,你通过电线发送的每个字节都会更便宜。
|
||||
> 相比之下,互联网动态分享网络带宽。发送者互相推挤和争夺,以让他们的数据包尽可能快地通过网络,并且网络交换机决定从一个时刻到另一个时刻发送哪个分组(即,带宽分配)。这种方法有排队的缺点,但其优点是它最大限度地利用了电线。电线固定成本,所以如果你更好地利用它,你通过电线发送的每个字节都会更便宜。
|
||||
>
|
||||
> CPU也会出现类似的情况:如果你在多个线程间动态共享每个CPU内核,则一个线程有时必须在操作系统的运行队列里等待,而另一个线程正在运行,这样每个线程都有可能被暂停一个不定的时间长度。但是,与为每个线程分配静态数量的CPU周期相比,这会更好地利用硬件(请参阅“[响应时间保证](#响应时间保证)”)。更好的硬件利用率也是使用虚拟机的重要动机。
|
||||
> CPU也会出现类似的情况:如果你在多个线程间动态共享每个CPU内核,则一个线程有时必须在操作系统的运行队列里等待,而另一个线程正在运行,这样每个线程都有可能被暂停一个不定的时间长度。但是,与为每个线程分配静态数量的CPU周期相比,这会更好地利用硬件(请参阅“[响应时间保证](#响应时间保证)”)。更好的硬件利用率也是使用虚拟机的重要动机。
|
||||
>
|
||||
> 如果资源是静态分区的(例如,专用硬件和专用带宽分配),则在某些环境中可以实现**延迟保证**。但是,这是以降低利用率为代价的——换句话说,它是更昂贵的。另一方面,动态资源分配的多租户提供了更好的利用率,所以它更便宜,但它具有可变延迟的缺点。
|
||||
> 如果资源是静态分区的(例如,专用硬件和专用带宽分配),则在某些环境中可以实现**延迟保证**。但是,这是以降低利用率为代价的——换句话说,它是更昂贵的。另一方面,动态资源分配的多租户提供了更好的利用率,所以它更便宜,但它具有可变延迟的缺点。
|
||||
>
|
||||
> 网络中的可变延迟不是一种自然规律,而只是成本/收益权衡的结果。
|
||||
> 网络中的可变延迟不是一种自然规律,而只是成本/收益权衡的结果。
|
||||
|
||||
|
||||
## 不可靠的时钟
|
||||
@ -377,19 +377,19 @@ Spanner以这种方式实现跨数据中心的快照隔离【59,60】。它使
|
||||
|
||||
```java
|
||||
while (true) {
|
||||
request = getIncomingRequest();
|
||||
// 确保租约还剩下至少10秒
|
||||
if (lease.expiryTimeMillis - System.currentTimeMillis() < 10000){
|
||||
lease = lease.renew();
|
||||
}
|
||||
request = getIncomingRequest();
|
||||
// 确保租约还剩下至少10秒
|
||||
if (lease.expiryTimeMillis - System.currentTimeMillis() < 10000){
|
||||
lease = lease.renew();
|
||||
}
|
||||
|
||||
if (lease.isValid()) {
|
||||
process(request);
|
||||
}
|
||||
if (lease.isValid()) {
|
||||
process(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
这个代码有什么问题?首先,它依赖于同步时钟:租约到期时间由另一台机器设置(例如,当前时间加上30秒,计算到期时间),并将其与本地系统时钟进行比较。如果时钟不同步超过几秒,这段代码将开始做奇怪的事情。
|
||||
这个代码有什么问题?首先,它依赖于同步时钟:租约到期时间由另一台机器设置(例如,当前时间加上30秒,计算到期时间),并将其与本地系统时钟进行比较。如果时钟不同步超过几秒,这段代码将开始做奇怪的事情。
|
||||
|
||||
其次,即使我们将协议更改为仅使用本地单调时钟,也存在另一个问题:代码假定在执行剩余时间检查`System.currentTimeMillis()`和实际执行请求`process(request)`中间的时间间隔非常短。通常情况下,这段代码运行得非常快,所以10秒的缓冲区已经足够确保**租约**在请求处理到一半时不会过期。
|
||||
|
||||
@ -514,11 +514,11 @@ while (true) {
|
||||
|
||||
> ### 拜占庭将军问题
|
||||
>
|
||||
> 拜占庭将军问题是对所谓“两将军问题”的泛化【78】,它想象两个将军需要就战斗计划达成一致的情况。由于他们在两个不同的地点建立了营地,他们只能通过信使进行沟通,信使有时会被延迟或丢失(就像网络中的信息包一样)。我们将在[第九章](ch9.md)讨论这个共识问题。
|
||||
> 拜占庭将军问题是对所谓“两将军问题”的泛化【78】,它想象两个将军需要就战斗计划达成一致的情况。由于他们在两个不同的地点建立了营地,他们只能通过信使进行沟通,信使有时会被延迟或丢失(就像网络中的信息包一样)。我们将在[第九章](ch9.md)讨论这个共识问题。
|
||||
>
|
||||
> 在这个问题的拜占庭版本里,有n位将军需要同意,他们的努力因为有一些叛徒在他们中间而受到阻碍。大多数的将军都是忠诚的,因而发出了真实的信息,但是叛徒可能会试图通过发送虚假或不真实的信息来欺骗和混淆他人(在试图保持未被发现的同时)。事先并不知道叛徒是谁。
|
||||
> 在这个问题的拜占庭版本里,有n位将军需要同意,他们的努力因为有一些叛徒在他们中间而受到阻碍。大多数的将军都是忠诚的,因而发出了真实的信息,但是叛徒可能会试图通过发送虚假或不真实的信息来欺骗和混淆他人(在试图保持未被发现的同时)。事先并不知道叛徒是谁。
|
||||
>
|
||||
> 拜占庭是后来成为君士坦丁堡的古希腊城市,现在在土耳其的伊斯坦布尔。没有任何历史证据表明拜占庭将军比其他地方更容易出现阴谋和阴谋。相反,这个名字来源于拜占庭式的过度复杂,官僚,迂回等意义,早在计算机之前就已经在政治中被使用了【79】。Lamport想要选一个不会冒犯任何读者的国家,他被告知将其称为阿尔巴尼亚将军问题并不是一个好主意【80】。
|
||||
> 拜占庭是后来成为君士坦丁堡的古希腊城市,现在在土耳其的伊斯坦布尔。没有任何历史证据表明拜占庭将军比其他地方更容易出现阴谋和阴谋。相反,这个名字来源于拜占庭式的过度复杂,官僚,迂回等意义,早在计算机之前就已经在政治中被使用了【79】。Lamport想要选一个不会冒犯任何读者的国家,他被告知将其称为阿尔巴尼亚将军问题并不是一个好主意【80】。
|
||||
|
||||
当一个系统在部分节点发生故障、不遵守协议、甚至恶意攻击、扰乱网络时仍然能继续正确工作,称之为**拜占庭容错(Byzantine fault-tolerant)** 的,在特定场景下,这种担忧在是有意义的:
|
||||
|
||||
|
4
ch9.md
4
ch9.md
@ -81,7 +81,7 @@
|
||||
|
||||
在这个例子中,寄存器有两种类型的操作:
|
||||
|
||||
* $ read(x)⇒v$表示客户端请求读取寄存器 `x` 的值,数据库返回值 `v`。
|
||||
* $read(x)⇒v$表示客户端请求读取寄存器 `x` 的值,数据库返回值 `v`。
|
||||
* $write(x,v)⇒r$ 表示客户端请求将寄存器 `x` 设置为值 `v` ,数据库返回响应 `r` (可能正确,可能错误)。
|
||||
|
||||
在[图9-2](img/fig9-2.png) 中,`x` 的值最初为 `0`,客户端C 执行写请求将其设置为 `1`。发生这种情况时,客户端A和B反复轮询数据库以读取最新值。 A和B的请求可能会收到怎样的响应?
|
||||
@ -638,7 +638,7 @@ CAP定理的正式定义仅限于很狭隘的范围【30】,它只考虑了一
|
||||
情况如[图9-10](img/fig9-10.png) 所示。在这个特定的例子中,协调者实际上决定提交,数据库2 收到提交请求。但是,协调者在将提交请求发送到数据库1 之前发生崩溃,因此数据库1 不知道是否提交或中止。即使**超时**在这里也没有帮助:如果数据库1 在超时后单方面中止,它将最终与执行提交的数据库2 不一致。同样,单方面提交也是不安全的,因为另一个参与者可能已经中止了。
|
||||
|
||||
![](img/fig9-10.png)
|
||||
**图9-10 参与者投赞成票后,协调者崩溃。数据库1不知道是否提交或中止**
|
||||
**图9-10 参与者投赞成票后,协调者崩溃。数据库1不知道是否提交或中止**
|
||||
|
||||
没有协调者的消息,参与者无法知道是提交还是放弃。原则上参与者可以相互沟通,找出每个参与者是如何投票的,并达成一致,但这不是2PC协议的一部分。
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
### 无共享架构
|
||||
|
||||
相比之下,**无共享架构**(shared-nothing architecture,有时被称为**水平伸缩**,即horizontal scaling,或**向外伸缩**,即scaling out)已经相当普及。在这种架构中,运行数据库软件的每台机器/虚拟机都称为**节点(node)**。每个节点只使用各自的处理器,内存和磁盘。节点之间的任何协调,都是在软件层面使用传统网络实现的。
|
||||
相比之下,**无共享架构**【3】(shared-nothing architecture,有时被称为**水平伸缩**,即horizontal scaling,或**向外伸缩**,即scaling out)已经相当普及。在这种架构中,运行数据库软件的每台机器/虚拟机都称为**节点(node)**。每个节点只使用各自的处理器,内存和磁盘。节点之间的任何协调,都是在软件层面使用传统网络实现的。
|
||||
|
||||
无共享系统不需要使用特殊的硬件,所以你可以用任意机器——比如性价比最好的机器。你也许可以跨多个地理区域分布数据从而减少用户延迟,或者在损失一整个数据中心的情况下幸免于难。随着云端虚拟机部署的出现,即使是小公司,现在无需Google级别的运维,也可以实现异地分布式架构。
|
||||
|
||||
|
@ -238,11 +238,11 @@
|
||||
|
||||
> #### 實踐中的百分位點
|
||||
>
|
||||
> 在多重呼叫的後端服務裡,高百分位數變得特別重要。即使並行呼叫,終端使用者請求仍然需要等待最慢的並行呼叫完成。如[圖1-5](../img/fig1-5.png)所示,只需要一個緩慢的呼叫就可以使整個終端使用者請求變慢。即使只有一小部分後端呼叫速度較慢,如果終端使用者請求需要多個後端呼叫,則獲得較慢呼叫的機會也會增加,因此較高比例的終端使用者請求速度會變慢(效果稱為尾部延遲放大【24】)。
|
||||
> 在多重呼叫的後端服務裡,高百分位數變得特別重要。即使並行呼叫,終端使用者請求仍然需要等待最慢的並行呼叫完成。如[圖1-5](../img/fig1-5.png)所示,只需要一個緩慢的呼叫就可以使整個終端使用者請求變慢。即使只有一小部分後端呼叫速度較慢,如果終端使用者請求需要多個後端呼叫,則獲得較慢呼叫的機會也會增加,因此較高比例的終端使用者請求速度會變慢(效果稱為尾部延遲放大【24】)。
|
||||
>
|
||||
> 如果你想將響應時間百分點新增到你的服務的監視儀表板,則需要持續有效地計算它們。例如,你可能希望在最近10分鐘內保持請求響應時間的滾動視窗。每一分鐘,你都會計算出該視窗中的中值和各種百分數,並將這些度量值繪製在圖上。
|
||||
> 如果你想將響應時間百分點新增到你的服務的監視儀表板,則需要持續有效地計算它們。例如,你可能希望在最近10分鐘內保持請求響應時間的滾動視窗。每一分鐘,你都會計算出該視窗中的中值和各種百分數,並將這些度量值繪製在圖上。
|
||||
>
|
||||
> 簡單的實現是在時間視窗內儲存所有請求的響應時間列表,並且每分鐘對列表進行排序。如果對你來說效率太低,那麼有一些演算法能夠以最小的CPU和記憶體成本(如前向衰減【25】,t-digest【26】或HdrHistogram 【27】)來計算百分位數的近似值。請注意,平均百分比(例如,減少時間解析度或合併來自多臺機器的資料)在數學上沒有意義 - 聚合響應時間資料的正確方法是新增直方圖【28】。
|
||||
> 簡單的實現是在時間視窗內儲存所有請求的響應時間列表,並且每分鐘對列表進行排序。如果對你來說效率太低,那麼有一些演算法能夠以最小的CPU和記憶體成本(如前向衰減【25】,t-digest【26】或HdrHistogram 【27】)來計算百分位數的近似值。請注意,平均百分比(例如,減少時間解析度或合併來自多臺機器的資料)在數學上沒有意義 - 聚合響應時間資料的正確方法是新增直方圖【28】。
|
||||
|
||||
![](../img/fig1-5.png)
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
> 帶有太強個人色彩的系統無法成功。當最初的設計完成並且相對穩定時,不同的人們以自己的方式進行測試,真正的考驗才開始。
|
||||
>
|
||||
> ——高德納
|
||||
> —— 高德納
|
||||
|
||||
---------------
|
||||
|
||||
@ -65,11 +65,11 @@ AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36"
|
||||
|
||||
```bash
|
||||
cat /var/log/nginx/access.log | #1
|
||||
awk '{print $7}' | #2
|
||||
sort | #3
|
||||
uniq -c | #4
|
||||
sort -r -n | #5
|
||||
head -n 5 #6
|
||||
awk '{print $7}' | #2
|
||||
sort | #3
|
||||
uniq -c | #4
|
||||
sort -r -n | #5
|
||||
head -n 5 #6
|
||||
```
|
||||
|
||||
1. 讀取日誌檔案
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
> 有效的複雜系統總是從簡單的系統演化而來。 反之亦然:從零設計的複雜系統沒一個能有效工作的。
|
||||
>
|
||||
> ——約翰·加爾,Systemantics(1975)
|
||||
> —— 約翰·加爾,Systemantics(1975)
|
||||
|
||||
---------------
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
> 如果船長的終極目標是保護船隻,他應該永遠待在港口。
|
||||
>
|
||||
> ——聖托馬斯·阿奎那《神學大全》(1265-1274)
|
||||
> —— 聖托馬斯·阿奎那《神學大全》(1265-1274)
|
||||
|
||||
---------------
|
||||
|
||||
@ -117,11 +117,11 @@ Spark在批處理引擎上執行流處理,將流分解為**微批次(microba
|
||||
|
||||
> ### 鐵路上的模式遷移
|
||||
>
|
||||
> 大規模的“模式遷移”也發生在非計算機系統中。例如,在19世紀英國鐵路建設初期,軌距(兩軌之間的距離)就有了各種各樣的競爭標準。為一種軌距而建的列車不能在另一種軌距的軌道上執行,這限制了火車網路中可能的相互連線【9】。
|
||||
> 大規模的“模式遷移”也發生在非計算機系統中。例如,在19世紀英國鐵路建設初期,軌距(兩軌之間的距離)就有了各種各樣的競爭標準。為一種軌距而建的列車不能在另一種軌距的軌道上執行,這限制了火車網路中可能的相互連線【9】。
|
||||
>
|
||||
> 在1846年最終確定了一個標準軌距之後,其他軌距的軌道必須轉換 —— 但是如何在不停運火車線路的情況下進行數月甚至數年的遷移?解決的辦法是首先透過新增第三條軌道將軌道轉換為**雙軌距(dual guage)** 或**混合軌距**。這種轉換可以逐漸完成,當完成時,兩種軌距的列車都可以線上路上跑,使用三條軌道中的兩條。事實上,一旦所有的列車都轉換成標準軌距,那麼可以移除提供非標準軌距的軌道。
|
||||
> 在1846年最終確定了一個標準軌距之後,其他軌距的軌道必須轉換 —— 但是如何在不停運火車線路的情況下進行數月甚至數年的遷移?解決的辦法是首先透過新增第三條軌道將軌道轉換為**雙軌距(dual guage)** 或**混合軌距**。這種轉換可以逐漸完成,當完成時,兩種軌距的列車都可以線上路上跑,使用三條軌道中的兩條。事實上,一旦所有的列車都轉換成標準軌距,那麼可以移除提供非標準軌距的軌道。
|
||||
>
|
||||
> 以這種方式“再加工”現有的軌道,讓新舊版本並存,可以在幾年的時間內逐漸改變軌距。然而,這是一項昂貴的事業,這就是今天非標準軌距仍然存在的原因。例如,舊金山灣區的BART系統使用了與美國大部分地區不同的軌距。
|
||||
> 以這種方式“再加工”現有的軌道,讓新舊版本並存,可以在幾年的時間內逐漸改變軌距。然而,這是一項昂貴的事業,這就是今天非標準軌距仍然存在的原因。例如,舊金山灣區的BART系統使用了與美國大部分地區不同的軌距。
|
||||
|
||||
衍生檢視允許**漸進演化(gradual evolution)**。如果你想重新構建資料集,不需要執行突然切換式的遷移。取而代之的是,你可以將舊架構和新架構並排維護為相同基礎資料上的兩個獨立衍生檢視。然後可以開始將少量使用者轉移到新檢視,以測試其效能並發現任何錯誤,而大多數使用者仍然會被路由到舊檢視。你可以逐漸地增加訪問新檢視的使用者比例,最終可以刪除舊檢視【10】。
|
||||
|
||||
@ -490,7 +490,7 @@ COMMIT;
|
||||
|
||||
抑制重複事務的這種情況只是一個更普遍的原則的一個例子,這個原則被稱為**端到端原則(end-to-end argument)**,它在1984年由Saltzer,Reed和Clark闡述【55】:
|
||||
|
||||
> 只有在通訊系統兩端應用的知識與幫助下,所討論的功能才能完全地正確地實現。因而將這種被質疑的功能作為通訊系統本身的功能是不可能的。 (有時,通訊系統可以提供這種功能的不完備版本,可能有助於提高效能。)
|
||||
> 只有在通訊系統兩端應用的知識與幫助下,所討論的功能才能完全地正確地實現。因而將這種被質疑的功能作為通訊系統本身的功能是不可能的。 (有時,通訊系統可以提供這種功能的不完備版本,可能有助於提高效能。)
|
||||
>
|
||||
|
||||
在我們的例子中**所討論的功能**是重複抑制。我們看到TCP在TCP連線層次抑制了重複的資料包,一些流處理器在訊息處理層次提供了所謂的恰好一次語義,但這些都無法阻止當一個請求超時時,使用者親自提交重複的請求。TCP,資料庫事務,以及流處理器本身並不能完全排除這些重複。解決這個問題需要一個端到端的解決方案:從終端使用者的客戶端一路傳遞到資料庫的事務識別符號。
|
||||
@ -846,9 +846,9 @@ ACID意義下的一致性(請參閱“[一致性](ch7.md#一致性)”)基
|
||||
|
||||
就像工業革命有著黑暗面需要應對一樣,我們轉向資訊時代的過程中,也有需要應對與解決的重大問題。我相信資料的收集與使用就是其中一個問題。用Bruce Schneier的話來說【96】:
|
||||
|
||||
> 資料是資訊時代的汙染問題,保護隱私是環境挑戰。幾乎所有的電腦都能生產資訊。它堆積在周圍,開始潰爛。我們如何處理它 —— 我們如何控制它,以及如何擺脫它 —— 是資訊經濟健康發展的核心議題。正如我們今天回顧工業時代的早期年代,並想知道我們的祖先在忙於建設工業世界的過程時怎麼能忽略汙染問題;我們的孫輩在回望資訊時代的早期年代時,將會就我們如何應對資料收集和濫用的挑戰來評斷我們。
|
||||
> 資料是資訊時代的汙染問題,保護隱私是環境挑戰。幾乎所有的電腦都能生產資訊。它堆積在周圍,開始潰爛。我們如何處理它 —— 我們如何控制它,以及如何擺脫它 —— 是資訊經濟健康發展的核心議題。正如我們今天回顧工業時代的早期年代,並想知道我們的祖先在忙於建設工業世界的過程時怎麼能忽略汙染問題;我們的孫輩在回望資訊時代的早期年代時,將會就我們如何應對資料收集和濫用的挑戰來評斷我們。
|
||||
>
|
||||
> 我們應該設法讓他們感到驕傲。
|
||||
> 我們應該設法讓他們感到驕傲。
|
||||
|
||||
#### 立法與自律
|
||||
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
### NoSQL的誕生
|
||||
|
||||
現在 - 2010年代,NoSQL開始了最新一輪嘗試,試圖推翻關係模型的統治地位。“NoSQL”這個名字讓人遺憾,因為實際上它並沒有涉及到任何特定的技術。最初它只是作為一個醒目的Twitter標籤,用在2009年一個關於分散式,非關係資料庫上的開源聚會上。無論如何,這個術語觸動了某些神經,並迅速在網路創業社群內外傳播開來。好些有趣的資料庫系統現在都與*#NoSQL#*標籤相關聯,並且NoSQL被追溯性地重新解釋為**不僅是SQL(Not Only SQL)** 【4】。
|
||||
現在 - 2010年代,NoSQL開始了最新一輪嘗試,試圖推翻關係模型的統治地位。“NoSQL”這個名字讓人遺憾,因為實際上它並沒有涉及到任何特定的技術。最初它只是作為一個醒目的Twitter標籤,用在2009年一個關於分散式,非關係資料庫上的開源聚會上。無論如何,這個術語觸動了某些神經,並迅速在網路創業社群內外傳播開來。好些有趣的資料庫系統現在都與 *#NoSQL* 標籤相關聯,並且NoSQL被追溯性地重新解釋為**不僅是SQL(Not Only SQL)** 【4】。
|
||||
|
||||
採用NoSQL資料庫的背後有幾個驅動因素,其中包括:
|
||||
|
||||
@ -257,8 +257,8 @@ if (user && user.name && !user.first_name) {
|
||||
|
||||
```sql
|
||||
ALTER TABLE users ADD COLUMN first_name text;
|
||||
UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL
|
||||
UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
|
||||
UPDATE users SET first_name = split_part(name, ' ', 1); -- PostgreSQL
|
||||
UPDATE users SET first_name = substring_index(name, ' ', 1); -- MySQL
|
||||
```
|
||||
|
||||
模式變更的速度很慢,而且要求停運。它的這種壞名譽並不是完全應得的:大多數關係資料庫系統可在幾毫秒內執行`ALTER TABLE`語句。MySQL是一個值得注意的例外,它執行`ALTER TABLE`時會複製整個表,這可能意味著在更改一個大型表時會花費幾分鐘甚至幾個小時的停機時間,儘管存在各種工具來解決這個限制【24,25,26】。
|
||||
@ -412,7 +412,7 @@ for (var i = 0; i < liElements.length; i++) {
|
||||
|
||||
在Web瀏覽器中,使用宣告式CSS樣式比使用JavaScript命令式地操作樣式要好得多。類似地,在資料庫中,使用像SQL這樣的宣告式查詢語言比使用命令式查詢API要好得多[^vi]。
|
||||
|
||||
[^vi]: vi IMS和CODASYL都使用命令式API。應用程式通常使用COBOL程式碼遍歷資料庫中的記錄,一次一條記錄【2,16】。
|
||||
[^vi]: IMS和CODASYL都使用命令式API。應用程式通常使用COBOL程式碼遍歷資料庫中的記錄,一次一條記錄【2,16】。
|
||||
|
||||
### MapReduce查詢
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
> 建立秩序,省卻搜尋
|
||||
>
|
||||
> ——德國諺語
|
||||
> —— 德國諺語
|
||||
>
|
||||
|
||||
-------------------
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
> 唯變所適
|
||||
>
|
||||
> ——以弗所的赫拉克利特,為柏拉圖所引(公元前360年)
|
||||
> —— 以弗所的赫拉克利特,為柏拉圖所引(公元前360年)
|
||||
>
|
||||
|
||||
-------------------
|
||||
|
32
zh-tw/ch5.md
32
zh-tw/ch5.md
@ -4,7 +4,7 @@
|
||||
|
||||
> 與可能出錯的東西比,'不可能'出錯的東西最顯著的特點就是:一旦真的出錯,通常就徹底玩完了。
|
||||
>
|
||||
> ——道格拉斯·亞當斯(1992)
|
||||
> —— 道格拉斯·亞當斯(1992)
|
||||
|
||||
------
|
||||
|
||||
@ -249,21 +249,21 @@ PostgreSQL和Oracle等使用這種複製方法【16】。主要缺點是日誌
|
||||
|
||||
第三個複製延遲例子違反了因果律。 想象一下Poons先生和Cake夫人之間的以下簡短對話:
|
||||
|
||||
> *Mr. Poons*
|
||||
> Mrs. Cake,你能看到多遠的未來?
|
||||
>
|
||||
> *Mrs. Cake*
|
||||
> 通常約十秒鐘,Mr. Poons.
|
||||
*Mr. Poons*
|
||||
> Mrs. Cake,你能看到多遠的未來?
|
||||
|
||||
*Mrs. Cake*
|
||||
> 通常約十秒鐘,Mr. Poons.
|
||||
|
||||
這兩句話之間有因果關係:Cake夫人聽到了Poons先生的問題並回答了這個問題。
|
||||
|
||||
現在,想象第三個人正在透過從庫來聽這個對話。 Cake夫人說的內容是從一個延遲很低的從庫讀取的,但Poons先生所說的內容,從庫的延遲要大的多(見[圖5-5](../img/fig5-5.png))。 於是,這個觀察者會聽到以下內容:
|
||||
|
||||
> *Mrs. Cake*
|
||||
> 通常約十秒鐘,Mr. Poons.
|
||||
>
|
||||
> *Mr. Poons*
|
||||
> Mrs. Cake,你能看到多遠的未來?
|
||||
*Mrs. Cake*
|
||||
> 通常約十秒鐘,Mr. Poons.
|
||||
|
||||
*Mr. Poons*
|
||||
> Mrs. Cake,你能看到多遠的未來?
|
||||
|
||||
對於觀察者來說,看起來好像Cake夫人在Poons先生提問前就回答了這個問題。
|
||||
這種超能力讓人印象深刻,但也會把人搞糊塗。【25】。
|
||||
@ -410,7 +410,7 @@ PostgreSQL和Oracle等使用這種複製方法【16】。主要缺點是日誌
|
||||
|
||||
> #### 自動衝突解決
|
||||
>
|
||||
> 衝突解決規則可能很快變得複雜,並且自定義程式碼可能容易出錯。亞馬遜是一個經常被引用的例子,由於衝突解決處理程式令人意外的效果:一段時間以來,購物車上的衝突解決邏輯將保留新增到購物車的物品,但不包括從購物車中移除的物品。因此,顧客有時會看到物品重新出現在他們的購物車中,即使他們之前已經被移走【37】。
|
||||
> 衝突解決規則可能很快變得複雜,並且自定義程式碼可能容易出錯。亞馬遜是一個經常被引用的例子,由於衝突解決處理程式令人意外的效果:一段時間以來,購物車上的衝突解決邏輯將保留新增到購物車的物品,但不包括從購物車中移除的物品。因此,顧客有時會看到物品重新出現在他們的購物車中,即使他們之前已經被移走【37】。
|
||||
>
|
||||
> 已經有一些有趣的研究來自動解決由於資料修改引起的衝突。有幾行研究值得一提:
|
||||
>
|
||||
@ -642,11 +642,11 @@ LWW實現了最終收斂的目標,但以**永續性**為代價:如果同一
|
||||
|
||||
> #### 併發性,時間和相對性
|
||||
>
|
||||
> 如果兩個操作 **“同時”** 發生,似乎應該稱為併發——但事實上,它們在字面時間上重疊與否並不重要。由於分散式系統中的時鐘問題,現實中是很難判斷兩個事件是否**同時**發生的,這個問題我們將在[第八章](ch8.md)中詳細討論。
|
||||
> 如果兩個操作 **“同時”** 發生,似乎應該稱為併發——但事實上,它們在字面時間上重疊與否並不重要。由於分散式系統中的時鐘問題,現實中是很難判斷兩個事件是否**同時**發生的,這個問題我們將在[第八章](ch8.md)中詳細討論。
|
||||
>
|
||||
> 為了定義併發性,確切的時間並不重要:如果兩個操作都意識不到對方的存在,就稱這兩個操作**併發**,而不管它們發生的物理時間。人們有時把這個原理和狹義相對論的物理學聯絡起來【54】,它引入了資訊不能比光速更快的思想。因此,如果兩個事件發生的時間差小於光透過它們之間的距離所需要的時間,那麼這兩個事件不可能相互影響。
|
||||
> 為了定義併發性,確切的時間並不重要:如果兩個操作都意識不到對方的存在,就稱這兩個操作**併發**,而不管它們發生的物理時間。人們有時把這個原理和狹義相對論的物理學聯絡起來【54】,它引入了資訊不能比光速更快的思想。因此,如果兩個事件發生的時間差小於光透過它們之間的距離所需要的時間,那麼這兩個事件不可能相互影響。
|
||||
>
|
||||
> 在計算機系統中,即使光速原則上允許一個操作影響另一個操作,但兩個操作也可能是**並行的**。例如,如果網路緩慢或中斷,兩個操作間可能會出現一段時間間隔,但仍然是併發的,因為網路問題阻止一個操作意識到另一個操作的存在。
|
||||
> 在計算機系統中,即使光速原則上允許一個操作影響另一個操作,但兩個操作也可能是**並行的**。例如,如果網路緩慢或中斷,兩個操作間可能會出現一段時間間隔,但仍然是併發的,因為網路問題阻止一個操作意識到另一個操作的存在。
|
||||
|
||||
|
||||
#### 捕獲"此前發生"關係
|
||||
@ -706,7 +706,7 @@ LWW實現了最終收斂的目標,但以**永續性**為代價:如果同一
|
||||
|
||||
> #### 版本向量和向量時鐘
|
||||
>
|
||||
> 版本向量有時也被稱為向量時鐘,即使它們不完全相同。 差別很微妙——細節請參閱參考資料【57,60,61】。 簡而言之,在比較副本的狀態時,版本向量是正確的資料結構。
|
||||
> 版本向量有時也被稱為向量時鐘,即使它們不完全相同。 差別很微妙——細節請參閱參考資料【57,60,61】。 簡而言之,在比較副本的狀態時,版本向量是正確的資料結構。
|
||||
>
|
||||
|
||||
## 本章小結
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
> #### 術語澄清
|
||||
>
|
||||
> 上文中的**分割槽(partition)**,在MongoDB,Elasticsearch和Solr Cloud中被稱為**分片(shard)**,在HBase中稱之為**區域(Region)**,Bigtable中則是 **表塊(tablet)**,Cassandra和Riak中是**虛節點(vnode)**,Couchbase中叫做**虛桶(vBucket)**。但是**分割槽(partitioning)** 是最約定俗成的叫法。
|
||||
> 上文中的**分割槽(partition)**,在MongoDB,Elasticsearch和Solr Cloud中被稱為**分片(shard)**,在HBase中稱之為**區域(Region)**,Bigtable中則是 **表塊(tablet)**,Cassandra和Riak中是**虛節點(vnode)**,Couchbase中叫做**虛桶(vBucket)**。但是**分割槽(partitioning)** 是最約定俗成的叫法。
|
||||
>
|
||||
|
||||
通常情況下,每條資料(每條記錄,每行或每個文件)屬於且僅屬於一個分割槽。有很多方法可以實現這一點,本章將進行深入討論。實際上,每個分割槽都是自己的小型資料庫,儘管資料庫可能支援同時進行多個分割槽的操作。
|
||||
@ -89,9 +89,9 @@
|
||||
|
||||
> #### 一致性雜湊
|
||||
>
|
||||
> 一致性雜湊由Karger等人定義。【7】 用於跨網際網路級別的快取系統,例如CDN中,是一種能均勻分配負載的方法。它使用隨機選擇的 **分割槽邊界(partition boundaries)** 來避免中央控制或分散式共識的需要。 請注意,這裡的一致性與複製一致性(請參閱[第五章](ch5.md))或ACID一致性(請參閱[第七章](ch7.md))無關,而只是描述了一種重新平衡(reblancing)的特定方法。
|
||||
> 一致性雜湊由Karger等人定義。【7】 用於跨網際網路級別的快取系統,例如CDN中,是一種能均勻分配負載的方法。它使用隨機選擇的 **分割槽邊界(partition boundaries)** 來避免中央控制或分散式共識的需要。 請注意,這裡的一致性與複製一致性(請參閱[第五章](ch5.md))或ACID一致性(請參閱[第七章](ch7.md))無關,而只是描述了一種重新平衡(reblancing)的特定方法。
|
||||
>
|
||||
> 正如我們將在“[分割槽再平衡](#分割槽再平衡)”中所看到的,這種特殊的方法對於資料庫實際上並不是很好,所以在實際中很少使用(某些資料庫的文件仍然會使用一致性雜湊的說法,但是它往往是不準確的)。 因為有可能產生混淆,所以最好避免使用一致性雜湊這個術語,而只是把它稱為**雜湊分割槽(hash partitioning)**。
|
||||
> 正如我們將在“[分割槽再平衡](#分割槽再平衡)”中所看到的,這種特殊的方法對於資料庫實際上並不是很好,所以在實際中很少使用(某些資料庫的文件仍然會使用一致性雜湊的說法,但是它往往是不準確的)。 因為有可能產生混淆,所以最好避免使用一致性雜湊這個術語,而只是把它稱為**雜湊分割槽(hash partitioning)**。
|
||||
|
||||
不幸的是,透過使用鍵雜湊進行分割槽,我們失去了鍵範圍分割槽的一個很好的屬性:高效執行範圍查詢的能力。曾經相鄰的鍵現在分散在所有分割槽中,所以它們之間的順序就丟失了。在MongoDB中,如果你使用了基於雜湊的分割槽模式,則任何範圍查詢都必須傳送到所有分割槽【4】。Riak 【9】,Couchbase 【10】或Voldemort不支援主鍵上的範圍查詢。
|
||||
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
![](../img/ch7.png)
|
||||
|
||||
> 一些作者聲稱,支援通用的兩階段提交代價太大,會帶來效能與可用性的問題。讓程式設計師來處理過度使用事務導致的效能問題,總比缺少事務程式設計好得多。
|
||||
> 一些作者聲稱,支援通用的兩階段提交代價太大,會帶來效能與可用性的問題。讓程式設計師來處理過度使用事務導致的效能問題,總比缺少事務程式設計好得多。
|
||||
>
|
||||
> ——James Corbett等人,Spanner:Google的全球分散式資料庫(2012)
|
||||
> —— James Corbett等人,Spanner:Google的全球分散式資料庫(2012)
|
||||
|
||||
------
|
||||
|
||||
|
48
zh-tw/ch8.md
48
zh-tw/ch8.md
@ -10,7 +10,7 @@
|
||||
>
|
||||
> 無食我數
|
||||
>
|
||||
> —— Kyle Kingsbury, Carly Rae Jepsen 《網路分割槽的危害》(2013年)[^譯著1]
|
||||
> —— Kyle Kingsbury, Carly Rae Jepsen 《網路分割槽的危害》(2013年)[^譯著1]
|
||||
|
||||
---------
|
||||
|
||||
@ -39,9 +39,9 @@
|
||||
|
||||
當你編寫執行在多臺計算機上的軟體時,情況有本質上的區別。在分散式系統中,我們不再處於理想化的系統模型中,我們別無選擇,只能面對現實世界的混亂現實。而在現實世界中,各種各樣的事情都可能會出現問題【4】,如下面的軼事所述:
|
||||
|
||||
> 在我有限的經驗中,我已經和很多東西打過交道:單個**資料中心(DC)** 中長期存在的網路分割槽,配電單元PDU故障,交換機故障,整個機架的意外重啟,整個資料中心主幹網路故障,整個資料中心的電源故障,以及一個低血糖的司機把他的福特皮卡撞在資料中心的HVAC(加熱,通風和空調)系統上。而且我甚至不是一個運維。
|
||||
> 在我有限的經驗中,我已經和很多東西打過交道:單個**資料中心(DC)** 中長期存在的網路分割槽,配電單元PDU故障,交換機故障,整個機架的意外重啟,整個資料中心主幹網路故障,整個資料中心的電源故障,以及一個低血糖的司機把他的福特皮卡撞在資料中心的HVAC(加熱,通風和空調)系統上。而且我甚至不是一個運維。
|
||||
>
|
||||
> ——柯達黑爾
|
||||
> —— 柯達黑爾
|
||||
|
||||
在分散式系統中,儘管系統的其他部分工作正常,但系統的某些部分可能會以某種不可預知的方式被破壞。這被稱為**部分失效(partial failure)**。難點在於部分失效是**不確定性的(nonderterministic)**:如果你試圖做任何涉及多個節點和網路的事情,它有時可能會工作,有時會出現不可預知的失敗。正如我們將要看到的,你甚至不知道是否成功了,因為訊息透過網路傳播的時間也是不確定的!
|
||||
|
||||
@ -122,7 +122,7 @@
|
||||
|
||||
> #### 網路分割槽
|
||||
>
|
||||
> 當網路的一部分由於網路故障而被切斷時,有時稱為**網路分割槽(network partition)** 或**網路斷裂(netsplit)**。在本書中,我們通常會堅持使用更一般的術語**網路故障(network fault)**,以避免與[第六章](ch6.md)討論的儲存系統的分割槽(分片)相混淆。
|
||||
> 當網路的一部分由於網路故障而被切斷時,有時稱為**網路分割槽(network partition)** 或**網路斷裂(netsplit)**。在本書中,我們通常會堅持使用更一般的術語**網路故障(network fault)**,以避免與[第六章](ch6.md)討論的儲存系統的分割槽(分片)相混淆。
|
||||
|
||||
即使網路故障在你的環境中非常罕見,故障可能發生的事實,意味著你的軟體需要能夠處理它們。無論何時透過網路進行通訊,都可能會失敗,這是無法避免的。
|
||||
|
||||
@ -180,9 +180,9 @@
|
||||
|
||||
> #### TCP與UDP
|
||||
>
|
||||
> 一些對延遲敏感的應用程式,比如影片會議和IP語音(VoIP),使用了UDP而不是TCP。這是在可靠性和和延遲變化之間的折衷:由於UDP不執行流量控制並且不重傳丟失的分組,所以避免了網路延遲變化的一些原因(儘管它仍然易受切換佇列和排程延遲的影響)。
|
||||
> 一些對延遲敏感的應用程式,比如影片會議和IP語音(VoIP),使用了UDP而不是TCP。這是在可靠性和和延遲變化之間的折衷:由於UDP不執行流量控制並且不重傳丟失的分組,所以避免了網路延遲變化的一些原因(儘管它仍然易受切換佇列和排程延遲的影響)。
|
||||
>
|
||||
> 在延遲資料毫無價值的情況下,UDP是一個不錯的選擇。例如,在VoIP電話呼叫中,可能沒有足夠的時間重新發送丟失的資料包,並在揚聲器上播放資料。在這種情況下,重發資料包沒有意義——應用程式必須使用靜音填充丟失資料包的時隙(導致聲音短暫中斷),然後在資料流中繼續。重試發生在人類層。 (“你能再說一遍嗎?聲音剛剛斷了一會兒。“)
|
||||
> 在延遲資料毫無價值的情況下,UDP是一個不錯的選擇。例如,在VoIP電話呼叫中,可能沒有足夠的時間重新發送丟失的資料包,並在揚聲器上播放資料。在這種情況下,重發資料包沒有意義——應用程式必須使用靜音填充丟失資料包的時隙(導致聲音短暫中斷),然後在資料流中繼續。重試發生在人類層。 (“你能再說一遍嗎?聲音剛剛斷了一會兒。“)
|
||||
|
||||
所有這些因素都會造成網路延遲的變化。當系統接近其最大容量時,排隊延遲的變化範圍特別大:擁有足夠備用容量的系統可以輕鬆排空佇列,而在高利用率的系統中,很快就能積累很長的佇列。
|
||||
|
||||
@ -224,17 +224,17 @@
|
||||
|
||||
> ### 延遲和資源利用
|
||||
>
|
||||
> 更一般地說,可以將**延遲變化**視為**動態資源分割槽**的結果。
|
||||
> 更一般地說,可以將**延遲變化**視為**動態資源分割槽**的結果。
|
||||
>
|
||||
> 假設兩臺電話交換機之間有一條線路,可以同時進行10,000個呼叫。透過此線路切換的每個電路都佔用其中一個呼叫插槽。因此,你可以將線路視為可由多達10,000個併發使用者共享的資源。資源以靜態方式分配:即使你現在是電話上唯一的電話,並且所有其他9,999個插槽都未使用,你的電路仍將分配與導線充分利用時相同的固定數量的頻寬。
|
||||
> 假設兩臺電話交換機之間有一條線路,可以同時進行10,000個呼叫。透過此線路切換的每個電路都佔用其中一個呼叫插槽。因此,你可以將線路視為可由多達10,000個併發使用者共享的資源。資源以靜態方式分配:即使你現在是電話上唯一的電話,並且所有其他9,999個插槽都未使用,你的電路仍將分配與導線充分利用時相同的固定數量的頻寬。
|
||||
>
|
||||
> 相比之下,網際網路動態分享網路頻寬。傳送者互相推擠和爭奪,以讓他們的資料包儘可能快地透過網路,並且網路交換機決定從一個時刻到另一個時刻傳送哪個分組(即,頻寬分配)。這種方法有排隊的缺點,但其優點是它最大限度地利用了電線。電線固定成本,所以如果你更好地利用它,你透過電線傳送的每個位元組都會更便宜。
|
||||
> 相比之下,網際網路動態分享網路頻寬。傳送者互相推擠和爭奪,以讓他們的資料包儘可能快地透過網路,並且網路交換機決定從一個時刻到另一個時刻傳送哪個分組(即,頻寬分配)。這種方法有排隊的缺點,但其優點是它最大限度地利用了電線。電線固定成本,所以如果你更好地利用它,你透過電線傳送的每個位元組都會更便宜。
|
||||
>
|
||||
> CPU也會出現類似的情況:如果你在多個執行緒間動態共享每個CPU核心,則一個執行緒有時必須在作業系統的執行佇列裡等待,而另一個執行緒正在執行,這樣每個執行緒都有可能被暫停一個不定的時間長度。但是,與為每個執行緒分配靜態數量的CPU週期相比,這會更好地利用硬體(請參閱“[響應時間保證](#響應時間保證)”)。更好的硬體利用率也是使用虛擬機器的重要動機。
|
||||
> CPU也會出現類似的情況:如果你在多個執行緒間動態共享每個CPU核心,則一個執行緒有時必須在作業系統的執行佇列裡等待,而另一個執行緒正在執行,這樣每個執行緒都有可能被暫停一個不定的時間長度。但是,與為每個執行緒分配靜態數量的CPU週期相比,這會更好地利用硬體(請參閱“[響應時間保證](#響應時間保證)”)。更好的硬體利用率也是使用虛擬機器的重要動機。
|
||||
>
|
||||
> 如果資源是靜態分割槽的(例如,專用硬體和專用頻寬分配),則在某些環境中可以實現**延遲保證**。但是,這是以降低利用率為代價的——換句話說,它是更昂貴的。另一方面,動態資源分配的多租戶提供了更好的利用率,所以它更便宜,但它具有可變延遲的缺點。
|
||||
> 如果資源是靜態分割槽的(例如,專用硬體和專用頻寬分配),則在某些環境中可以實現**延遲保證**。但是,這是以降低利用率為代價的——換句話說,它是更昂貴的。另一方面,動態資源分配的多租戶提供了更好的利用率,所以它更便宜,但它具有可變延遲的缺點。
|
||||
>
|
||||
> 網路中的可變延遲不是一種自然規律,而只是成本/收益權衡的結果。
|
||||
> 網路中的可變延遲不是一種自然規律,而只是成本/收益權衡的結果。
|
||||
|
||||
|
||||
## 不可靠的時鐘
|
||||
@ -377,19 +377,19 @@ Spanner以這種方式實現跨資料中心的快照隔離【59,60】。它使
|
||||
|
||||
```java
|
||||
while (true) {
|
||||
request = getIncomingRequest();
|
||||
// 確保租約還剩下至少10秒
|
||||
if (lease.expiryTimeMillis - System.currentTimeMillis() < 10000){
|
||||
lease = lease.renew();
|
||||
}
|
||||
request = getIncomingRequest();
|
||||
// 確保租約還剩下至少10秒
|
||||
if (lease.expiryTimeMillis - System.currentTimeMillis() < 10000){
|
||||
lease = lease.renew();
|
||||
}
|
||||
|
||||
if (lease.isValid()) {
|
||||
process(request);
|
||||
}
|
||||
if (lease.isValid()) {
|
||||
process(request);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
這個程式碼有什麼問題?首先,它依賴於同步時鐘:租約到期時間由另一臺機器設定(例如,當前時間加上30秒,計算到期時間),並將其與本地系統時鐘進行比較。如果時鐘不同步超過幾秒,這段程式碼將開始做奇怪的事情。
|
||||
這個程式碼有什麼問題?首先,它依賴於同步時鐘:租約到期時間由另一臺機器設定(例如,當前時間加上30秒,計算到期時間),並將其與本地系統時鐘進行比較。如果時鐘不同步超過幾秒,這段程式碼將開始做奇怪的事情。
|
||||
|
||||
其次,即使我們將協議更改為僅使用本地單調時鐘,也存在另一個問題:程式碼假定在執行剩餘時間檢查`System.currentTimeMillis()`和實際執行請求`process(request)`中間的時間間隔非常短。通常情況下,這段程式碼執行得非常快,所以10秒的緩衝區已經足夠確保**租約**在請求處理到一半時不會過期。
|
||||
|
||||
@ -514,11 +514,11 @@ while (true) {
|
||||
|
||||
> ### 拜占庭將軍問題
|
||||
>
|
||||
> 拜占庭將軍問題是對所謂“兩將軍問題”的泛化【78】,它想象兩個將軍需要就戰鬥計劃達成一致的情況。由於他們在兩個不同的地點建立了營地,他們只能透過信使進行溝通,信使有時會被延遲或丟失(就像網路中的資訊包一樣)。我們將在[第九章](ch9.md)討論這個共識問題。
|
||||
> 拜占庭將軍問題是對所謂“兩將軍問題”的泛化【78】,它想象兩個將軍需要就戰鬥計劃達成一致的情況。由於他們在兩個不同的地點建立了營地,他們只能透過信使進行溝通,信使有時會被延遲或丟失(就像網路中的資訊包一樣)。我們將在[第九章](ch9.md)討論這個共識問題。
|
||||
>
|
||||
> 在這個問題的拜占庭版本里,有n位將軍需要同意,他們的努力因為有一些叛徒在他們中間而受到阻礙。大多數的將軍都是忠誠的,因而發出了真實的資訊,但是叛徒可能會試圖透過傳送虛假或不真實的資訊來欺騙和混淆他人(在試圖保持未被發現的同時)。事先並不知道叛徒是誰。
|
||||
> 在這個問題的拜占庭版本里,有n位將軍需要同意,他們的努力因為有一些叛徒在他們中間而受到阻礙。大多數的將軍都是忠誠的,因而發出了真實的資訊,但是叛徒可能會試圖透過傳送虛假或不真實的資訊來欺騙和混淆他人(在試圖保持未被發現的同時)。事先並不知道叛徒是誰。
|
||||
>
|
||||
> 拜占庭是後來成為君士坦丁堡的古希臘城市,現在在土耳其的伊斯坦布林。沒有任何歷史證據表明拜占庭將軍比其他地方更容易出現陰謀和陰謀。相反,這個名字來源於拜占庭式的過度複雜,官僚,迂迴等意義,早在計算機之前就已經在政治中被使用了【79】。Lamport想要選一個不會冒犯任何讀者的國家,他被告知將其稱為阿爾巴尼亞將軍問題並不是一個好主意【80】。
|
||||
> 拜占庭是後來成為君士坦丁堡的古希臘城市,現在在土耳其的伊斯坦布林。沒有任何歷史證據表明拜占庭將軍比其他地方更容易出現陰謀和陰謀。相反,這個名字來源於拜占庭式的過度複雜,官僚,迂迴等意義,早在計算機之前就已經在政治中被使用了【79】。Lamport想要選一個不會冒犯任何讀者的國家,他被告知將其稱為阿爾巴尼亞將軍問題並不是一個好主意【80】。
|
||||
|
||||
當一個系統在部分節點發生故障、不遵守協議、甚至惡意攻擊、擾亂網路時仍然能繼續正確工作,稱之為**拜占庭容錯(Byzantine fault-tolerant)** 的,在特定場景下,這種擔憂在是有意義的:
|
||||
|
||||
|
@ -81,7 +81,7 @@
|
||||
|
||||
在這個例子中,暫存器有兩種型別的操作:
|
||||
|
||||
* $ read(x)⇒v$表示客戶端請求讀取暫存器 `x` 的值,資料庫返回值 `v`。
|
||||
* $read(x)⇒v$表示客戶端請求讀取暫存器 `x` 的值,資料庫返回值 `v`。
|
||||
* $write(x,v)⇒r$ 表示客戶端請求將暫存器 `x` 設定為值 `v` ,資料庫返回響應 `r` (可能正確,可能錯誤)。
|
||||
|
||||
在[圖9-2](../img/fig9-2.png) 中,`x` 的值最初為 `0`,客戶端C 執行寫請求將其設定為 `1`。發生這種情況時,客戶端A和B反覆輪詢資料庫以讀取最新值。 A和B的請求可能會收到怎樣的響應?
|
||||
@ -638,7 +638,7 @@ CAP定理的正式定義僅限於很狹隘的範圍【30】,它只考慮了一
|
||||
情況如[圖9-10](../img/fig9-10.png) 所示。在這個特定的例子中,協調者實際上決定提交,資料庫2 收到提交請求。但是,協調者在將提交請求傳送到資料庫1 之前發生崩潰,因此資料庫1 不知道是否提交或中止。即使**超時**在這裡也沒有幫助:如果資料庫1 在超時後單方面中止,它將最終與執行提交的資料庫2 不一致。同樣,單方面提交也是不安全的,因為另一個參與者可能已經中止了。
|
||||
|
||||
![](../img/fig9-10.png)
|
||||
**圖9-10 參與者投贊成票後,協調者崩潰。資料庫1不知道是否提交或中止**
|
||||
**圖9-10 參與者投贊成票後,協調者崩潰。資料庫1不知道是否提交或中止**
|
||||
|
||||
沒有協調者的訊息,參與者無法知道是提交還是放棄。原則上參與者可以相互溝通,找出每個參與者是如何投票的,並達成一致,但這不是2PC協議的一部分。
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
|
||||
### 無共享架構
|
||||
|
||||
相比之下,**無共享架構**(shared-nothing architecture,有時被稱為**水平伸縮**,即horizontal scaling,或**向外伸縮**,即scaling out)已經相當普及。在這種架構中,執行資料庫軟體的每臺機器/虛擬機器都稱為**節點(node)**。每個節點只使用各自的處理器,記憶體和磁碟。節點之間的任何協調,都是在軟體層面使用傳統網路實現的。
|
||||
相比之下,**無共享架構**【3】(shared-nothing architecture,有時被稱為**水平伸縮**,即horizontal scaling,或**向外伸縮**,即scaling out)已經相當普及。在這種架構中,執行資料庫軟體的每臺機器/虛擬機器都稱為**節點(node)**。每個節點只使用各自的處理器,記憶體和磁碟。節點之間的任何協調,都是在軟體層面使用傳統網路實現的。
|
||||
|
||||
無共享系統不需要使用特殊的硬體,所以你可以用任意機器——比如價效比最好的機器。你也許可以跨多個地理區域分佈資料從而減少使用者延遲,或者在損失一整個資料中心的情況下倖免於難。隨著雲端虛擬機器部署的出現,即使是小公司,現在無需Google級別的運維,也可以實現異地分散式架構。
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user