35.10 FMEA(故障模式与影响分析)

一句话

FMEA 不是设计方法,而是设计完之后的一张复核表:逐个假设某个部件坏了,填 “哪个用户功能受影响、多严重、多大概率、现在有什么措施”,再用 风险程度 = 严重程度 × 故障概率 排序——它把 “我们的系统很稳” 从一句断言,变成一份可审计、可分配责任、可追踪关闭的清单。

为什么需要它

高可用比高性能难,难在异常场景的数量:性能只有 “快与慢”,可用性的失效路径是 “部件数 × 故障形态”。漏掉一条,就留一个隐患;而墨菲定律说,可能出错的事最终都会出错。

做法 覆盖范围 结果
凭经验想 “可能会挂哪” 想到的 3–5 个点 漏掉的点在上线后以事故形式补课
按监控告警反推 已发生过的问题 只能覆盖 “已经疼过” 的地方
FMEA 架构里每个部件 × 每种故障形态 在事故发生前把清单列全

🔑 FMEA 的价值不在 “算出一个分数”,而在强迫你把 “这个部件坏了会怎样” 逐格填满。填不满的格子,就是没想清楚的地方。

表格的 11 列

一列都不能省,每一列对应一个决策:

# 列 填什么 判据
1 功能点 用户视角的功能,如 “登录”“注册” 不是 模块视角(MySQL、Redis 不能当功能点)
2 故障模式 现象 + 量化,如 “MySQL 响应 5 秒” 不写 “响应慢”;不写原因
3 故障影响 量化到比例,如 “60% 用户登录缓慢” 不写 “大部分用户”;20% / 40% 级别就够
4 严重程度 致命 / 高 / 中 / 低 / 无 见下方三因子公式
5 故障原因 无索引 / 磁盘坏道 / 单点 / 单线 不同原因的概率、检测、处理都不同
6 故障概率 高 / 中 / 低 硬件随年限上升、新开源/新自研偏高
7 风险程度 严重 × 概率 用来排序,不是用来定生死
8 已有措施 检测告警 / 容错 / 自恢复 三档能力,逐条标注
9 规避措施 降低发生概率:冗余、换盘、限流 管理手段也算(如 “2 年强制换盘”)
10 解决措施 真正消除:加索引、加密、白名单 能解决优先解决,不能解决才规避
11 后续规划 谁、什么时候、做什么 没有 owner 和 deadline 的规划等于没写

严重程度:三个因子相乘

严重程度 = 功能点重要程度 × 故障影响范围 × 功能点受损程度
档 原文给的判定样例
致命 超过 70% 用户无法登录
高 超过 30% 用户无法登录
中 所有用户登录时间超过 5 秒;所有用户无法修改资料
低 10% 用户登录时间超过 5 秒;20% 用户无法修改头像
无 用户无感知

有争议时(“所有用户都无法改资料” 算高还是中),架构师裁定,不要在会上纠缠——这张表是用来排优先级的,不是用来追求精确的。

风险程度:致命项要单独设红线

风险程度 = 严重程度 × 故障概率,概率只分高/中/低三档,因为绝对概率算不出来、也不值得算。

同一个 “机房整体不可用”,换一种原因,风险完全不同:

故障原因 概率 风险判断
地震(5 级以上,该地区 20 世纪仅 1 次) 低 低
机房空调烧坏(约 2 年 1 次) 中 中
机架掉电(约 1 年 1 次) 高 高

对本机一算就会发现乘法的问题(真实输出见下):“100% 用户不可用” 但概率低,分数可能低于 “30% 用户变慢” 但概率中。我的判断是:乘法只用于排序,致命项必须额外设一条红线——凡 “影响 ≥ 70% 用户” 的故障模式,无论概率多低,都要有措施或书面接受理由。否则你会把 “单线接入” 排到 “缓存单点” 后面,最后先修了缓存。

一个完整样例:用户管理系统

系统只有三个部件:Server、Memcache、MySQL。功能点两个:登录、注册。

功能点 故障模式 故障影响 严重 故障原因 概率 已有措施 后续规划
登录 MySQL 主库宕机 100% 用户无法登录 致命 单点无备机 中 无 加备机 + 自动切换
登录 MySQL 响应 > 5 秒 60% 用户登录缓慢 高 缺索引/慢查询 中 慢查询告警 加索引 + 超时 1 秒
登录 机房出口单线中断 100% 用户不可用 致命 单线接入 低 无 双线接入
登录 MC 单机故障 30% 请求回源变慢 中 缓存单点 中 无 MC 改集群
注册 MySQL 断电 100% 用户无法注册 中 单实例 低 无 不处理(稍后可注册)

这张表最后的规划汇总成三条改进:MySQL 加备机、MC 从单机改集群、MySQL 双网卡——注意 “注册” 那条被明确判定为 “不处理”,因为新用户注册失败的影响远小于老用户登录失败。FMEA 允许 “接受风险”,但要求写下来。

代码:把表变成可排序的清单

人工排 20 行就会乱,写个十几行的脚本,让风险分数自己排:

SEV  = {"致命": 5, "高": 4, "中": 3, "低": 2, "无": 1}
PROB = {"高": 3, "中": 2, "低": 1}

rows = [
    ("登录", "MySQL 主库宕机",   "致命", "中", "无",         "加备机 + 自动切换"),
    ("登录", "MySQL 响应 > 5s",  "高",   "中", "慢查询告警",  "加索引 + 超时 1s"),
    ("登录", "机房出口单线中断",   "致命", "低", "无",         "双线接入"),
    ("登录", "MC 单机故障",      "中",   "中", "无",         "MC 改集群"),
    ("注册", "MySQL 断电",       "中",   "低", "无",         "不处理(稍后可注册)"),
]

scored = sorted(((SEV[s] * PROB[p], fn, mode, s, p, have, plan)
                 for fn, mode, s, p, have, plan in rows), reverse=True)

for risk, fn, mode, s, p, have, plan in scored:
    print(f"风险 {risk:>2} | {fn} | {mode} | 严重={s} 概率={p} | 已有={have} -> {plan}")

hi = [x for x in scored if x[0] >= 8]
fatal = [x for x in scored if x[3] == "致命"]
print(f"\n高风险(>=8): {len(hi)} 条; 致命项: {len(fatal)} 条 -> 致命项无论概率高低都单独设红线")

本机实测输出(Python 3.14 / macOS arm64):

风险 10 | 登录 | MySQL 主库宕机 | 严重=致命 概率=中 | 已有=无 -> 加备机 + 自动切换
风险  8 | 登录 | MySQL 响应 > 5s | 严重=高 概率=中 | 已有=慢查询告警 -> 加索引 + 超时 1s
风险  6 | 登录 | MC 单机故障 | 严重=中 概率=中 | 已有=无 -> MC 改集群
风险  5 | 登录 | 机房出口单线中断 | 严重=致命 概率=低 | 已有=无 -> 双线接入
风险  3 | 注册 | MySQL 断电 | 严重=中 概率=低 | 已有=无 -> 不处理(稍后可注册)

高风险(>=8): 2 条; 致命项: 2 条 -> 致命项无论概率高低都单独设红线

倒数第二行就是上面说的那个陷阱:“单线中断” 是全站不可用,却被排到了 “缓存单点” 之后。所以真实评审里要跑两遍:先按分数排,再把 “致命项” 单独拉出来核对。

落地:一周跑完一次 FMEA

指名:一张在线表格(飞书/Excel/Notion 均可)+ 四个角色(架构、运维/DBA、业务、测试),2 小时会议 + 1 天整理。不需要专门工具,需要的是 “列全” 和 “认领”。

可抄(表头直接复制):

功能点,故障模式,故障影响,严重程度,故障原因,故障概率,风险程度,已有措施,规避措施,解决措施,后续规划,owner,deadline

执行六步:

步 动作 产出
① 画图 把架构图画出来,标出所有单点 一张带单点标记的图
② 列功能点 用户视角 3–8 个(超过 8 个说明粒度太细) 功能点清单
③ 列部件 应用、DB、缓存、MQ、DNS、LB、第三方、机房 部件清单
④ 组合 每个部件 × 2–3 种故障形态(宕机/变慢/数据错) 故障模式行
⑤ 打分 严重 × 概率,填已有措施 排好序的表
⑥ 认领 高风险的写 owner + deadline;接受风险的写理由 可追踪的改进清单

可验:三个检查点,缺一条就是 “做了张表但没用”。

检查 合格线
每条高风险都有 owner 与 deadline 缺一条即不合格
每个单点都有 “措施” 或 “书面接受理由” 空白 = 没分析
功能点全部是用户视角 出现 “MySQL” 作为功能点即不合格

可退:FMEA 只适合分析可用性(原文明确:不适合分析高性能、成本、冗余优化),而且它是架构层的容错分析,不是代码层的 try/catch。系统很小(单机、单服务、无外部依赖)时,一张纸的清单就够,不必上表;系统变了(新增组件、换机房、上多活)要重跑,否则表会过期成 “历史文档”。

反模式

反模式 后果
把模块当功能点(“MySQL 故障 → 用户服务不可用”) 说不清业务影响,严重程度无法打分
故障模式写 “服务挂了”“响应慢” 不可量化,全靠感觉打分,评审变成吵架
只写故障不写原因 概率无从判断,措施也没有落点
全表都打 “高” 排序失效,等于没排
只做一次,架构变了不重跑 表格变成过期文档,反而给人虚假安全感
把 FMEA 当设计方法用 在设计阶段做,产出的是 “更多担心”,不是架构
没有 owner 与 deadline 改进项永远躺在文档里
用 FMEA 分析性能/成本 方法用错场景,结论不成立;性能问题见 34.2 性能建模 (利特尔法则、排队论)

三条铁律

🔑 功能点必须是用户视角:登录/注册/下单,不是 MySQL/Redis/Pod。

🔑 故障模式必须量化:“3 秒” 而不是 “慢”,“60% 用户” 而不是 “大部分”。

🔑 风险排序必须落到 “谁、什么时候、做什么”:没有认领的风险清单,只是一份心理安慰。

自检

Q1 为什么 “MySQL 响应慢” 不能作为功能点?功能点和故障模式分别该写什么?

▸ 答案:功能点必须从用户视角出发(登录、注册、下单),因为严重程度要按 “多少用户受影响” 判断;MySQL 是部件。正确的填法是:功能点 = “登录”,故障模式 = “MySQL 响应 5 秒”,影响 = “60% 用户登录缓慢”。

Q2 严重程度由哪三个因子决定?举一个 “低” 的例子。

▸ 答案:功能点重要程度 × 故障影响范围 × 受损程度。低档样例:10% 的用户登录时间超过 5 秒,或 20% 的用户无法修改头像——影响范围小 + 非核心功能。

Q3 为什么故障模式里不写故障原因?

▸ 答案:因为同一个现象对业务的影响是一样的,不管是磁盘坏道、慢查询还是 bug,用户看到的都是 “MySQL 5 秒才返回”。原因单独列,是因为它影响概率、检测手段、处理措施这三件事——同样现象、不同原因,措施完全不同(加索引 vs 升级版本)。

Q4 风险程度怎么算?为什么 “致命但概率低” 的项可能排不到第一?该怎么办?

▸ 答案:风险程度 = 严重程度 × 故障概率,概率只分三档。乘法会让 “致命 × 低概率” 的分数低于 “中等 × 中概率”(本机样例:5 分 vs 6 分)。我的判断是:乘法只用于排序,致命项(影响 ≥ 70% 用户)必须单独设红线,无论概率多低都要有措施或书面接受理由。

Q5 “规避措施” 和 “解决措施” 有什么区别?什么时候只能用规避?

▸ 答案:规避是降低发生概率(冗余、双线、定期换盘),解决是消除问题本身(加索引、数据加密、密码重试限制)。能解决优先解决;但像磁盘坏道、开源软件 bug、地震这类系统自己解决不了的问题,只能规避(异地备份、接受损失)。

Q6 如果一张 FMEA 表里 80% 的条目都是 “高风险”,问题出在哪?

▸ 答案:多半出在故障模式没量化(全是 “服务不可用”)或严重程度没有分级(都按致命算)。分级的意义是拉开差距;如果拉不开,说明填表的人没有真正评估影响范围,这张表也就失去了排序功能。

Q7 FMEA 能用来分析性能问题吗?为什么不能?

▸ 答案:不能。它的结构是 “部件故障 → 功能影响 → 严重度”,天然对应可用性(能不能用);性能问题是 “慢了多久、影响多少请求”,用的是另一套方法(34.1 性能指标 (延迟 / 吞吐 / P99)、34.2 性能建模 (利特尔法则、排队论))。原文明确:FMEA 只适合可用性分析,而且分析的是架构层,不是代码层的异常处理。

关联