KIOSHIROI's CS-learning Road

可靠、可扩展与可维护的应用系统

当今许多新型应用都属于数据密集型(data-intensive)而不是计算密集型(compute-intensive)。对于这些类型应用,CPU的处理能力往往不是第一限制性因素,关键在于数据量、数据的复杂度及数据的快速多变性。

数据密集型应用通常也是基于标准模块构建而成,每个模块负责单一的常用功能,例如,许多应用系统都包含以下模块:

  • 数据库:用以存储数据,这样之后应用可以再次访问
  • 高速缓存:缓存那些复杂或操作代价昂贵的结果,以加速下一次访问。
  • 索引:用户可以按关键字搜索数据并支持各种过滤
  • 流式处理:持续发送消息至另一个进程,处理采用异步方式
  • 批处理:定期处理大量的累计数据

可靠性:当出现意外情况如硬件、软件故障、人为失误等,系统应可能继续正常运转,虽然性能可能有所降低,但确保功能正确。

可扩展性:随着规模的增长,例如数据量、流量或复杂性,系统应以合理的方式来匹配这种增长。

可维护性:随着时间的推移,许多新的人员会参与到系统开发和运维,以维护现有功能或适配新场景等,系统都应高效运转。

可靠性

对于软件,典型的期望包括:

  • 应用程序执行用户所期望的功能
  • 可以容忍用户出现错误或不正确的软件使用方法
  • 性能可以应对典型场景,合理负载压力和数据量
  • 系统可防止任何未经授权的访问和滥用

可能出错的事情称为错误(fault)或故障,系统可以应对错误则称为容错(fault tolerant)或者弹性(resilient)

容错总是指特定类型的故障(而不是各种可能的类型),这样的系统才更有实际意义

故障(fault)与失效(failure)不完全一直。故障通常被定义为组件偏离其正常规格,而失效意味系统作为一个整体停止,无法向用户提供所需的服务,我们不太可能将故障概率降为零,因此通常设计容错机制来避免从故障引发系统失效

硬件故障

当有很多机器时,这类错误迟早会发生

  1. 硬件冗余方案(对磁盘配置 RAID,服务器配置双电源、热插拔 CPU,数据中心添加备用电源,发电机等)

采用硬件冗余方案对大多数应用场景是足够的,它使得单台机器完全失效的概率降为非常低的水平

  1. 软件容错方式

软件错误

通常认为硬件故障之间多是相互独立的:一台机器的磁盘出现故障并不意味着另一台机器的磁盘要失效。

另一类故障则是系统内的软件问题。这些故障事先更加难以预料,而且因为节点之间是由软件关联的,因而往往会导致更多的系统故障。

导致软件故障的 bug 通常会长时间处于引而不发的状态,直到碰到特定的触发条件

人为失误

如果我们假定人是不可靠的,那么该如何保证系统的可靠性呢?

  • 以最小出错的方式来设计系统
  • 想办法分离最容易出错的地方,容易引发故障的接口
  • 充分的测试:从各单元测试到全系统集成测试以及手动测试。
  • 当出现人为失误时,提供快速的恢复机制以尽量减少故障影响。
  • 设置详细而清晰的遥控子系统,包括性能指标和错误率(遥测)
  • 推行管理流程并加以培训

可扩展性

可扩展性是用来描述系统应对负载增加能力的术语。

讨论可扩展性通常要考虑这类问题:“如果系统以某种方式增长,我们应对增长的措施有哪些”“我们该如何增加计算资源来处理额外的负载?”

描述负载

负载可以用称为负载参数的若干数字来描述。参数的最佳选择取决于系统的体系结构,它可能是 Web 服务器的每秒请求处理次数,数据库中写入的比例,聊天室的同时活动用户数量,缓存命中率等。有时平均值很重要,有时系统瓶颈来自少量峰值。

描述性能

在批处理系统中,我们通常关心吞吐量,即每秒可处理的记录条数,或在某指定数据集运行作业所需的总时间;而在线系统通常更看重服务器的响应时间,即客户端从发送请求到接受响应之间的间隔。

采用较高的响应时间百分位数(tail latencies,尾部延迟或长尾效应)很重要,因为他们直接影响用户的总体服务体验

百分位数通常用于描述、定义服务质量目标(Service Level Objectives,SLO)和服务质量协议(Service Level Agreements,SLA),这些是规定服务预期质量和可用性的合同,例如一份SLA合约通常会声明,响应时间中位数小于 200 ms,99%请求的响应时间小于 1s,且要求至少 99.9% 的事件都要达到上述服务指标,这些指标明确了服务质量预期,并允许客户在不符合 SLA 的情况下进行赔偿。

排队延迟往往在高百分比数响应时间中影响很大,由于服务器并行处理的请求有限,还在处理的少数请求可能会阻挡后续请求,这种情况有时称为队头阻塞。

可维护性

  • 可运维性:方便运行团队来保护系统稳定
  • 简单性:简化系统复杂性,使新工程师能够轻松理解系统。注意这与用户界面的简单性并不一样
  • 可演化性:后续工程师能够轻松地对系统进行改进,并根据需求变化将其适配到非典型场景,也称为可延伸性,易修改性或可塑性

第8章 分布式系统的挑战

“所有可能出错的事情一定会出错”

分布式系统与在单节点上的软件有着非常显著的区别。

故障与部分失效

在分布式系统中,可能会出现系统的一部分工作正常,但其他某些部分出现难以预期的故障,我们称之为“部分失效”

云计算与超算

关于如何构建大规模计算系统有以下几种不同的思路:

  • 规模的一个极端是高性能计算(high-performance computing,HPC)。包括成千上万个CPU的超级计算机构成一个庞大的集群。通常用于计算密集型的科学计算任务
  • 另一个计算是云计算。虽然云计算的定义并非那么明确,但通常它是具有以下特征:多租户数据中心,通用计算机,用 IP 以太网链接,弹性/按需分配并按需计费
  • 传统企业数据中心则位于以上两个极端之间

不同的集群构建方式所对应的错误处理方法也不尽相同。对于高性能计算,通常会定期对人物状态进行快照,然后保存在持久存储上。当某节点出现故障,解决方案是简单地停止整个集群的任务;等故障节点修复之后,从最近的快照检查点继续执行,从这一点上看高性能计算其实更像是一个单节点系统而不是分布式系统,它将局部失效升级为整体失效

不可靠的网络

互联网以及大多数数据中心的内部网络都是异步网络,在这种网络中,一个节点可以发送消息到另一个节点,但是网络并不保护它什么时候到达甚至它是否一定到达。发送之后等待响应过程中,有很多事情可能会出错。

  1. 请求可能已经丢失
  2. 请求可能正在某个队列中等待,无法马上发送
  3. 远程接收节点可能已经失效
  4. 远程接收节点可能暂时无法响应
  5. 远程接收节点已经完成了请求处理,但回复在网络中丢失
  6. 运城接受节点已经完成了请求处理,但回复却被延迟处理

网络阻塞与排队

计算机网络上的数据包延的变化根源往往在于排队:

  • 当多个不同节点同时发送数据包到相同的目标节点时,网络交换机会出现排队,然后依次将数据包转发到目标网络。如果网络负载过重,数据包可能必须等待一段时间才能获得发送机会(即网络拥塞),如果数据量太大,交换机队列塞满,之后的数据包则会倍丢弃,网络还在运转,但会引发大量数据包重传。
  • 当数据包到达目标机器后,如果所有CPU核都处于繁忙状态,则网络数据请求会被操作系统排队,直到应用程序能够处理。根据机器的配置和负载情况,这里也会引入一段不确定的等待时间
  • 在虚拟机环境下,CPU核会切换虚拟机,从而导致在运行的操作系统会突然暂停几十毫秒。在这段时间,客户虚机无法从网络中接收任何数据。入向的包会被虚拟机管理器排队缓冲,进一步增加了网络延迟的不确定性
  • TCP 执行流量控制(也称拥塞消除,或背压)时,节点会主动限制自己的发射速率以避免加重网络链路或接收节点负载。这意味着数据甚至在进入网络之前,已经在发送方开始了排队。

同步与异步网络

为什么数据中心网络和互联网采用分组交换呢?答案是针对突发流量进行了很多优化。电路非常适合音频或视频通话,通话期间只需每秒传递固定数量的数据。如果你想通过电路链接来传输文件,将不得不预估一个待分配的带宽,对于突发数据和传输,电路网络无法充分利用网络容量,导致发送缓慢。相比之下,TCP动态调整传输速率则可以充分利用所有可用的网络容量。、

不可靠的时钟

在分布式系统中,时间总是件棘手的问题,由于跨节点通信不可能即时完成,消息经由网络从一台机器到另一台机器总是需要花费时间。

可以在一定程度上同步机器之间的时钟。最常用的方法是网络时间协议(NTP),他可以根据一组专门的时间服务器来调整本地时间,时间服务器则从精确更高的时间源获取高精度时间。

现代计算机内部至少有两种不同的时钟:一是墙上时钟,一个是单调时钟。

墙上时钟根据某个日历返回当前的日期和时间。(1970年1月1日)

单调时钟更适合测量持续时间段(时间间隔),例如超时或服务的响应时间


在 Notion 参与讨论

本文托管在 Notion,欢迎到原文评论区留言交流

在 Notion 打开
笔记-DDIA
https://kioshiroi.github.io/blog/csapp5
Author KIOSHIROI
Published at 2026年5月14日
Comment seems to stuck. Try to refresh?✨