软件可靠性是指软件系统在规定条件下和规定时间内完成规定功能的能力。这个定义来自国家标准与国际标准对软件质量模型的统一描述,它把可靠性的内涵拆解为规定条件、规定时间和规定功能三个关键要素。缺失任何一个要素,可靠性的讨论都将失去基准。
规定条件包含软件运行所依赖的全部环境因素,既有硬件平台和操作系统这样的基础设施,也包括输入数据的分布特征、用户操作的行为模式以及系统负载的强度范围。同一个软件在高负载条件下和在低负载条件下表现出来的可靠性可能截然不同。理解规定条件有助于讨论可靠性指标时不脱离实际部署环境。
规定时间是可靠性度量中最容易被忽视的维度。软件不会像硬件那样随使用时间增长而自然磨损,但这并不意味着软件可靠性与时间无关。软件故障的发生往往与系统运行状态的特殊组合有关,这种组合的出现概率随时间累积。一个长期运行的软件系统遇上异常组合的概率远大于一个刚刚启动的系统,所以讨论可靠性必须明确时间尺度。
规定功能是可靠性定义中最核心的部分。软件系统通常需要完成多种功能,不同功能的失效对系统整体可用性的影响差异巨大。对于关键任务系统,即使是一个低频功能的失效也可能带来灾难性后果。可靠性分析的第一步就是明确功能需求清单,然后逐项评估每种功能在特定条件下的失效概率和失效后果。
度量软件可靠性的四个基础指标分别是平均无故障时间、平均修复时间、平均失效间隔和可用性。平均无故障时间衡量从系统启动到第一次失效的平均时长,平均修复时间衡量从失效发生到系统恢复的平均时长,平均失效间隔则综合考虑了修复之后的下一次失效周期。可用性由前两者决定,等于平均无故障时间除以前者与修复时间之和。这个公式虽然简单,却包含了可靠性工程的两个核心方向:延长无故障运行时间和缩短故障恢复时间。
可靠性与可用性常常被混为一谈,但两者着眼点有本质区别。可靠性关注的是系统不出故障的概率,可用性关注的是系统处于可服务状态的时间比例。一个每周必须停机维护一小时但其余时间极其稳定的系统,其可用性可能很高而可靠性并不突出。软件架构师在设计阶段就需要明确这两个目标的平衡关系,这个平衡将直接影响后续容错策略的选择和冗余资源的配置。
容错设计的核心理念可以概括为:系统在设计阶段就预见到故障不可避免,并为此内置应对机制,使系统在部分组件失效时仍然能够维持整体功能的正常运行。容错不等于零故障,而是承认故障的客观存在,通过技术手段将故障影响控制在可接受范围内。
容错设计建立在故障、错误和失效三个基本概念的递进关系之上。故障是系统中存在的物理缺陷或设计缺陷,是导致错误的潜在原因。错误是故障在系统运行状态中的表现,是系统内部出现了不正确状态。失效则是系统对外提供的服务偏离了规定功能,是错误传播到系统边界后的最终结果。不同阶段的应对策略完全不同:故障阶段采取缺陷预防和排除,错误阶段采取错误检测和恢复,失效阶段只能依靠失效保护降低损失。
故障检测是容错系统运行的第一道防线。只有及时发现了故障的存在,后续的容错措施才有可能被触发。最基础的故障检测手段包括复制检查、时序检查、编码检查和合理性检查四种类型。
复制检查是将同一计算任务提交给两个或多个独立计算单元执行,然后比较执行结果。如果结果一致则认为计算正确,如果不一致则至少有一个单元出现了故障。这种方法的优点是检测覆盖面广,几乎所有类型的计算错误都能被捕获,但代价是需要额外的硬件或软件冗余。
时序检查用于检测实时系统中与时间约束相关的故障。系统预先定义每个关键任务的截止时间和最小到达间隔,运行时通过看门狗定时器或超时计数器持续监控。如果某个任务在规定时间内没有完成,就判定发生了超时故障。在航空航天和工业控制领域,时序检查是保障安全运行的最后屏障。
编码检查利用信息冗余来检测数据在存储或传输过程中发生的意外变化。最常用的编码包括奇偶校验码、循环冗余校验码和海明码。奇偶校验只能检测单比特错误,循环冗余校验可以检测多比特和突发错误,海明码则不仅能检测还能定位并纠正单比特错误。不同编码方式在检错能力、纠错能力和冗余开销之间各有取舍。
合理性检查是最轻量级的检测方式,它根据系统状态变量的先验知识设置合理的取值范围或变化趋势约束。一旦监测值超出合理范围就判定出现了异常。合理性检查实现简单,但有效性依赖对系统行为的准确建模,约束过松导致漏检,约束过严则产生误报。
故障屏蔽是容错设计的最高目标:使系统在存在故障的情况下,对外仍然表现出无故障的行为。实现故障屏蔽的主要手段是冗余技术,根据冗余资源类型可分为硬件冗余、软件冗余、信息冗余和时间冗余四大类。
硬件冗余是最直观的冗余形式,通过部署额外物理组件提供备份能力。最简单的方案是双机热备,两台相同计算机同时运行相同软件,主用机处理业务,备用机实时接收状态同步数据。当主用机出现故障时备用机在极短时间内接管业务,对外表现为短暂服务中断而非永久功能丧失。
软件冗余通过编写多个功能等价但实现方式不同的软件版本来规避共因故障。共因故障是指因同一个根本原因导致多个冗余组件同时失效。如果两个冗余模块使用完全相同代码,代码中隐藏的一个边界条件缺陷就会同时击穿两个模块,使冗余完全失效。多版本程序设计要求不同团队使用不同算法和编程语言独立开发功能等价的版本,运行时通过表决机制决定最终输出。
信息冗余是在数据层面增加校验信息来检测和纠正错误。一个重要应用场景是分布式存储系统的纠删码技术,它允许在部分存储节点失效时仍能完整恢复原始数据,方法是将原始数据分割成若干分片,计算出额外的校验分片分散存储在多个节点上。
时间冗余是检测到错误后通过重新执行同一计算任务来消除瞬时故障。很多故障是瞬时的而非永久的,比如宇宙射线造成的单粒子翻转或电磁干扰引起的信号畸变。当瞬时故障消失后重新执行计算往往就能得到正确结果。时间冗余实现成本最低,但只能应对瞬时故障,对永久性硬件损坏无能为力。
恢复块技术是最早提出的软件容错方案之一。系统将关键计算模块识别出来,为每个关键模块编写一个主版本和若干个备用版本,它们实现完全相同的功能接口但内部可以用不同的算法和数据结构。运行时首先执行主版本,执行完毕后对输出进行验收测试。验收测试是一个独立编写的检查程序,只关心结果是否满足预先定义的可接受条件。如果主版本通过验收测试则直接采纳结果。如果失败就回滚到模块执行前的状态,然后尝试备用版本,直到某个版本的输出通过验收测试或所有版本都尝试完毕。
恢复块技术的关键在于验收测试的设计。测试不能过于宽松,否则有缺陷的结果可能通过测试导致系统失效。测试也不能过于严格,否则实际上正确的但形式有别的结果也可能被判失败,造成不必要的回滚和重试。好的验收测试应当捕捉功能需求的本质约束,比如结果值是否在合理数值范围内、输出数据结构是否符合预期、计算时间是否在可接受时限之内。
多版本程序设计将多个版本同时投入运行,通过表决器对各版本输出进行仲裁。最简单的表决策略是多数表决,即超过半数的版本输出一致的结果时,该结果作为最终输出。多版本设计的关键挑战是保证各版本间的独立性。如果两个版本无意中使用相同的算法库或对问题域做了相同
本篇完!