CPT304 SoftwareEngineeringII 软件工程 2 Pt.6 批判性分析 / 关键性分析(Critical Analysis)
文章目录
- 1. 批判性分析(Critical Analysis)介绍
- 1.1 什么是批判性分析 / 关键性分析
- 1.2 为什么批判性分析很重要
- 1.3 批判性思维和批判性分析的区别
- 2. 批判性分析的技术与工具
- 2.1 FMEA(Failure Modes and Effects Analysis,失效模式与影响分析)
- 2.1.1 步骤
- 2.1.1.1 列出系统的组成部分或功能
- 2.1.1.2 评估严重程度、发生概率、检测难度(Assess Severity, Occurrence, Detection)
- 2.1.1.2.1 RPN(Risk Priority Number,风险优先级数)
- 2.1.1.2.2 收集严重程度 S 的数据
- 2.1.1.2.3 收集发生概率 O 的数据
- 2.1.1.2.4 收集发生概率 D 的数据
- 2.1.2 示例 1
- 2.1.3 示例 2
- 2.1.4 练习 1
- 2.1.5 Quantitative FMEA(定量 FMEA)
- 2.1.6 Mitigation Prioritization(缓解措施优先级排序)
- 2.1.7 练习 2
- 2.2 RCA(Root Cause Analysis,根本原因分析)
- 2.2.1 五个为什么
- 2.2.2 示例 1
- 2.2.3 示例 2
- 2.2.4 进阶 RCA
- 2.2.4.1 故障树分析(Fault Tree Analysis,FTA)
- 2.2.4.1.1 Fault Tree Analysis(FTA,故障树分析) 的作用
- 2.2.4.1.2 FTA(Fault Tree Analysis,故障树分析)具体怎么工作
- 2.2.4.1.3 识别并量化原因(Identifying and Quantifying the Causes)
- 2.2.4.1.4 计算故障发生概率(Calculate Failure Probabilities)
- 2.2.4.1.5 练习 1
- 2.2.4.1.6 概率数据的“动态”性质(Dynamic Nature)
- 2.2.4.1.7 概率数据的“静态”性质(Static Nature)
- 2.2.4.1.8 概率数据的混合性质(Mixed Nature)
- 2.2.4.1.9 示例3
- 2.2.4.1.10 练习 2
- 2.3 把 RCA 和 FMEA 连接起来
1. 批判性分析(Critical Analysis)介绍
1.1 什么是批判性分析 / 关键性分析
Critical Analysis 是一个系统性的评估过程,用来评估软件产物的:
- strengths(优点 / 强项)
- weaknesses(缺点 / 弱点)
- validity(有效性 / 合理性)
Critical Analysis 在软件工程中的目的是:
- 尽早发现缺陷(Identify defects early)。
例如在需求阶段就发现问题,避免后期开发完才返工。 - 提高系统可靠性(Improve system reliability)。
也就是让软件更稳定、更少出错。 - 优化开发流程(Optimize development processes)。
例如发现某些开发步骤效率低、容易出错,然后进行改进。
1.2 为什么批判性分析很重要
大约 60% 的软件缺陷来源于需求阶段。
在软件上线后再修复 bug,成本可能是在设计阶段修复的 100 倍。
现实世界中的影响例子有:
Therac-25 事故(1985–1987):软件缺陷导致了致命的辐射过量事故。
1.3 批判性思维和批判性分析的区别
批判性思维 是一种比较通用的思考能力,用来解决问题和进行逻辑推理。
批判性分析 是针对某个具体对象进行有重点、有结构的评价。
它们有重叠的地方,都需要:
- Logical reasoning(逻辑推理)
- Skepticism(怀疑精神 / 不盲目相信)
二者的区别是:
批判性分析会使用某个专业领域里的工具和方法。
2. 批判性分析的技术与工具
- 失效模式与影响分析(Failure Modes and Effects Analysis,FMEA)
找出系统可能会在哪里出错,以及这些错误会造成什么影响(Identifies potential failures and their impact)。 - 根本原因分析(Root Cause Analysis,RCA)
把问题一步步追溯到最根本的原因(Traces problems back to their origin)。 - SWOT 分析(SWOT Analysis)
从四个方面(优势、劣势、机会、威胁)分析一个系统、项目或方案(Evaluates strengths, weaknesses, opportunities, threats)。 - 代码审查(Code review)
系统地检查源代码(Systematic examination of source code)。 - 需求验证(Requirements Validation)
确保需求是清楚的、完整的、可以测试的(Ensures requirements are clear, complete, and testable)。
这些分析方法不只是“最佳做法”,而且经常是法律或行业标准要求必须做的。
例如:医疗软件 需要遵循 ISO 14971 标准。
汽车软件 需要遵循 ISO 26262 标准。
2.1 FMEA(Failure Modes and Effects Analysis,失效模式与影响分析)
FMEA 是主动预防型的,它是一种计划工具,用来在麻烦发生之前就阻止它。
FMEA 会列出系统中所有可能的故障,然后对它们进行评分,最后决定哪些问题要优先修复。
这里的评估标准有:
| 缩写 | 英文 | 中文 | 含义 |
|---|---|---|---|
| S | Severity | 严重程度 | 问题发生后有多严重 |
| O | Occurrence | 发生概率 | 这个问题有多可能发生 |
| D | Detection | 检测难度 | 这个问题在发生前有多容易被发现 |
一般来说:
S 越高,说明后果越严重;
O 越高,说明越容易发生;
D 越高,说明越难提前发现。
然后通常会计算一个风险优先级:
RPN = S × O × D
RPN 越高,说明这个问题越应该优先处理。
假设我们正在开发一个 网上商店系统。我们提前预测到:
支付系统可能会失败,比如信用卡无法处理付款。然后我们按照 S、O、D 标准评估该问题的严重程度、发生概率和检测难度,并计算 RPN。根据 RPN 的高低,我们判断该失效模式的优先级。如果 RPN 较高,就需要优先添加相应的预防措施、缓解措施或备用方案,例如备用支付方式、失败重试、异常监控和防止重复扣款机制。
2.1.1 步骤
- 列出系统的组成部分或功能(List components/functions of the system)。
- 识别潜在的失效模式,也就是找出每个部分可能会怎么出错(Identify potential failure modes)。
- 评估三个指标,并计算风险优先级数 RPN(Assess severity, occurrence, detection (Risk Priority Number: RPN))。
| 缩写 | 英文 | 中文 | 含义 |
|---|---|---|---|
| S | Severity | 严重程度 | 这个故障发生后有多严重 |
| O | Occurrence | 发生概率 | 这个故障有多可能发生 |
| D | Detection | 检测难度 | 这个故障有多难被提前发现 |
2.1.1.1 列出系统的组成部分或功能
我们应该把系统中的哪些功能纳入 FMEA 分析?
我们可以根据评估风险 SOD 去回答这个问题。
SOD 在这一步上那就是:
- 如果这个功能失败,后果很严重(Failures have high impact,Severity, S)。
- 这个功能比较容易出错,发生概率较高(Failures are likely,Occurrence, O)。
- 这个功能出错以后不容易被提前发现(Failures are hard to catch,Detection, D)。
所以在做 FMEA 时,应该优先选择那些“出错后影响严重、发生概率较高、又不容易被发现”的系统功能进行分析。、
具体的步骤如下:
- 理解系统的目的和范围(Understand the System’s Purpose and Scope)
- 评估关键性和影响(Assess Criticality and Impact)
- 评估使用频率和暴露程度(Evaluate Usage and Exposure)
- 检查复杂性和变更频率(Check Complexity and Change Frequency)
- 评估检测难度(Assess Detection Challenges)
- 利用历史故障记录(Leverage Historical Failures)
- 考虑外部接口(Consider External Interfaces)
2.1.1.2 评估严重程度、发生概率、检测难度(Assess Severity, Occurrence, Detection)
这里我们再看一下SOD分别是什么意思:
- Severity,S(严重程度):
如果这个故障发生了,它对系统、用户或业务的影响有多严重?评分一般是 1(minor) 到 10(catastrophic)。 - Occurrence,O(发生概率):
这个故障发生得有多频繁?评分也是 1(rare) 到 10(frequent)。 - Detection,D(检测难度):
在问题影响用户之前,我们有多大可能提前发现它?评分也是 1(always detected) 到 10(undetectable)。
2.1.1.2.1 RPN(Risk Priority Number,风险优先级数)
RPN = Severity × Occurrence × Detection
(风险优先级数 = 严重程度 × 发生概率 × 检测难度)
也就是:RPN = S × O × D
RPN 高的问题需要立即关注。
但是,批判性分析要求:任何 Severity 为 9 或 10 的项目,不管总 RPN 是多少,都必须被处理,因为它对人的生命或整个系统完整性的风险太高,不能忽视。
2.1.1.2.2 收集严重程度 S 的数据
也就是说,不能随便给 S 打分,可以以下面的几种方法来收集数据。
用户影响分析(User Impact Analysis):
数据来源:
- User stories(用户故事)
- Requirements docs(需求文档)
- Customer support tickets(客户支持工单 / 客服反馈记录)
方法:检查这个故障会怎样影响用户。
利益相关者意见(Stakeholder Input):
数据来源:
- Product managers(产品经理)
- Developers(开发人员)
- Business analysts(业务分析师)
方法:可以开一个讨论会,或者做问卷调查,让相关人员根据业务目标给影响程度打 1 到 10 分。
行业标准(Industry Standards):
数据来源:我们可以参考行业里的 FMEA 指南或软件风险管理标准。
例如:
AIAG:汽车行业常用的 FMEA 指南;
IEC 60812:关于 FMEA 方法的国际标准;
ISO 27001:信息安全管理标准,常用于软件安全风险分析。
方法:使用预先定义好的严重程度评分表(predefined severity tables)。
历史数据(Historical Data):
数据来源:可以参考过去系统中真实发生过的问题记录,比如:
- Incident reports(事故报告)
- Post-mortems(事后复盘报告)
- Bug trackers(缺陷跟踪系统),比如 Jira
方法:分析过去故障造成过什么影响。
2.1.1.2.3 收集发生概率 O 的数据
在 FMEA 里评估一个故障“有多容易发生”时,不能完全凭感觉,要参考多方面。
日志分析(Log Analysis):
数据来源:
- System logs(系统日志)
- Error tracking(错误追踪工具),比如 Sentry、New Relic
方法:统计一段时间内故障发生的次数。
测试数据(Testing Data):
数据来源:
- Unit test results(单元测试结果)
- Integration test results(集成测试结果)
- Load test results(负载测试结果 / 压力测试结果)
方法:运行测试来模拟不同条件下系统是否会出错。
开发人员估计(Developer Estimates):
数据来源:
- Software engineers(软件工程师)
- Architects(系统架构师)
方法:根据代码复杂度和过去的 bug 记录,讨论并估计故障发生概率。
使用模式 / 使用规律(Usage Patterns):
数据来源:数据分析工具(Analytics),例如:Google Analytics、server metrics
通过数据分析工具或服务器指标来观察用户怎么使用系统。
方法:把故障和用户活动情况联系起来分析。
基准数据 / 行业参考数据(Benchmarks):
数据来源:行业数据或者供应商服务等级协议(vender SLA),例如 AWS uptime stats
方法:使用典型故障率来估计问题发生概率,例如:网络中断平均每小时 0.01 次。
2.1.1.2.4 收集发生概率 D 的数据
我们可以从多个方面判断一个故障是否容易被发现。
测试覆盖率(Test Coverage):
数据来源:Test reports,例如 JaCoCo、pytest 这些测试工具生成的报告。
方法:测量有多少比例的代码被测试到了。
监控系统(Monitoring Systems):
数据来源:Logs, alerts,例如 Prometheus、Datadog。
方法:检查故障发生时,系统是否会触发报警。
质量保证流程(QA Processes):
数据来源:Development lifecycle,例如代码审查、静态分析。
意思是是说看开发流程中有没有质量检查环节。
方法:评估这些控制措施是否足够强。
用户报告 / 用户反馈(User Reports):
数据来源:
- Support tickets(客服工单)
- Bug reports(Bug 报告 / 缺陷报告)
方法:如果是用户先发现问题,而不是系统提前发现,那么 Detection 分数就高。
设计审查(Design Reviews):
数据来源:
- Architecture docs(架构文档)
- Peer reviews(同行评审 / 同事评审)
可以通过检查系统架构文档、让其他工程师评审设计,判断系统有没有内置的检查机制。
方法:评估系统中是否有内置检查机制。
2.1.2 示例 1
我们用几个例子来说明 FMEA。
示例1是游戏 App。
需求层面的 FMEA:
功能 / 需求:游戏要能在所有手机上运行。
失效模式:游戏在旧手机上卡顿。
RPN 评分:
严重程度(S)= 8,如果游戏很卡,玩家可能会直接退出游戏。
发生概率(O)= 4,因为市场上还有不少用户在用旧手机,所以这个问题有一定可能发生。但不是所有用户都会遇到,所以不是特别高。
检测难度(D)= 3,因为这个问题比较容易测试。开发团队可以拿一些旧手机测试游戏运行情况,很容易发现卡不卡。
所以,RPN = 8 × 4 × 3 = 96
解决方案:优化图形。
2.1.3 示例 2
这里分析一个银行 APP。
从系统设计角度分析 FMEA:
功能:把钱转到另一个账户。
失效模式:如果服务器宕机,转账可能失败。
RPN 评分:
严重程度(S)= 10,因为银行转账涉及用户的钱。
如果转账失败导致钱丢失、扣款异常、账户金额错误,这就是非常严重的问题。
发生概率(O)= 2,服务器宕机不是每天都会发生,所以发生概率较低。
检测难度(D)= 5,比如服务器突然宕机,可能只有在转账请求发送时才发现。
如果没有良好的监控、重试机制和状态检查,就可能比较难及时发现和处理。
所以,RPN = 10 × 2 × 5 = 100
解决方案:添加离线队列(offline queue)。当服务器暂时不可用时,系统不要直接让转账请求丢失,而是先把请求安全地排队保存起来,等服务器恢复后再继续处理。
2.1.4 练习 1
我们尝试通过一个练习更加全方位的使用 FMEA。
任务:对一个在线投票系统进行 FMEA 分析。
需要分析的功能:
- 用户身份验证
- 投票提交
- 结果统计
我们的步骤还是:
- 识别 3 个失效模式。或者说是找出 3 个可能出错的情况。
- 给每个故障打 S、O、D 分数。
- 计算RPN。
结果如下:
- 用户认证 / 身份验证的失效模式是密码泄露。
评分是:
Severity = 10:非常严重,因为会影响投票合法性和用户安全;
Occurrence = 3:发生概率不是特别高;
Detection = 4:有一定可能发现,但不一定很容易;
RPN = 10 × 3 × 4 = 120。 - 投票提交的失效模式是重复投票。
评分是:
Severity = 8:比较严重,因为会影响投票公平性;
Occurrence = 2:发生概率较低;
Detection = 5:检测难度中等;
RPN = 8 × 2 × 5 = 80。 - 结果统计 / 计票的失效模式是计票错误。
评分是:
Severity = 10:非常严重,因为会直接影响最终投票结果;
Occurrence = 1:发生概率很低;
Detection = 3:相对容易检测;
RPN = 10 × 1 × 3 = 30。
2.1.5 Quantitative FMEA(定量 FMEA)
传统 FMEA 里,S、O、D 通常是人工打分,范围是 1 到 10。这种打法有一定主观性,因为分数主要靠经验判断。
进阶方法:进阶 FMEA 会尽量用真实数据来代替主观打分。
严重程度(S)可以用具体指标来衡量,比如:系统停机时间百分比、收入损失。
发生概率(O)可以用历史数据或 MTTF 来判断,比如:MTTF(Mean Time To Failure,平均无故障时间)。
检测能力(D)可以用测试覆盖率或检测概率来表示。比如:测试覆盖率 80%,这就相当于原来传统 FMEA 的 2 分。
因此传统 FMEA 里:D 越高 = 越难发现 = 风险越大。
现在在 D 表示检测概率(detection probability),因此D% 越高 = 越容易发现 = 风险越低。
所以现在 RPN 的计算公式为 RPN = S × O × (1 - D%)(RPN = 严重程度 × 发生概率 ×(1 - 检测概率))
2.1.6 Mitigation Prioritization(缓解措施优先级排序)
做完 FMEA 后,我们不仅要知道哪些风险高,还要决定先解决哪个问题最划算、最值得投入资源。
我们使用成本—收益模型(Cost-Benefit Model)解决这个问题。
成本—收益模型(Cost-Benefit Model)用两个方面来判断:
- Cost = Development effort (hours) + Tooling cost
也就是说修复成本包括花费的时间以及所花费的成本。
比如修复一个数据库崩溃问题,需要工程师工作 50 小时,成本大约是 5000 美元。 - Benefit = Risk reduction (ΔRPN × Impact)
意思是说收益来自于风险降低了多少。
比如修复这个数据库崩溃问题,可以将 RPN 从 200 降低到 50,并减少 10K 美元损失,那么这里的收益就是 (200 - 50)× 10,000 = 1,500,000 美元,所以就是投资回报率(ROI,Return on Investment)高。
2.1.7 练习 2
我们通过一个练习尝试使用定量 RPN 的 FMEA。
练习要求我们分析一个云存储 API。
失效模式:高负载情况下上传失败。
MTTF(Mean Time To Failure,平均无故障时间)= 1000 h
Downtime = = $5K/h,意思是系统停机或上传功能不可用时,每小时损失 5000 美元。
Test Coverage = 90%。
因此指标计算如下:
严重程度(S):$5K/hour × 1-hour outage = $5K impact → 8,因为上传失败停机 1 小时,每小时损失 5000 美元,这个损失比较高,但还没有达到灾难级别。
发生概率(O):MTTF = 1000 hours → Failure rate = 1/1000 = 0.001/hour → 2,MTTF = 1000 h,所以 1 / 1000 = 0.001 次/小时,这个发生概率比较低,所以给 2。
检测能力(D):90% coverage → D% = 0.9 → (1 - 0.9) = 0.1 → 1,这个计算用具体的公式参考,较为简单。
我们现在计算 RPN = 8 × 2 × 1 = 16。
缓解措施:增加自动扩容机制,用来应对流量高峰。
2.2 RCA(Root Cause Analysis,根本原因分析)
RCA 是反应型的(reactive),它像侦探工具一样,用来在问题发生之后找出原因并修复问题。
例子:网上商店上线后,支付系统崩溃了。我们开始问“为什么?”最后发现原因是服务器无法承受 1000 个用户同时访问,因为之前没有做负载测试。
2.2.1 五个为什么
对于 RCA,我们的常用方法是 五个为什么(5 Whys)。
遇到问题后,不只停留在表面原因,而是连续追问“为什么”,直到找到根本原因。
不一定真的必须问满 5 次,重点是要一直追问到真正原因。
例如我们前面问题是:系统在高峰负载时崩溃。
然后开始追问:为什么系统崩溃?(Why? → Server overload)
因为服务器过载了。
继续追问:为什么服务器会过载?(Why? → Insufficient scaling.)
因为扩展能力不够。
接着追问:为什么扩展能力不够的问题之前没发现?(Why? → No load testing.)
因为没有做负载测试。
2.2.2 示例 1
我们再看几个示例。
第一个例子是有关一个太空射击游戏崩溃的问题。
我们尝试用五个为什么去进行 RCA。
为什么?因为敌人生成得太快。
为什么敌人生成太快?因为循环没有限制。
根本原因:缺少敌人生成速率上限。
解决方法:添加限制。
2.2.3 示例 2
我们再看一个银行 APP 的例子。
问题:转账重复。
为什么?因为重新点击转账按钮重新发送了请求。
为什么重试会导致重复转账?因为系统没有检查唯一 ID。
根本原因:缺少交易 ID。也就是说,系统没有给每笔转账设置唯一的交易编号,所以无法判断两次请求是不是同一笔交易。
解决方法:添加 ID。这样即使用户点击重试,系统也能识别:这是同一笔转账请求,不应该重复执行。
2.2.4 进阶 RCA
基础 RCA 通过 5 Whys 方法挖掘问题的根本原因。
进阶 RCA 通过 故障树分析(Fault Tree Analysis,FTA)。
FTA (Fault Tree Analysis,故障树分析)用逻辑树和概率来分析故障原因。
FTA 会把一个最终故障放在最上面,比如:系统崩溃
然后往下分解可能原因:
- 服务器过载;
- 数据库故障;
- 网络中断;
- 代码死循环;
- 第三方服务失败。
每个原因还可以继续往下分解,并且可以给它们加上发生概率。
这样就能判断哪个原因最可能导致系统故障。
FTA 还可以用贝叶斯推断(Bayesian Inference)来分析。
贝叶斯推断(Bayesian Inference)用统计方法不断修正(refine)我们对原因的判断(guesses)。
2.2.4.1 故障树分析(Fault Tree Analysis,FTA)
故障树分析(Fault Tree Analysis,FTA)是进阶 RCA 里的一种方法,用来系统地分析某个故障是由哪些原因造成的。
FTA 是一种自上而下的(top-down)、演绎式(deductive)的分析方法,用来识别和分析某个具体系统故障的原因。
自上而下(top-down)是因为先从最上面的故障开始,然后一步步往下找原因。
演绎式(deductive)是从一个已知的问题出发,推导出可能导致它的原因。
顶事件放在故障树最上面,下面的分支就是导致这个故障的各种可能原因、子原因和条件。
因此 FTA 是可视化的、逻辑化的、系统化的(visual, logical, and systematic)。
2.2.4.1.1 Fault Tree Analysis(FTA,故障树分析) 的作用
FTA 的目的如下:
- 识别系统薄弱点(Identifying Weak Points):通过可视化地展示单个组件的故障如何在系统中传播,FTA 可以帮助工程师找到“单点故障”。
- 定量可靠性评估(Quantitative Reliability Assessment):FTA 可以帮助计算顶事件发生的概率。
- 确定安全与资源投入的优先级(Prioritizing Safety and Resources):FTA 可以帮助我们判断资源应该投到哪里。
- 诊断和故障排查(Diagnostic Troubleshooting):当故障真的发生以后,可以用故障树来检查各个组件的状态,从而定位问题。
总结一下就是 FTA 的作用是帮助我们找出系统薄弱点,计算故障发生概率,决定资源优先投入在哪里,并在故障发生后帮助排查原因。
2.2.4.1.2 FTA(Fault Tree Analysis,故障树分析)具体怎么工作
FTA 的具体步骤如下:
- 定义顶事件(Define the Top Event)
- 识别原因(Identify Causes)
- 使用逻辑门(Use Logic Gates)
- 继续向下拆分原因(Break Down Further)
- 量化(Quantify)
- 分析(Analyze)
前面提到的逻辑门包括两种:
| 逻辑门 | 意思 |
|---|---|
| OR gate | 只要其中一个原因发生,顶事件就可能发生 |
| AND gate | 必须多个原因同时发生,顶事件才会发生 |
事件也可以进行划分:
- 顶事件(Top event):故障树最上面的事件,也就是我们最终要分析的主要故障。
- 中间事件(Intermediate event):中间事件是顶事件下面的原因,但它本身还可以继续被分解。
- 基本事件(Basic event):基本事件是最底层、不能再继续分解的具体原因。
- 未展开事件(Undeveloped event):这是一个暂时不继续分析的事件。
下图给出了一个例子。
2.2.4.1.3 识别并量化原因(Identifying and Quantifying the Causes)
在做故障树分析时,我们要找出哪些组件故障、系统问题或操作问题可能导致 Top Event(顶事件)。
设计工程师 / 系统工程师可以从系统结构角度找原因,运维和维护团队可以从真实运行情况、日志和故障现场中提供证据,帮助我们判断哪些原因可能导致顶事件。
设计工程师 / 系统工程师(Design / Systems Engineers):他们负责识别硬件或软件组件之间是如何交互的,并判断哪些组件故障可能导致顶事件。
运维和维护团队(Operations & Maintenance Teams):他们是问题发生后最先响应的人。运维团队可以告诉我们系统实际运行时是什么样的(ground truth,真实情况),而不只是设计文档里写的样子。
到这里我们成功识别了原因(Identifying the causes)。
我们现在开始量化原因(Quantifying the causes)。
可靠性工程师(Reliability Engineers):可靠性工程师会使用历史数据、制造商规格说明,以及 MTBF 来给基本事件分配具体数值。
安全 / 风险管理部门(Safety / Risk Management,HSE):在高风险行业,比如石油天然气或航空业,这些部门负责确保量化后的风险符合监管标准。
数据科学家(Data Scientists)在现代软件环境中,数据分析人员可以通过日志、遥测数据和大量用户会话中的错误率来量化原因。
2.2.4.1.4 计算故障发生概率(Calculate Failure Probabilities)
我们现在通过一个例子来看一下怎么计算故障发生概率(Calculate Failure Probabilities)。
我们先提供几个底层原因的发生概率:
| 事件 | 中文意思 | 概率/故障率 |
|---|---|---|
| Server offline | 服务器离线 | 0.01/hour |
| Auth failed | 认证失败 | 0.02/hour |
| Too many transactions | 交易太多 | 0.05/hour |
| No lock timeout | 没有锁超时机制 | 0.1 |
| No validation | 没有输入验证 | 0.03 |
那么现在支付网关失败的概率是多少呢?
支付网关失败的可能有两种:服务器离线 或 认证失败。
因此这里是一个 OR gate(或门)。
因此如果 A 和 B 是两个独立事件:
A = Server offline
B = Auth failed
那么:p(A OR B) = 1 - p(A 不发生 AND B 不发生)
也就是:p(A OR B) = 1 - (1 - p(A)) × (1 - p(B))
代入数字:p(Server offline) = 0.01
p(Auth failed) = 0.02
p(Payment Gateway failed)
= 1 - [(1 - 0.01) × (1 - 0.02)]
= 1 - 0.99 × 0.98
= 1 - 0.9702
= 0.0298/hour
那么我们现在要计算数据库锁死的概率怎么计算呢?
数据库锁死 = 交易太多 AND 没有锁超时机制
这里是 AND gate(与门),表示两个条件要同时发生,才会导致数据库锁死。
所以计算方法是:
p(DB locks) = p(Too many transactions) × p(No lock timeout)
代入数字:
p(Too many transactions) = 0.05/hour
p(No lock timeout) = 0.1
p(DB locks) = 0.05 × 0.1 = 0.005/hour
计算输入崩溃的概率:
输入崩溃 = 没有输入验证 OR 某个未展开事件
但是这个 undeveloped event(未展开事件) 没有给具体概率。
因此:p(Input crash) = 0.03
计算结账失败的概率:
它由三个中间事件导致,就是我们刚刚计算的三个:支付网关失败、数据库锁死、输入崩溃.
这三个之间是 OR gate(或门)。
只要其中任何一个发生,都可能导致结账失败。
所以公式是:
p(Checkout fails)
= 1 - (1 - 0.0298) × (1 - 0.005) × (1 - 0.03)
= 1 - 0.9702 × 0.995 × 0.97
= 1 - 0.9364
= 0.0636/hour
这里或许有人会有问题,为什么计算 OR gate(或门)的时候是 1 减去两者都不发生的概率,而不是直接将这里 事件 A 和 事件 B 的概率相加。
因为对于两个独立事件,有四种情况,事件 A 和 事件 B 都发生;事件 A 发生 但事件 B 不发生; 事件 B 发售 但事件 A 不发生; 事件 A 和 B 都不发生。
OR gate(或门)是只要有一个发生,那么上层事件就会发生,所以这里包含前三种情况。
那么如果我们直接将 事件 A 和 事件 B 的概率相加,这里 事件 A 发生的概率包含事件 A 和 事件 B 都发生;事件 A 发生 但事件 B 不发生两种情况的概率。
事件 B 发生的概率包含事件 A 和 事件 B 都发生;事件 B 发生 但事件 A 不发生两种情况的概率。
所以这里两种情况都发生的概率算了两次,所以不能在这个例子中直接 0.01 + 0.02。
计算“至少一个事件发生”的概率的步骤是:
- 先算两个事件都不发生的概率。
- 用 1 减去这个结果。
也就是上面计算的过程。
2.2.4.1.5 练习 1
我们现在尝试一个场景:用户无法登录银行 App。
现在我们建立一个 fault tree(故障树),然后计算登录失败的概率。
顶事件:Login fails(登录失败)
它可能由两个原因导致:DB down(数据库宕机)、Auth error(认证错误)。
只要数据库宕机,或者认证错误,用户就可能登录失败。
这是 OR gate(或门)。
数据库宕机可能由两个原因导致:
Power outage(停电),概率是 0.01/hour;
DB crash(数据库崩溃),概率是 0.02/hour。
这里也是 OR gate(或门)。
认证错误需要两个条件同时发生:
Bad credentials(用户凭证错误),比如用户名或密码错误,概率 0.04/hour;
No retry(没有重试机制),概率 0.5。
这里是 AND gate(与门)。
我们先计算 DB down(数据库宕机)的概率:
p(DB down) = 1 - (1 - 0.01) × (1 - 0.02)
= 1 - 0.99 × 0.98
= 1 - 0.9702
= 0.0298/hour
再计算Auth error(认证错误)的概率:
p(Auth error) = 0.04 × 0.5
= 0.02/hour
最后计算登录失败的概率:
p(Login fails)
= 1 - (1 - 0.0298) × (1 - 0.02)
= 1 - 0.9702 × 0.98
= 1 - 0.950796
= 0.049204/hour
2.2.4.1.6 概率数据的“动态”性质(Dynamic Nature)
故障树里有些事件的概率是 随时间发生的故障率,比如“每小时发生多少次”;有些事件则是 没有时间单位的条件概率或状态概率。
所以前者这些是 动态事件,会随着时间发生,所以用 每小时 来表示故障率。后者这些事件是 静态条件,不是按小时发生的。
Dynamic(动态)指的是:
这些事件会随着时间随机发生,不是一直固定存在的状态。
如果一个概率写成 “每小时多少”,比如 0.01/hour,它表示的是故障率(failure rates)。故障率(failure rates)指的是某个故障在一段时间内发生得有多频繁。
故障率被用于:
- 动态事件:那些随机发生或间歇发生的事件。
- 基于时间分析的系统:当我们分析系统在一段时间内的行为时,就会用这种动态概率。
在故障树分析中,故障率(failure rate)通常和 MTTF(Mean Time To Failure,平均无故障时间)有关系。
例如:网关服务器离线的故障率是:0.01/hour
那它的 MTTF 就是:MTTF = 1 / 故障率 = 1 / 0.01 = 100 hours
在软件系统中,每小时故障率 通常来自三类数据。
- Logs(日志):可以通过历史日志来统计故障率。
比如:服务器在 500 小时内崩溃了 5 次。
那么故障率就是:5 / 500 = 0.01/hour。 - Metrics(监控指标):可以通过监控系统的数据计算故障或异常发生率。
比如:在 1000 小时内发生了 50 次流量高峰。
那么发生率就是:50 / 1000 = 0.05/hour
也就是说,平均每小时发生 0.05 次流量高峰。 - Vendor Specs(供应商规格说明):也可以参考供应商提供的可靠性数据,比如服务器、云服务、数据库服务的可用性说明。
例如:服务商承诺 99.9% uptime(系统正常运行时间比例)。
所以可以近似理解为:downtime rate = 0.001
2.2.4.1.7 概率数据的“静态”性质(Static Nature)
概率的静态性质(Static Nature)指的是:这个问题不是随着时间随机发生的,而是系统设计或配置中本来就存在的状态。
没有时间单位的概率是静态概率。它们不是故障率,而是一次性的、没有单位的概率。
适用于:
- Constant Conditions(固定条件 / 常态条件):有些问题不是运行过程中突然发生的,而是系统本身就存在的缺陷,比如:
Design flaws(设计缺陷);
Configuration errors(配置错误);
Missing features(缺少某个功能)。 - State-Based Events(状态型事件):静态概率表示某个状态在某一时刻是否成立的可能性,而不是它发生得有多频繁。
比如:No lock timeout = 0.1
意思不是“没有锁超时每小时发生 0.1 次”,而是:
在当前系统设计中,存在“没有设置锁超时”这个问题的可能性是 0.1。
No validation = 0.03
意思是“没有输入验证”这个问题存在的概率是 3%。
这些静态概率 通常来自三个方面:
- Code Reviews(代码审查):通过代码审查发现大约 3% 的代码提交缺少输入验证。
- Design Audits(设计审计 / 设计检查):通过设计检查或配置审计发现大约 10% 的配置缺少超时设置。
- Assumption(假设 / 粗略估计):如果没有足够的数据,就只能根据经验做一个粗略估计。
2.2.4.1.8 概率数据的混合性质(Mixed Nature)
我们前面计算的时候使用的是:
p(Checkout fails) = 1 - (1 - 0.0298) × (1 - 0.005) × (1 - 0.03)
这里默认了所有概率是 同一种类型、可以直接比较和组合的概率。
如果我们细看这里 0.0298 和 0.005 是基于时间的概率 / 故障率。而 0.03 是静态概率。
2.2.4.1.9 示例3
我们现在用一个 视频流媒体 App 卡住/冻结 的例子来说明 动态概率 和 静态概率 混合出现时该怎么理解。
这里的顶事件是视频播放冻结 / 卡住。
下面的原因可能是网络断开、视频编解码器过时。
网络断开是一个基于时间的动态故障率。
过时的视频编解码器是一个非时间型概率。
我们有两种方式解决这里的混合问题。
- 把静态概率转换成时间型概率。
如果有 10% 的系统实例使用了过时的 codec,并且假设在高负载下,这个过时 codec 有一定概率导致播放冻结。
我们作出假设:每小时有 10% 的概率因为 codec 解码失败而导致视频卡住。
那么现在两个原因都变成时间型概率:
Network disconnected = 0.02/hour
Outdated codec = 0.1/hour
顶事件是视频播放卡住。
它们之间是 OR gate。
所以公式是:
p(Video playback freeze)
= 1 - [(1 - 0.02) × (1 - 0.1)]
= 1 - (0.98 × 0.9)
= 1 - 0.882
= 0.118/hour
我们可以用 MTTF 表示,那就是 MTTF = 1 / 故障率 = 1 / 0.118 ≈ 8.47 小时。 - 把静态概率当作条件概率来处理。
不要强行把静态概率改成 /hour 的故障率,而是把它看成一个前提条件。
前一种方法把“10% 的概率使用过时 codec”理解成:每小时有 10% 的概率因为 codec 过时导致视频冻结。
这样做其实有点不严谨。
0.1 表示系统存在过时 codec 的概率是 10%。
只有当它和某个触发事件同时出现时,才会导致播放冻结。比如:系统有过时 codec;用户刚好播放了不兼容的视频流。
因此这里不要把静态概率强行改成每小时概率,而是把它看成一个条件。这个条件需要和某个触发事件同时发生,才会导致故障。
我们把它拆成两个条件:Incompatible Stream = 0.05/hour 和 Outdated Codec = 0.1。
所以计算这里的 p(Codec fails) = 0.5 × 0.2 = 0.1/hour
Video Playback Freeze 的概率:
p(Video playback freeze)
= 1 - (1 - 0.02) × (1 - 0.1)
= 1 - 0.98 × 0.9
= 1 - 0.9702
= 0.0298/hour
约等于:0.03/hour
那么 MTTF = 1 / 0.03 = 33.3 hours
所以包括我们前面举例的 Checkout Failure(结账失败)的故障树计算。
我们计算的是:p(Checkout fails) = 1 - (1 - 0.0298) × (1 - 0.005) × (1 - 0.03)
= 0.0636/hour
因为 malicious attack(恶意攻击) 是一个 undeveloped event(未展开事件),为了简化计算,暂时不考虑它。
我们现在把 No validation(没有输入验证) 这个静态概率,当作 条件概率 来处理。
更严谨的计算应该是:
p(Input crash) = p(No validation) × p(Bad input occurs)
如果假设:
p(No validation) = 0.03
p(Bad input occurs) = x/hour
那么:
p(Input crash) = 0.03 × x
然后新的结账失败概率是:
p(Checkout fails)
= 1 - (1 - 0.0298) × (1 - 0.005) × [1 - (0.03 × x)]
2.2.4.1.10 练习 2
我们现在分析一个实时聊天 App,最上层故障事件是:用户意外断开连接。
原因有:
- 网络掉线:0.03/hour
- 服务器崩溃:由两个因素组成,服务器过载,每小时概率 0.04;没有重试机制,概率 0.2。这里是 AND gate。
- 客户端 Bug:有两个因素组成,客户端代码 bug,每小时概率 0.02;用户没有更新客户端,概率 0.1。这里是 OR gate。
因此我们计算概率的话,如果使用方法一那么计算如下:
p(Server Crash) =0.04 × 0.2 = 0.008/hour
p(Client Bug)=1−(1−0.02)(1−0.1)
=1−(0.98)(0.9)
=1−0.882
=0.118/hour
那么p(User Disconnects)=1−(1−0.03)(1−0.008)(1−0.118)
=1−(0.97)(0.992)(0.882)
=1−0.84869568
=0.15130432/hour
也就是:用户意外断连概率约为 0.1513/hour,即每小时约 15.13%。
MTTF≈6.61 hours,平均大约每 6.61 小时 会发生一次用户意外断连。
2.3 把 RCA 和 FMEA 连接起来
把 RCA 和 FMEA 连接起来,就是把一次“事后得到的教训”,转化成“以后提前预防的措施”。
RCA 是 reactive:问题发生之后,分析为什么出问题;
FMEA 是 proactive:问题发生之前,提前预测风险并预防。
RCA 可以发现原来不知道的风险。
比如系统上线后才发现一个问题:
数据库发生死锁,系统崩溃了。
这个问题可能之前 FMEA 没有预测到。
RCA 的输出是一个具体的、真实发生过的原因。
把 RCA 的发现更新到 FMEA 文档里。
RCA 做完之后,不是写个报告就结束,而是要把发现的问题加入 FMEA 表格。
这样以后做系统设计、测试和上线检查时,就会提前考虑这个风险。
简单总结一下那就是:RCA 是事后找原因,FMEA 是事前防风险。把 RCA 和 FMEA 连接起来,就是把真实事故中发现的根本原因,补充进 FMEA 表格,让以后类似问题可以被提前识别和预防。
