可靠性

可靠性(Reliability)

系统的可靠性是指在规定的时间内及规定的环境下完成规定功能的能力,也就是系统的无故障运行概率。人们对可靠软件的典型期望包括:

  • 应用程序表现出用户所期望的功能。
  • 允许用户犯错,允许用户以出乎意料的方式使用软件。
  • 在预期的负载和数据量下,性能满足要求。
  • 系统能防止未经授权的访问和滥用。

可靠性不仅仅是针对核电站和空中交通管制软件而言,我们也期望更多平凡的应用能可靠地运行。商务应用中的错误会导致生产力损失(也许数据报告不完整还会有法律风险),而电商网站的中断则可能会导致收入和声誉的巨大损失。

故障与错误

造成错误的原因叫做故障(fault),能预料并应对故障的系统特性可称为容错(fault-tolerant)或韧性(resilient)。注意故障(fault)不同于失效(failure),故障通常定义为系统的一部分状态偏离其标准,而失效则是系统作为一个整体停止向用户提供服务。故障的概率不可能降到零,因此最好设计容错机制以防因故障而导致失效。

在这类容错系统中,通过故意触发来提高故障率是有意义的,例如:在没有警告的情况下随机地杀死单个进程。许多高危漏洞实际上是由糟糕的错误处理导致的,像 Netflix 公司的 Chaos Monkey 就是通过故意引发故障来确保容错机制不断运行并接受考验,从而提高故障自然发生时系统能正确处理的信心的例子。

安全性和活性

为了澄清这种情况,有必要区分两种不同的性质:安全性(safety)和活性(liveness)。在刚刚给出的例子中,唯一性(uniqueness)和单调序列(monotonic sequence)是安全属性,但可用性是活性(liveness)属性。安全性通常被非正式地定义为,没有坏事发生,而活性通常就类似:最终好事发生。但是,最好不要过多地阅读那些非正式的定义,因为好与坏的含义是主观的。安全性和活性的实际定义是精确的和数学的:

  • 如果安全属性被违反,我们可以指向一个特定的时间点(例如,如果违反了唯一性属性,我们可以确定重复的防护令牌返回的特定操作)。违反安全属性后,违规行为不能撤销——损失已经发生。

  • 活性属性反过来:在某个时间点(例如,一个节点可能发送了一个请求,但还没有收到响应),它可能不成立,但总是希望在未来(即通过接受答复)。

区分安全性和活性属性的一个优点是可以帮助我们处理困难的系统模型。对于分布式算法,在系统模型的所有可能情况下,要求始终保持安全属性是常见的。也就是说,即使所有节点崩溃,或者整个网络出现故障,算法仍然必须确保它不会返回错误的结果(即保证安全性得到满足)。但是,对于活性属性,我们可以提出一些注意事项:例如,只有在大多数节点没有崩溃的情况下,只有当网络最终从中断中恢复时,我们才可以说请求需要接收响应。部分同步模型的定义要求系统最终返回到同步状态——即任何网络中断的时间段只会持续一段有限的时间,然后进行修复。

安全性和活性属性以及系统模型对于推理分布式算法的正确性非常有用。然而,在实践中实施算法时,现实的混乱事实再一次地让你咬牙切齿,很明显系统模型是对现实的简化抽象。例如,在故障恢复模型中的算法通常假设稳定存储器中的数据经历了崩溃。但是,如果磁盘上的数据被破坏,或者由于硬件错误或错误配置导致数据被清除,会发生什么情况?如果服务器存在固件错误并且在重新启动时无法识别其硬盘驱动器,即使驱动器已正确连接到服务器,也会发生什么情况?

法定人数算法依赖节点来记住它声称存储的数据。如果一个节点可能患有健忘症,忘记了以前存储的数据,这会打破法定条件,从而破坏算法的正确性。也许需要一个新的系统模型,在这个模型中,我们假设稳定的存储大多存在崩溃,但有时可能会丢失。但是那个模型就变得更难以推理了。算法的理论描述可以简单宣称一些事在假设上是不会发生的——在非拜占庭式系统中。但实际上我们还是需要对可能发生和不可能发生的故障做出假设,真实世界的实现,仍然会包括处理“假设上不可能”情况的代码,即使代码可能就是 printf(“you sucks”)和 exit(666),实际上也就是留给运维来擦屁股。(这可以说是计算机科学和软件工程间的一个差异)。

这并不是说理论上抽象的系统模型是毫无价值的,恰恰相反。它们对于将实际系统的复杂性降低到一个我们可以推理的可处理的错误是非常有帮助的,以便我们能够理解这个问题,并试图系统地解决这个问题。我们可以证明算法是正确的,通过显示它们的属性总是保持在某个系统模型中。证明算法正确并不意味着它在真实系统上的实现必然总是正确的。但这迈出了很好的第一步,因为理论分析可以发现算法中的问题,这种问题可能会在现实系统中长期潜伏,直到你的假设(例如,时间)因为不寻常的情况被打破。理论分析与经验测试同样重要。

故障模型

系统故障是指硬件或者软件的错误状态,一般引进故障的原因是这些:部件的失效、环境的物理干扰、操作错误或不正确的设计。按照时间的长短,故障可以分为:永久性、间歇性、瞬时性。

故障的级别有:逻辑级故障、数据结构级故障、软件故障和差错故障、系统级故障。

硬件故障

硬件故障(hardware faults)譬如等等硬盘崩溃、内存出错、机房断电、有人拔错网线,任何大型的数据中心都可能出现这种错误。据报道称,硬盘的 平均无故障时间(MTTF mean time to failure)约为 10 到 50 年,因此从数学期望上讲,在拥有 10000 个磁盘的存储集群上,平均每天会有 1 个磁盘出故障。

为了减少系统的故障率,第一反应通常都是增加单个硬件的冗余度,例如:磁盘可以组建 RAID,服务器可能有双路电源和热插拔 CPU,数据中心可能有电池和柴油发电机作为后备电源,某个组件挂掉时冗余组件可以立刻接管。这种方法虽然不能完全防止由硬件问题导致的系统失效,但它简单易懂,通常也足以让机器不间断运行很多年。

我们通常认为硬件故障是随机的、相互独立的:一台机器的磁盘失效并不意味着另一台机器的磁盘也会失效。大量硬件组件不可能同时发生故障,除非它们存在比较弱的相关性(同样的原因导致关联性错误,例如服务器机架的温度)。随着数据量和应用计算需求的增加,越来越多的应用开始大量使用机器,这会相应地增加硬件故障率。此外在一些云平台(如亚马逊网络服务(AWS, Amazon Web Services))中,虚拟机实例不可用却没有任何警告也是很常见的,因为云平台的设计就是优先考虑灵活性(flexibility)和弹性(elasticity),而不是单机可靠性。

如果在硬件冗余的基础上进一步引入软件容错机制,那么系统在容忍整个(单台)机器故障的道路上就更进一步了。这样的系统也有运维上的便利,例如:如果需要重启机器(例如应用操作系统安全补丁),单服务器系统就需要计划停机。而允许机器失效的系统则可以一次修复一个节点,无需整个系统停机。

软件错误

另一类错误是内部的系统性错误(systematic error)。这类错误难以预料,而且因为是跨节点相关的,所以比起不相关的硬件故障往往可能造成更多的系统失效。例子包括:

  • 接受特定的错误输入,便导致所有应用服务器实例崩溃的 BUG。例如 2012 年 6 月 30 日的闰秒,由于 Linux 内核中的一个错误,许多应用同时挂掉了。
  • 失控进程会占用一些共享资源,包括 CPU 时间、内存、磁盘空间或网络带宽。
  • 系统依赖的服务变慢,没有响应,或者开始返回错误的响应。
  • 级联故障,一个组件中的小故障触发另一个组件中的故障,进而触发更多的故障。

虽然软件中的系统性故障无法避免,但我们还是有很多小办法,例如:仔细考虑系统中的假设和交互;彻底的测试;进程隔离;允许进程崩溃并重启;测量、监控并分析生产环境中的系统行为。如果系统能够提供一些保证(例如在一个消息队列中,进入与发出的消息数量相等),那么系统就可以在运行时不断自检,并在出现差异(discrepancy)时报警。

人为错误

设计并构建了软件系统的工程师是人类,维持系统运行的运维也是人类,人非圣贤,孰能无过。一项关于大型互联网服务的研究发现,运维配置错误是导致服务中断的首要原因,而硬件故障(服务器或网络)仅导致了 10-25%的服务中断。

  • 以最小化犯错机会的方式设计系统。例如,精心设计的抽象、API 和管理后台使做对事情更容易,搞砸事情更困难。但如果接口限制太多,人们就会忽略它们的好处而想办法绕开。很难正确把握这种微妙的平衡。
  • 将人们最容易犯错的地方与可能导致失效的地方解耦(decouple)。特别是提供一个功能齐全的非生产环境沙箱(sandbox),使人们可以在不影响真实用户的情况下,使用真实数据安全地探索和实验。
  • 在各个层次进行彻底的测试,从单元测试、全系统集成测试到手动测试。自动化测试易于理解,已经被广泛使用,特别适合用来覆盖正常情况中少见的边缘场景(corner case)。
  • 允许从人为错误中简单快速地恢复,以最大限度地减少失效情况带来的影响例如,快速回滚配置变更,分批发布新代码(以便任何意外错误只影响一小部分用户),并提供数据重算工具(以备旧的计算出错)。
  • 配置详细和明确的监控,比如性能指标和错误率在其他工程学科中这指的是遥测(telemetry)(一旦火箭离开了地面,遥测技术对于跟踪发生的事情和理解失败是至关重要的。)监控可以向我们发出预警信号,并允许我们检查是否有任何地方违反了假设和约束。当出现问题时,指标数据对于问题诊断是非常宝贵的。
  • 良好的管理实践与充分的培训。

可靠性模型

与故障模型想对应的,就是系统的可靠性模型。常用的有以下三种:时间模型、故障植入模型和数据模型。

可靠性指标

可靠性指标,主要有以下几个:

平均无故障时间(MTTF-Mean Time To Failure)

它表示一个系统平均情况下,正常运行的时间。

与它相关的指标是“失效率”U,关系:U = 1 / MTTF。

平均故障修复时间(MTTR-Mean Time To Fix/Repire)

平均每次修复所需要的时间

平均故障间隔时间(MTBF-Mean Time Between Failure)

一看就知道,MTBF = MTTF + MTTR。

在实际情况下,一般 MTTR 都会比较小,所以我们近似地认为 MTBF = MTTF。

MTTF 是用来说明一个软件系统能够正常运行的时间的指标。它越大,说明该系统越可靠。计算方法很简单,

可靠性计算

一个系统的可靠性计算往往不能直接得出。这是因为计算机系统是一个复杂的系统,影响其可靠性的因素也非常复杂。所以我们需要为其建立适当的数据模型,把大系统划分为若干子系统,然后再根据一定原则进行组合计算。

这种计算方法,可以简化分析的过程。

对于系统的划分,我们可以把它分为:串联系统、并联系统、模冗余系统、混联系统。(其中模冗余系统是 M 个并联的子系统中,需要有 N 个以上的子系统能正常工作,整个系统才能正常工作。这种系统,常在并联后加上一个表决器。)

计算这些系统可靠性时,我们需要计算出每个子系统的失效率,然后根据概率的加法原则(串联系统)和乘法原则(并联系统)进行综合运算,最后得出整个系统的可靠性。

上一页
下一页