mirror of
https://github.com/Vonng/ddia.git
synced 2026-06-21 00:47:05 +08:00
Merge pull request #386 from uncle-lv/improve-ch2-translation
improve ch2 translation
This commit is contained in:
commit
330f3f3b27
1 changed files with 42 additions and 42 deletions
|
|
@ -68,17 +68,17 @@ SELECT posts.*, users.* FROM posts
|
|||
|
||||
如果由于某些特殊事件导致帖子速率激增,我们不必立即进行时间线交付——我们可以将它们排队,并接受帖子在粉丝的时间线中显示会暂时花费更长时间。即使在这种负载峰值期间,时间线仍然可以快速加载,因为我们只是从缓存中提供它们。
|
||||
|
||||
这种预先计算和更新查询结果的过程称为 *物化*,时间线缓存是 *物化视图* 的一个例子(我们将在 [待补充链接] 中进一步讨论这个概念)。物化视图加速了读取,但作为回报,我们必须在写入时做更多的工作。对于大多数用户来说,写入成本是适度的,但社交网络还必须考虑一些极端情况:
|
||||
这种预先计算和更新查询结果的过程称为 *物化*,时间线缓存是 *物化视图* 的一个例子(我们将在 [待补充链接] 中进一步讨论这个概念)。物化视图加速了读取,但作为交换,我们必须在写入时做更多的工作。对于大多数用户来说,写入成本是适中的,但社交网络还必须考虑一些极端情况:
|
||||
|
||||
* 如果用户关注非常多的账户,并且这些账户发布很多内容,该用户的物化时间线将有很高的写入率。然而,在这种情况下,用户实际上不太可能阅读其时间线中的所有帖子,因此可以简单地丢弃其时间线的一些写入,只向用户显示他们关注的账户的帖子样本 [^5]。
|
||||
* 当拥有大量粉丝的名人账户发布帖子时,我们必须做大量工作将该帖子插入到他们数百万粉丝的每个首页时间线中。在这种情况下,丢弃一些写入是不可接受的。解决这个问题的一种方法是将名人帖子与其他人的帖子分开处理:我们可以通过将名人帖子单独存储并在读取时与物化时间线合并,来节省将它们添加到数百万时间线的工作。尽管有这些优化,在社交网络上处理名人仍然需要大量基础设施 [^6]。
|
||||
* 当拥有大量粉丝的名人账户发布帖子时,我们必须做大量工作将该帖子插入到他们数百万粉丝的每个首页时间线中。在这种情况下,丢弃一些写入是不可接受的。解决这个问题的一种方法是将名人帖子与其他人的帖子分开处理:我们可以通过将名人帖子单独存储并在读取时与物化时间线合并,来节省将它们添加到数百万时间线的工作。尽管有这些优化,处理社交网络上的名人仍然需要大量基础设施 [^6]。
|
||||
|
||||
## 描述性能 {#sec_introduction_percentiles}
|
||||
|
||||
大多数关于软件性能的讨论都考虑两种主要的度量类型:
|
||||
|
||||
响应时间
|
||||
: 从用户发出请求到收到所请求答案的经过时间。测量单位是秒(或毫秒,或微秒)。
|
||||
: 从用户发出请求到收到请求应答所经过的时间。测量单位是秒(或毫秒,或微秒)。
|
||||
|
||||
吞吐量
|
||||
: 系统正在处理的每秒请求数,或每秒数据量。对于给定的硬件资源分配,存在可以处理的 *最大吞吐量*。测量单位是"每秒某物"。
|
||||
|
|
@ -156,7 +156,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
|
||||
{{< figure src="/fig/ddia_0206.png" id="fig_tail_amplification" caption="图 2-6. 当需要几个后端调用来服务请求时,只需要一个慢的后端请求就可以减慢整个最终用户请求。" class="w-full my-4" >}}
|
||||
|
||||
百分位数通常用于 *服务级别目标*(SLO)和 *服务级别协议*(SLA),作为定义服务预期性能和可用性的方式 [^27]。例如,SLO 可能设定服务的中位响应时间小于 200 毫秒且第 99 百分位低于 1 秒的目标,以及至少 99.9% 的有效请求导致非错误响应的目标。SLA 是一份合同,规定如果不满足 SLO 会发生什么(例如,客户可能有权获得退款)。这至少是基本想法;实际上,为 SLO 和 SLA 定义良好的可用性指标并不简单 [^28] [^29]。
|
||||
百分位数通常用于 *服务级别目标*(SLO)和 *服务级别协议*(SLA),作为定义服务预期性能和可用性的方式 [^27]。例如,SLO 可能设定服务的中位响应时间小于 200 毫秒且第 99 百分位低于 1 秒的目标,以及至少 99.9% 的有效请求产生非错误响应的目标。SLA 是一份合同,规定如果不满足 SLO 会发生什么(例如,客户可能有权获得退款)。这至少是基本想法;实际上,为 SLO 和 SLA 定义良好的可用性指标并不简单 [^28] [^29]。
|
||||
|
||||
--------
|
||||
|
||||
|
|
@ -197,28 +197,28 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
|
||||
容错总是限于某些类型的某些数量的故障。例如,系统可能能够容忍最多两个硬盘驱动器同时故障,或最多三个节点中的一个崩溃。如果所有节点都崩溃,没有什么可以做的,这没有意义容忍任何数量的故障。如果整个地球(及其上的所有服务器)被黑洞吞噬,容忍该故障将需要在太空中进行网络托管——祝你获得批准该预算项目的好运。
|
||||
|
||||
反直觉地,在这种容错系统中,通过故意触发故障来 *增加* 故障率是有意义的——例如,在没有警告的情况下随机杀死单个进程。这称为 *故障注入*。许多关键错误实际上是由于错误处理不当造成的 [^38];通过故意引发故障,你确保容错机制不断得到锻炼和测试,这可以增加你对故障自然发生时将被正确处理的信心。*混沌工程* 是一门旨在通过故意注入故障等实验来提高对容错机制的信心的学科 [^39]。
|
||||
反直觉地是,在这种容错系统中,通过故意触发故障来 *增加* 故障率是有意义的——例如,在没有警告的情况下随机杀死单个进程。这称为 *故障注入*。许多关键错误实际上是由于错误处理不当造成的 [^38];通过故意引发故障,你确保容错机制不断得到锻炼和测试,这可以增加你对故障自然发生时将被正确处理的信心。*混沌工程* 是一门旨在通过故意注入故障等实验来提高对容错机制的信心的学科 [^39]。
|
||||
|
||||
尽管我们通常更喜欢容忍故障而不是预防故障,但在预防比治疗更好的情况下(例如,因为不存在治疗方法)。安全问题就是这种情况:如果攻击者已经破坏了系统并获得了对敏感数据的访问,该事件无法撤消。然而,本书主要涉及可以治愈的故障类型,如以下部分所述。
|
||||
尽管我们通常更喜欢容忍故障而不是预防故障,但在预防比治疗更好的情况下(例如,因为不存在治疗方法)。安全问题就是这种情况:如果攻击者已经破坏了系统并获得了对敏感数据的访问,该事件无法撤消。然而,本书主要涉及可以恢复的故障类型,如以下部分所述。
|
||||
|
||||
### 硬件与软件故障 {#sec_introduction_hardware_faults}
|
||||
|
||||
当我们想到系统失效的原因时,硬件故障很快就会浮现在脑海中:
|
||||
|
||||
* 大约 2-5% 的磁性硬盘驱动器每年发生故障 [^40] [^41];在拥有 10,000 个磁盘的存储集群中,我们因此应该期望平均每天有一个磁盘故障。最近的数据表明磁盘变得更可靠,但故障率仍然很显著 [^42]。
|
||||
* 大约 0.5-1% 的固态硬盘(SSD)每年发生故障 [^43]。少量位错误会自动纠正 [^44],但不可纠正的错误大约每年每个驱动器发生一次,即使在相当新的驱动器中(即,经历很少磨损);这个错误率高于磁性硬盘驱动器 [^45]、[^46]。
|
||||
* 大约 2-5% 的硬磁盘驱动器每年发生故障 [^40] [^41];在拥有 10,000 个磁盘的存储集群中,我们因此应该认为平均每天有一个磁盘故障。最近的数据表明磁盘变得更可靠,但故障率仍然很显著 [^42]。
|
||||
* 大约 0.5-1% 的固态硬盘(SSD)每年发生故障 [^43]。少量位错误会自动纠正 [^44],但不可纠正的错误大约每年每个驱动器发生一次,即使在相当新的驱动器中(即,经历很少磨损);这个错误率高于硬磁盘驱动器 [^45]、[^46]。
|
||||
* 其他硬件组件,如电源、RAID 控制器和内存模块也会发生故障,尽管频率低于硬盘驱动器 [^47] [^48]。
|
||||
* 大约千分之一的机器有一个 CPU 核心偶尔计算错误的结果,可能是由于制造缺陷 [^49] [^50] [^51]。在某些情况下,错误的计算会导致崩溃,但在其他情况下,它会导致程序简单地返回错误的结果。
|
||||
* RAM 中的数据也可能被损坏,要么是由于宇宙射线等随机事件,要么是由于永久性物理缺陷。即使使用纠错码(ECC)的内存,超过 1% 的机器在给定年份遇到不可纠正的错误,这通常会导致机器崩溃和受影响的内存模块需要更换 [^52]。此外,某些病理内存访问模式可以以高概率翻转位 [^53]。
|
||||
* 整个数据中心可能变得不可用(例如,由于停电或网络配置错误)甚至被永久摧毁(例如,由火灾、洪水或地震 [^54])。太阳风暴,当太阳喷射大量带电粒子时,会在长距离电线中感应出大电流,可能会损坏电网和海底网络电缆 [^55]。尽管这种大规模故障很少见,但如果服务不能容忍数据中心的丢失,它们的影响可能是灾难性的 [^56]。
|
||||
* 大约千分之一的机器有一个 CPU 核心偶尔计算错误的结果,可能是制造缺陷 [^49] [^50] [^51]造成的。在某些情况下,错误的计算会导致崩溃,但在其他情况下,它会导致程序简单地返回错误的结果。
|
||||
* RAM 中的数据也可能被损坏,要么是由于宇宙射线等随机事件,要么是由于永久性物理缺陷。即使使用纠错码(ECC)的内存,也有超过 1% 的机器在给定年限内遇到不可纠正的错误,这通常会导致机器崩溃和受影响的内存模块需要更换 [^52]。此外,某些病态的内存访问模式可能会以很高的概率翻转比特位 [^53]。
|
||||
* 整个数据中心可能变得不可用(例如,由于停电或网络配置错误)甚至被永久摧毁(例如,由于火灾、洪水或地震 [^54])。太阳风暴,当太阳喷射大量带电粒子时,会在长距离电线中感应出大电流,可能会损坏电网和海底网络电缆 [^55]。尽管这种大规模故障很少见,但如果服务不能容忍数据中心的丢失,它们的影响可能是灾难性的 [^56]。
|
||||
|
||||
这些事件足够罕见,你在处理小型系统时通常不需要担心它们,只要你可以轻松更换变得有故障的硬件。然而,在大规模系统中,硬件故障发生得足够频繁,以至于它们成为正常系统运行的一部分。
|
||||
这些事件足够罕见,你在处理小型系统时通常不需要担心它们,只要你能够轻松地更换出现故障的硬件。然而,在大规模系统中,硬件故障发生得足够频繁,以至于它们成为正常系统运行的一部分。
|
||||
|
||||
#### 通过冗余容忍硬件故障 {#tolerating-hardware-faults-through-redundancy}
|
||||
|
||||
我们对不可靠硬件的第一反应通常是向各个硬件组件添加冗余,以降低系统的故障率。磁盘可以设置为 RAID 配置(将数据分布在同一台机器的多个磁盘上,以便故障磁盘不会导致数据丢失),服务器可能有双电源和可热插拔的 CPU,数据中心可能有电池和柴油发电机作为备用电源。这种冗余通常可以使机器不间断运行多年。
|
||||
|
||||
当组件故障独立时,冗余最有效,即一个故障的发生不会改变另一个故障发生的可能性。然而,经验表明,组件故障之间通常存在显著的相关性 [^41] [^57] [^58];整个服务器机架或整个数据中心的不可用仍然比我们希望的更频繁地发生。
|
||||
当组件故障独立时,冗余最有效,即一个故障的发生不会改变另一个故障发生的可能性。然而,经验表明,组件故障之间通常存在显著的相关性 [^41] [^57] [^58];整个服务器机架或整个数据中心的不可用仍然比我们预期的更频繁地发生。
|
||||
|
||||
硬件冗余增加了单台机器的正常运行时间;然而,如 ["分布式与单节点系统"](/ch1#sec_introduction_distributed) 中所讨论的,使用分布式系统有一些优势,例如能够容忍一个数据中心的完全中断。出于这个原因,云系统倾向于较少关注单个机器的可靠性,而是旨在通过在软件级别容忍故障节点来使服务高度可用。云提供商使用 *可用区* 来识别哪些资源在物理上位于同一位置;同一地方的资源比地理上分离的资源更可能同时发生故障。
|
||||
|
||||
|
|
@ -242,27 +242,27 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
|
||||
### 人类与可靠性 {#id31}
|
||||
|
||||
人类设计和构建软件系统,保持系统运行的操作员也是人类。与机器不同,人类不只是遵循规则;他们的力量是创造性和适应性地完成工作。然而,这一特征也导致不可预测性,有时会导致失效的错误,尽管有最好的意图。例如,一项对大型互联网服务的研究发现,操作员的配置更改是中断的主要原因,而硬件故障(服务器或网络)仅在 10-25% 的中断中发挥作用 [^70]。
|
||||
人类设计和构建软件系统,保持系统运行的操作员也是人类。与机器不同,人类不只是遵循规则;他们的优势是创造性和适应性地完成工作。然而,这一特征也导致了不可预测性,有时会导致失效的错误,即使本意是好的。例如,一项对大型互联网服务的研究发现,操作员的配置更改是中断的主要原因,而硬件故障(服务器或网络)仅在 10-25% 的中断中发挥作用 [^70]。
|
||||
|
||||
将这些问题标记为"人为错误"并希望通过更严格的程序和规则合规性来更好地控制人类行为来解决它们是很诱人的。然而,责怪人们的错误是适得其反的。我们所说的"人为错误"实际上不是事件的原因,而是人们在社会技术系统中尽力做好工作的问题的症状 [^71]。通常,复杂系统具有紧急行为,组件之间的意外交互也可能导致故障 [^72]。
|
||||
人们很自然地倾向于将这类问题归咎于“人为错误”,并希望通过更严格的程序和规则遵守来更好地控制人为行为从而解决问题。然而,将错误归咎于人是适得其反的。我们所说的“人为错误”并非事件的真实原因,而是人们尽力工作时,社会技术系统中存在问题的征兆 [^71]。通常,复杂系统具有紧急行为,组件之间的意外交互也可能导致故障 [^72]。
|
||||
|
||||
各种技术措施可以帮助最小化人为错误的影响,包括彻底测试(手写测试和对大量随机输入的 *属性测试*)[^38]、快速回滚配置更改的回滚机制、新代码的逐步推出、详细和清晰的监控、用于诊断生产问题的可观测性工具(参见 ["分布式系统的问题"](/ch1#sec_introduction_dist_sys_problems)),以及鼓励"正确的事情"并阻止"错误的事情"的精心设计的界面。
|
||||
各种技术措施可以帮助最小化人为错误的影响,包括彻底测试(手写测试和对大量随机输入的 *属性测试*)[^38]、快速回滚配置更改的回滚机制、新代码的渐进部署、详细和清晰的监控、用于诊断生产问题的可观测性工具(参见 ["分布式系统的问题"](/ch1#sec_introduction_dist_sys_problems)),以及鼓励"正确的事情"并阻止"错误的事情"的精心设计的界面。
|
||||
|
||||
然而,这些事情需要时间和金钱的投资,在日常业务的务实现实中,组织通常优先考虑创收活动而不是增加其抵御错误的韧性的措施。如果在更多功能和更多测试之间有选择,许多组织可以理解地选择功能。鉴于这种选择,当可预防的错误不可避免地发生时,责怪犯错误的人是没有意义的——问题是组织的优先事项。
|
||||
然而,这些事情需要时间和金钱的投资,在日常业务的务实现实中,组织通常优先考虑创收活动而不是增加其抵御错误的韧性措施。如果要在更多功能和更多测试之间做出选择,许多组织会很自然地选择功能。鉴于这种选择,当可预防的错误不可避免地发生时,责怪犯错误的人是没有意义的——问题在于组织的事项优先级。
|
||||
|
||||
越来越多的组织正在采用 *无责备事后分析* 的文化:事件发生后,鼓励相关人员充分分享发生的事情的细节,而不用担心惩罚,因为这允许组织中的其他人学习如何在未来防止类似的问题 [^73]。这个过程可能会发现需要改变业务优先级、需要投资于被忽视的领域、需要改变相关人员的激励措施,或者需要引起管理层注意的其他一些系统性问题。
|
||||
越来越多的组织正在采用 *无责备事后分析* 的文化:事件发生后,鼓励相关人员充分分享发生的事情的细节,而不用担心惩罚,因为这允许组织中的其他人学习如何在未来防止类似的问题 [^73]发生。这个过程可能会发现需要改变业务优先级、需要投资于被忽视的领域、需要改变相关人员的激励措施,或者需要引起管理层注意的其他一些系统性问题。
|
||||
|
||||
作为一般原则,在调查事件时,你应该对简单化的答案持怀疑态度。"鲍勃在部署该更改时应该更加小心"是没有成效的,但"我们必须用 Haskell 重写后端"也不是。相反,管理层应该借此机会从每天与之合作的人的角度了解社会技术系统如何工作的细节,并根据这些反馈采取措施改进它 [^71]。
|
||||
作为一般原则,在调查事件时,你应该对简单化的答案持怀疑态度。"鲍勃在部署该更改时应该更加小心"是没有意义的,"我们必须用 Haskell 重写后端"也一样。相反,管理层应该借此机会从每天与之合作的人的角度了解社会技术系统如何工作的细节,并根据这些反馈采取措施改进它 [^71]。
|
||||
|
||||
--------
|
||||
|
||||
> [!TIP] 可靠性有多重要?
|
||||
|
||||
可靠性不仅仅适用于核电站和空中交通管制——更平凡的应用程序也应该可靠地工作。业务应用程序中的错误会导致生产力损失(如果数字报告不正确,还会有法律风险),电子商务网站的中断可能会在收入和声誉损害方面造成巨大成本。
|
||||
可靠性不仅仅适用于核电站和空中交通管制——更普通的应用程序也应该可靠地工作。业务应用程序中的错误会导致生产力损失(如果数据被不正确地报告,还会有法律风险),而电子商务网站的中断会因收入损失和声誉受损而产生巨大的成本。
|
||||
|
||||
在许多应用程序中,几分钟甚至几小时的临时中断是可以容忍的 [^74],但永久数据丢失或损坏将是灾难性的。考虑一位家长在你的照片应用程序中存储他们孩子的所有照片和视频 [^75]。如果该数据库突然损坏,他们会有什么感觉?他们会知道如何从备份中恢复吗?
|
||||
|
||||
作为不可靠软件如何伤害人们的另一个例子,考虑邮局地平线丑闻。在 1999 年至 2019 年期间,管理英国邮局分支机构的数百人因会计软件显示其账户短缺而被判盗窃或欺诈罪。最终变得清楚,许多这些短缺是由于软件中的错误,许多定罪已被推翻 [^76]。导致这一可能是英国历史上最大的司法不公的是,英国法律假设计算机正确运行(因此,计算机产生的证据是可靠的),除非有相反的证据 [^77]。软件工程师可能会嘲笑软件可能无错误的想法,但这对那些因不可靠的计算机系统而被错误监禁、宣布破产甚至自杀的人来说,这是很少的安慰。
|
||||
作为不可靠软件如何伤害人们的另一个例子,可以参考邮局“Horizon”丑闻。在 1999 年至 2019 年期间,管理英国邮局分支机构的数百人因会计软件显示其账户财务漏洞而被判盗窃或欺诈罪。最终真相水落石出,许多这些财务漏洞是由于软件中的错误,许多定罪已被推翻 [^76]。导致这一可能是英国历史上最大的司法不公的是,英国法律假设计算机正确运行(因此,计算机产生的证据是可靠的),除非有相反的证据 [^77]。软件工程师可能会嘲笑软件可能无错误的想法,但这对那些因不可靠的计算机系统而被错误监禁、宣布破产甚至自杀的人来说,这几乎没什么安慰。
|
||||
|
||||
在某些情况下,我们可能选择牺牲可靠性以降低开发成本(例如,在为未经证实的市场开发原型产品时)——但我们应该非常清楚何时走捷径并牢记潜在的后果。
|
||||
|
||||
|
|
@ -279,16 +279,16 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
原因是可伸缩性不是一维标签:说"X 是可伸缩的"或"Y 不伸缩"是没有意义的。相反,讨论可伸缩性意味着考虑诸如以下问题:
|
||||
|
||||
* "如果系统以特定方式增长,我们有什么选择来应对增长?"
|
||||
* "我们如何添加计算资源来处理额外的负载?"
|
||||
* "我们如何增加计算资源来处理额外的负载?"
|
||||
* "基于当前的增长预测,我们何时会达到当前架构的极限?"
|
||||
|
||||
如果你成功地使你的应用程序受欢迎,因此处理越来越多的负载,你将了解你的性能瓶颈在哪里,因此你将知道需要沿着哪些维度进行伸缩。那时是开始担心可伸缩性技术的时候。
|
||||
|
||||
### 描述负载 {#id33}
|
||||
|
||||
首先,我们需要简洁地描述系统上的当前负载;只有这样我们才能讨论增长问题(如果我们的负载翻倍会发生什么?)。通常这将是吞吐量的度量:例如,对服务的每秒请求数、每天到达多少千兆字节的新数据,或每小时购物车结账的数量。有时你关心某个变量数量的峰值,例如 ["案例研究:社交网络首页时间线"](/ch2#sec_introduction_twitter) 中同时在线用户的数量。
|
||||
首先,我们需要简洁地描述系统上的当前负载;只有这样我们才能讨论增长问题(如果我们的负载翻倍会发生什么?)。通常这将是吞吐量的度量:例如,对服务的每秒请求数、每天到达多少千兆字节的新数据,或每小时购物车结账的数量。有时你关心某个变量数值的峰值,例如 ["案例研究:社交网络首页时间线"](/ch2#sec_introduction_twitter) 中同时在线用户的数量。
|
||||
|
||||
通常还有其他影响访问模式并因此影响可伸缩性要求的负载统计特征。例如,你可能需要知道数据库中的读写比率、缓存的命中率或每个用户的数据项数量(例如,社交网络案例研究中的粉丝数量)。也许平均情况对你很重要,或者也许你的瓶颈由少数极端情况主导。这一切都取决于你特定应用程序的细节。
|
||||
通常还有其他负载统计特征,会影响访问模式,从而影响可扩展性要求。例如,你可能需要知道数据库中的读写比率、缓存的命中率或每个用户的数据项数量(例如,社交网络案例研究中的粉丝数量)。也许平均情况对你很重要,或者也许你的瓶颈由少数极端情况主导。这一切都取决于你特定应用程序的细节。
|
||||
|
||||
一旦你描述了系统上的负载,你就可以调查当负载增加时会发生什么。你可以从两个方面来看待它:
|
||||
|
||||
|
|
@ -303,7 +303,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
|
||||
增加服务硬件资源的最简单方法是将其移动到更强大的机器。单个 CPU 核心不再变得显著更快,但你可以购买一台机器(或租用云实例)具有更多 CPU 核心、更多 RAM 和更多磁盘空间。这种方法称为 *纵向伸缩* 或 *向上扩展*。
|
||||
|
||||
你可以通过使用多个进程或线程在单台机器上获得并行性。属于同一进程的所有线程都可以访问相同的 RAM,因此这种方法也称为 *共享内存架构*。共享内存方法的问题是成本增长速度快于线性:具有两倍硬件资源的高端机器通常成本远远超过两倍。由于瓶颈,两倍大小的机器通常可以处理不到两倍的负载。
|
||||
你可以通过使用多个进程或线程在单台机器上获得并行性。属于同一进程的所有线程都可以访问相同的 RAM,因此这种方法也称为 *共享内存架构*。共享内存方法的问题是成本增长速度快于线性:具有两倍硬件资源的高端机器通常成本远远超过两倍。由于瓶颈,两倍大小的机器通常只能处理不到两倍的负载。
|
||||
|
||||
另一种方法是 *共享磁盘架构*,它使用几台具有独立 CPU 和 RAM 的机器,但将数据存储在机器之间共享的磁盘阵列上,这些机器通过快速网络连接:*网络附加存储*(NAS)或 *存储区域网络*(SAN)。这种架构传统上用于本地数据仓库工作负载,但争用和锁定的开销限制了共享磁盘方法的可伸缩性 [^81]。
|
||||
|
||||
|
|
@ -321,15 +321,15 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
|
||||
可伸缩性的一个良好通用原则是将系统分解为可以在很大程度上相互独立运行的较小组件。这是微服务背后的基本原则(参见 ["微服务与无服务器"](/ch1#sec_introduction_microservices))、分片([第 7 章](/ch7))、流处理([待补充链接])和无共享架构。然而,挑战在于知道在哪里划分应该在一起的事物和应该分开的事物之间的界限。微服务的设计指南可以在其他书籍中找到 [^84],我们在 [第 7 章](/ch7) 中讨论无共享系统的分片。
|
||||
|
||||
另一个好原则是不要让事情变得比必要的更复杂。如果单机数据库可以完成工作,它可能比复杂的分布式设置更可取。自动伸缩系统(根据需求自动添加或删除资源)很酷,但如果你的负载相当可预测,手动伸缩的系统可能会有更少的操作意外(参见 ["操作:自动或手动再平衡"](/ch7#sec_sharding_operations))。具有五个服务的系统比具有五十个服务的系统更简单。良好的架构通常涉及方法的务实混合。
|
||||
另一个好原则是不要让事情变得比必要的更复杂。如果单机数据库可以完成工作,它可能比复杂的分布式设置更可取。自动伸缩系统(根据需求自动添加或删除资源)很酷,但如果你的负载相当可预测,手动伸缩的系统可能会有更少的操作意外(参见 ["操作:自动或手动再平衡"](/ch7#sec_sharding_operations))。具有五个服务的系统比具有五十个服务的系统更简单。良好的架构通常涉及多种方案的务实混合。
|
||||
|
||||
## 可维护性 {#sec_introduction_maintainability}
|
||||
|
||||
软件不会磨损或遭受材料疲劳,因此它不会像机械物体那样以同样的方式损坏。但应用程序的要求经常变化,软件运行的环境发生变化(例如其依赖项和底层平台),并且它有需要修复的错误。
|
||||
软件不会磨损或遭受材料老化,因此它不会像机械物体那样损坏。但应用程序的需求经常变化,软件运行在变化的环境中(例如其依赖项和底层平台),并且它有需要修复的错误。
|
||||
|
||||
人们普遍认为,软件的大部分成本不在其初始开发中,而在其持续维护中——修复错误、保持其系统运行、调查故障、将其适应新平台、为新用例修改它、偿还技术债务和添加新功能 [^85] [^86]。
|
||||
|
||||
然而,维护也很困难。如果系统已成功运行很长时间,它可能使用今天不多工程师理解的过时技术(如大型机和 COBOL 代码);关于系统如何以及为何以某种方式设计的机构知识可能已经随着人们离开组织而丢失;可能需要修复其他人的错误。此外,计算机系统通常与它支持的人类组织交织在一起,这意味着此类 *遗留* 系统的维护既是人的问题,也是技术问题 [^87]。
|
||||
然而,维护也很困难。如果系统已成功运行很长时间,它可能使用如今很少有工程师理解的过时技术(如大型机和 COBOL 代码);随着人员的离开,关于系统如何以及为何以某种特定方式设计的制度性知识可能已经丢失了;可能需要修复其他人的错误。此外,计算机系统通常与它支持的人类组织交织在一起,这意味着此类 *遗留* 系统的维护既是人的问题,也是技术问题 [^87]。
|
||||
|
||||
如果我们今天创建的每个系统都足够有价值以长期生存,它有一天将成为遗留系统。为了最小化需要维护我们软件的未来几代人的痛苦,我们应该在设计时考虑维护问题。尽管我们不能总是预测哪些决定可能会在未来造成维护难题,但在本书中,我们将注意几个广泛适用的原则:
|
||||
|
||||
|
|
@ -340,7 +340,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
: 通过使用易于理解、一致的模式和结构来实施它,并避免不必要的复杂性,使新工程师容易理解系统。
|
||||
|
||||
可演化性(Evolvability)
|
||||
: 使工程师将来容易对系统进行更改,随着需求变化而适应和扩展它以用于未预料的用例。
|
||||
: 使工程师将来容易对系统进行更改,随着需求变化而适应和扩展它以用于未预料的使用场景。
|
||||
|
||||
### 可运维性:让运维更轻松 {#id37}
|
||||
|
||||
|
|
@ -348,7 +348,7 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
事实上,有人提出 “良好的运维通常可以解决糟糕(或不完整)软件的局限性,但再好的软件碰上糟糕的运维也难以可靠地运行” [^60]。
|
||||
|
||||
在由数千台机器组成的大规模系统中,手动维护将是不合理地昂贵的,自动化是必不可少的。然而,自动化可能是一把双刃剑:
|
||||
总会有边缘情况(如罕见的故障场景)需要运维团队的手动干预。由于无法自动处理的情况是最复杂的问题,更大的自动化需要一个 **更** 熟练的运维团队来解决这些问题 [^88]。
|
||||
总会有边际场景(如罕见的故障场景)需要运维团队的手动干预。由于无法自动处理的情况是最复杂的问题,更大的自动化需要一个 **更** 熟练的运维团队来解决这些问题 [^88]。
|
||||
|
||||
此外,如果自动化系统出错,通常比依赖操作员手动执行某些操作的系统更难排除故障。出于这个原因,更多的自动化并不总是对可操作性更好。
|
||||
然而,一定程度的自动化很重要,最佳点将取决于你特定应用程序和组织的细节。
|
||||
|
|
@ -356,8 +356,8 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
良好的可操作性意味着使常规任务变得容易,使运维团队能够将精力集中在高价值活动上。
|
||||
数据系统可以做各种事情来使常规任务变得容易,包括 [^89]:
|
||||
|
||||
* 允许监控工具检查系统的关键指标,并支持可观测性工具(参见 ["分布式系统的问题"](/ch1#sec_introduction_dist_sys_problems))以深入了解系统的运行时行为。各种商业和开源工具可以在这里提供帮助 [^90]。
|
||||
* 避免对单个机器的依赖(允许在系统整体继续不间断运行的同时关闭机器进行维护)
|
||||
* 允许监控工具检查系统的关键指标,并支持可观测性工具(参见 ["分布式系统的问题"](/ch1#sec_introduction_dist_sys_problems))以深入了解系统的运行时行为。各种商业和开源工具可以在这方面提供帮助 [^90]。
|
||||
* 避免对单个机器的依赖(允许在系统整体持续不间断运行的同时关闭机器进行维护)
|
||||
* 提供良好的文档和易于理解的操作模型("如果我做 X,Y 将会发生")
|
||||
* 提供良好的默认行为,但也给管理员在需要时覆盖默认值的自由
|
||||
* 在适当的地方自我修复,但也在需要时给管理员手动控制系统状态
|
||||
|
|
@ -365,8 +365,8 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
|
||||
### 简单性:管理复杂度 {#id38}
|
||||
|
||||
小型软件项目可以有令人愉快地简单和富有表现力的代码,但随着项目变大,它们通常变得非常复杂且难以理解。
|
||||
这种复杂性减慢了需要在系统上工作的每个人,进一步增加了维护成本。陷入复杂性的软件项目有时被描述为 *大泥球* [^91]。
|
||||
小型软件项目可以有令人愉悦的、简单而富有表现力的代码,但随着项目变大,它们通常变得非常复杂且难以理解。
|
||||
这种复杂性减慢了需要在系统上工作的每个人的效率,进一步增加了维护成本。陷入复杂性的软件项目有时被描述为 *大泥团* [^91]。
|
||||
|
||||
当复杂性使维护困难时,预算和时间表经常超支。在复杂软件中,进行更改时引入错误的风险也更大:
|
||||
当系统对开发人员来说更难理解和推理时,隐藏的假设、意外的后果和意外的交互更容易被忽视 [^69]。
|
||||
|
|
@ -381,10 +381,10 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
不幸的是,这种区别也有缺陷,因为本质和偶然之间的边界随着我们工具的发展而变化 [^94]。
|
||||
|
||||
我们管理复杂性的最佳工具之一是 **抽象**。良好的抽象可以在干净、易于理解的外观后面隐藏大量实现细节。良好的抽象也可以用于各种不同的应用程序。
|
||||
这种重用不仅比多次重新实现类似的东西更有效,而且还导致更高质量的软件,因为抽象组件中的质量改进使所有使用它的应用程序受益。
|
||||
这种重用不仅比多次重新实现类似的东西更有效,而且还能提高软件质量,因为抽象组件中的质量改进使所有使用它的应用程序受益。
|
||||
|
||||
例如,高级编程语言是隐藏机器代码、CPU 寄存器和系统调用的抽象。SQL 是一种隐藏复杂的磁盘上和内存中数据结构、来自其他客户端的并发请求以及崩溃后不一致性的抽象。
|
||||
当然,在用高级语言编程时,我们仍在使用机器代码;我们只是不 *直接* 使用它,因为编程语言抽象使我们免于考虑它。
|
||||
例如,高级编程语言是隐藏机器码、CPU 寄存器和系统调用的抽象。SQL 是一种隐藏磁盘和内存中的复杂数据结构、来自其他客户端的并发请求以及崩溃后的不一致性的抽象。
|
||||
当然,在用高级语言编程时,我们仍在使用机器码;我们只是不 *直接* 使用它,因为编程语言抽象使我们不必考虑它。
|
||||
|
||||
应用程序代码的抽象,旨在降低其复杂性,可以使用诸如 *设计模式* [^95] 和 *领域驱动设计*(DDD)[^96] 等方法创建。
|
||||
本书不是关于此类特定于应用程序的抽象,而是关于你可以在其上构建应用程序的通用抽象,例如数据库事务、索引和事件日志。如果你想使用像 DDD 这样的技术,你可以在本书中描述的基础之上实现它们。
|
||||
|
|
@ -396,10 +396,10 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
新平台取代旧平台、法律或监管要求发生变化、系统增长迫使架构变化等。
|
||||
|
||||
在组织流程方面,*敏捷* 工作模式为适应变化提供了框架。敏捷社区还开发了在频繁变化的环境中开发软件时有用的技术工具和流程,
|
||||
例如测试驱动开发(TDD)和重构。在本书中,我们搜索在由具有不同特征的几个不同应用程序或服务组成的系统级别增加敏捷性的方法。
|
||||
例如测试驱动开发(TDD)和重构。在本书中,我们探寻在由具有不同特征的几个不同应用程序或服务组成的系统级别增加敏捷性的方法。
|
||||
|
||||
你可以修改数据系统并使其适应不断变化的需求的容易程度与其简单性及其抽象密切相关:松散耦合、简单系统通常比紧密耦合、复杂系统更容易修改。
|
||||
由于这是一个如此重要的想法,我们将使用不同的词来指代数据系统级别的敏捷性:*可演化性* [^97]。
|
||||
你可以修改数据系统并使其适应不断变化的需求的容易程度与其简单性及其抽象密切相关:松耦合、简单的系统通常比紧耦合、复杂的系统更容易修改。
|
||||
由于这是一个如此重要的概念,我们将使用一个不同的词来指代数据系统级别的敏捷性:*可演化性* [^97]。
|
||||
|
||||
使大型系统中的变化困难的一个主要因素是某些操作不可逆,因此需要非常谨慎地采取该操作 [^98]。
|
||||
例如,假设你正在从一个数据库迁移到另一个数据库:如果在新数据库出现问题时无法切换回旧系统,风险就会高得多,而如果你可以轻松返回。最小化不可逆性提高了灵活性。
|
||||
|
|
@ -407,10 +407,10 @@ Akamai 最近的一项研究 [^24] 声称响应时间增加 100 毫秒将电子
|
|||
## 总结 {#summary}
|
||||
|
||||
在本章中,我们研究了几个非功能性需求的例子:性能、可靠性、可伸缩性和可维护性。
|
||||
通过这些主题,我们还遇到了我们在本书其余部分需要的原则和术语。我们从社交网络中首页时间线如何实现的案例研究开始,这说明了规模上出现的一些挑战。
|
||||
通过这些主题,我们还遇到了我们在本书其余部分需要的原则和术语。我们从社交网络中首页时间线如何实现的案例研究开始,这说明了在规模扩大时出现的一些挑战。
|
||||
|
||||
我们讨论了如何衡量性能(例如,使用响应时间百分位数)、系统上的负载(例如,使用吞吐量指标),以及它们如何在 SLA 中使用。
|
||||
可伸缩性是一个密切相关的概念:即,在负载增长时确保性能保持不变。我们看到了可伸缩性的一些一般原则,例如将任务分解为可以独立运行的较小部分,我们将在以下章节中深入研究可伸缩性技术的技术细节。
|
||||
可伸缩性是一个密切相关的概念:即,在负载增长时确保性能保持不变。我们看到了可伸缩性的一些通用性原则,例如将任务分解为可以独立运行的较小部分,我们将在以下章节中深入研究可伸缩性技术的技术细节。
|
||||
|
||||
为了实现可靠性,你可以使用容错技术,即使某个组件(例如,磁盘、机器或其他服务)出现故障,系统也可以继续提供其服务。
|
||||
我们看到了可能发生的硬件故障的例子,并将它们与软件故障区分开来,软件故障可能更难处理,因为它们通常是强相关的。
|
||||
|
|
|
|||
Loading…
Reference in a new issue