软件分析与设计笔记 15:非功能需求:把「快、稳、安全」变成数字
为什么「系统要快」不是一条需求:非功能需求怎么量化,决定性能的用户量、交易量、数据量三张表怎么估,安全需求要写清什么,以及可靠性的「不可用」该怎么定义。
本文是 《软件分析与设计笔记》 系列的第 15 篇,共 28 篇
这是我在学习软件分析与设计时整理的系列笔记。概念部分是我按自己的理解写的,示例与图示都是自己编、自己画的。理解有偏差的地方欢迎指出。
贯穿案例:片区家电维修派单系统。前面几篇都在说「做什么」,这一篇补上「做得多好」。
0 「系统要快」害了多少项目
需求文档里写着「系统响应要快」。这句话最后会变成三种结局:
- 开发觉得已经够快,用户觉得慢 —— 因为没有标准;
- 上线后才发现要支持的并发量是估算的十倍 —— 因为没估过规模;
- 为了「快」把架构做得极复杂,结果大部分是白做的 —— 因为没有说不快会怎样。
非功能需求(non-functional requirement, NFR) 要解决的正是这个问题:把「快、稳、安全」翻译成可测量、可验收的数字。其中最常见的一类,就是性能需求(performance requirement)。
1 它和功能需求的区别
| 功能需求 | 非功能需求 | |
|---|---|---|
| 回答 | 系统做什么 | 系统做得多好 |
| 本例例子 | 工程师可以现场申请配件 | 申请提交在派单高峰时段 2 秒内完成 |
| 验收方式 | 按用例走一遍 | 按指标测量 |
| 出问题的时机 | 一眼能看出来 | 上线后高负载时才暴露 |
最后一行是关键:非功能缺陷往往要到真实压力下才暴露,而那时改动的代价最高。所以它必须在需求阶段就被量化。
2 性能:三个量决定一切
聊性能时最容易跑偏到「用什么技术」。其实在技术选型之前,要先回答三个规模问题 —— 也就是三张表。
贯穿案例的规模先摆出来(一个城市、6 个片区、40 名工程师的试点):
2.1 用户量表(user volume table)
有多少种使用者、各有多少人。注意区分「注册用户」和「同时在线用户(concurrent user)」——后者才是压力的来源。
| 使用者 | 总量 | 同时在线(峰值) |
|---|---|---|
| 客户 | 约 12 万(一年累计) | 约 300(早 9 点报修高峰) |
| 客服 | 8 | 8(全员在线) |
| 工程师 | 40 | 约 30(其余在路上) |
| 仓管 | 6 | 4 |
| 片区经理 | 6 | 3 |
注意这张表最有价值的地方:「同时在线」这一列和「总量」差了两三个数量级。如果按 12 万客户来设计并发,架构会贵得离谱;如果按 300 来设计,才是对的。
2.2 业务交易量表(business transaction volume table)
每种操作一天发生多少次。这张表的价值在于暴露各操作之间的数量级差异。
| 操作 | 日均 | 峰值(单小时) | 倍数 |
|---|---|---|---|
| 客户查工单进度 | 3 600 | 900 | 6× |
| 客户提交报修 | 400 | 90 | 5× |
| 客服看排期 | 800 | 160 | 4× |
| 客服派单 | 400 | 90 | 5× |
| 工程师回填工单 | 400 | 120 | 7× |
| 库存查询(含外部接口) | 500 | 150 | 7× |
这里有个关键观察:「查进度」的量是「派单」的 9 倍。如果两者用同一套设计(比如都走同一条重查询链路),查询会把派单拖慢。而派单是业务核心 —— 这就是为什么值得为查询单独做缓存或读库分离。
2.3 数据量表(data volume table)
每种数据一年增长多少、要保留多久。它决定的是存储、索引和归档方案,而不是接口响应。
| 数据 | 年增量 | 保留要求 | 说明 |
|---|---|---|---|
| 工单 | 10 万条 | 5 年(法规) | 5 年后约 50 万条,可归档 |
| 配件消耗 | 30 万条 | 5 年 | 随工单归档 |
| 客户档案 | 12 万条 | 长期 | 含手机号,属敏感数据 |
| 工单状态流水 | 200 万条 | 1 年 | 用于分析,1 年后可汇总 |
2.4 增长率的启示
这三张表的另一个作用是引出增长率:现在的量和三年后的量可能差好几倍。只按今天的规模设计,等于提前锁定一次重构 —— 而让系统在规模增长时仍保持表现,正是可扩展性(scalability)要解决的问题。
本例的增长率很明确:王姐说要推到另外 5 个片区,也就是三年内规模可能放大 6 倍。这一句就决定了:
- 数据表要一开始就按分片区设计(否则后期要迁移);
- 「单城市 300 并发」这个数字不能写死进架构假设。
3 峰值与均值
估这三张表时,必须同时给出两个数:均值和峰值(peak load)。
峰值和均值的差距常常是十倍以上:本例的报修高峰在早上 9 点到 10 点(客户上班前发现家里坏了),此时的量是日均的 5–7 倍。如果只报均值,容量规划一定会出问题。
吞吐量(throughput)同理:系统每秒能处理多少请求,要按峰值来定,而不是按日均。
峰值还需要注意一件事:峰值不一定同时发生。本例里「客户查进度」的峰值在早上,「工程师回填工单」的峰值在下午 5–6 点(收工前集中填),两者错开。这意味着同一套服务器资源可以在一天里被两拨不同的流量复用。
4 安全需求(security requirement)
4.1 至少要写清四件事
「要保证系统安全」不是需求。「要按角色控制数据可见范围」也不是 —— 还是不够具体。
安全需求至少要写清四件事:
- 认证(authentication)与授权(authorization) —— 谁能登录、登录后能看到和操作什么。要具体到角色与数据范围;
- 加密(encryption)与脱敏 —— 哪些字段属于敏感信息、传输和存储时怎么处理、在界面上怎么打码;
- 审计日志(audit log) —— 哪些操作必须留痕,日志保留多久,谁能查;
- 会话管理 —— 会话有效期、异常登录的处理方式。
本例填出来是这样:
| 项 | 本例的具体内容 |
|---|---|
| 认证与授权 | 客户用手机号 + 验证码;工程师与客服用工号 + 密码 + 短信二次验证;工程师只能看到派给自己的工单,客服只能看到本片区的 |
| 加密与脱敏 | 客户手机号在界面上显示为 138****5678;地址在工程师接单前只显示到街道;数据库中对手机号与地址做加密存储 |
| 审计日志 | 派单、改派、库存扣减、工单状态变更必须留痕(含操作人、时间、变更前后值),保留 3 年 |
| 会话管理 | 客服端 30 分钟无操作自动登出;工程师 App 记住登录 7 天;同一账号异地登录需短信确认 |
4.2 一个实用的追问
写这些内容时有个实用的问题可以问:「如果这个人做了这件事,我们事后能查出来吗?」 如果答案是「查不出来」,这就是一条欠缺的审计需求。
本例里这个追问很有效:
- 「客服能不能偷偷把一个好单派给自己熟悉的工程师?」→ 需要记「派单操作人」;
- 「仓管能不能私下扣减库存?」→ 需要记「库存变更前后值」;
- 「工程师能不能看别人片区的客户地址?」→ 需要授权控制,不只是记日志。
注意第三条不是日志能解决的 —— 日志只能事后追责,不能事前阻止。这也说明「审计」和「授权」是两件事,都要写。
5 可靠性需求(reliability requirement)
5.1 先定义「什么叫不可用」
可靠性最容易写得含糊,因为它依赖一个关键定义:什么叫「不可用」?
- 是完全打不开,还是慢到用户放弃?
- 是所有人都受影响,还是只影响某一个功能?
本例里的定义可以写得很有分寸:
- 客户查进度打不开 → 算不可用(这是最核心的体验);
- 客户查进度打开慢于 5 秒 → 也算不可用(用户会放弃并打电话,等于没做);
- 月度报表打不开 → 不算核心不可用(王姐可以等半小时);
- 工程师 App 打不开但网页版能用 → 算部分不可用。
最后两条尤其重要 —— 如果不先把「哪些不算不可用」写清,可用性这个指标就永远达不成。
5.2 四项指标
把这个定义写清楚之后,几项指标才有意义:
- 可用性(availability)目标 —— 例如 99.9% 意味着一年允许停机约 8.8 小时,这个数字对多数系统已经相当紧;这类对服务水平的正式约定通常写在服务级别协议(SLA)里;
- 故障恢复时间 —— 出问题后多久必须恢复;
- 备份与恢复 —— 备份频率,以及有没有真的演练过恢复;
- 降级(degradation)策略 —— 部分功能故障时,是整体停掉还是让核心功能继续跑。
本例的降级策略可以写得很具体:
| 故障 | 降级做法 |
|---|---|
| 供应商库存接口挂了 | 允许工程师先提交申请,标记「库存待确认」,不阻塞流程 |
| 看板服务挂了 | 客服端仍可派单,看板显示「数据延迟」提示 |
| 短信服务挂了 | 通知改走 App 内消息,并记录待补发 |
「降级」这一条常被忽略,但对用户体验影响很大:报表打不开和派不了单,严重程度完全不同。
注意第一条降级的巧妙之处:它把「外部依赖不可用」变成了「内部流程继续」。这是本例里最值得写的一条 NFR —— 因为供应商接口的可用性你控制不了。
6 一条合格的非功能需求长什么样
6.1 指标 + 条件 + 阈值
判断标准很简单:能不能直接拿去测?
| 不合格 | 合格(本例的版本) |
|---|---|
| 系统要快 | 早 9:00–10:00 报修高峰,客户查工单进度的响应时间(response time)95 分位不超过 2 秒 |
| 支持高并发 | 早高峰 300 客户同时在线时,派单操作成功率不低于 99.5% |
| 数据要安全 | 客户手机号在界面仅显示后四位,数据库中以加密形式存储 |
| 系统要稳定 | 月度可用性不低于 99.5%,故障恢复时间不超过 30 分钟 |
| 外部接口要可靠 | 供应商库存接口超时 3 秒即降级为「待确认」,不阻塞工程师提交 |
合格的写法包含三样东西:指标 + 条件 + 阈值。缺任何一样,都没法验证。
最后一行值得单独说:它把「外部依赖」写进了自己的需求。很多人只写自己的系统要多快,却忘了系统里有一半的延迟来自别人的接口。
6.2 容易忘掉的一类
顺带说一句,非功能需求里还有一类容易被忘掉的:可维护性(maintainability) 与 可用性(usability) 层面的要求 —— 例如「新成员能在一周内跑通本地环境」这类看似软性的目标。它们也是需求,只是更难测。
7 常见的四个错误
- 对所有人提同样的指标。 查进度可以放宽到 2 秒,派单不行 —— 应该按操作类型分别定;
- 只有均值没有峰值。 见上面第 3 节;
- 只写目标不写代价。 99.99% 的可用性比 99.9% 贵得多。需求里最好说明「这个目标值不值得」;
- 把技术方案当需求写。 「要用 Redis 缓存」是实现方案,不是需求。需求只说要多快、多少量。
本例里第一条的具体表现:如果把「查进度」和「派单」都定成「1 秒内」,那查进度那条就是浪费 —— 它一天 3600 次、每次只是读一条记录,2 秒完全够用;而为了让它 1 秒,可能要加一层缓存,多出来的复杂度换不到任何用户价值。
本节自测
- 为什么非功能缺陷往往到上线后才暴露?这对需求阶段意味着什么?
- 三张容量表里,哪一张决定了存储与归档方案?
- 为什么必须同时给出均值和峰值?本例的峰值出现在什么时候?
- 「系统要稳定」要补上什么,才能变成一条合格的需求?
- 为什么「用 Redis 缓存」不能写成需求?
- 本例中「客户查进度的量是派单的 9 倍」,这个观察对设计有什么影响?
- 为什么必须先写清「哪些情况不算不可用」?
- 本例把「供应商接口挂了」写成降级策略,为什么这条特别值得写?
参考答案
- 因为非功能缺陷只在真实压力下才暴露(低负载时一切正常)。所以它必须在需求阶段就量化,否则上线后改动代价最高(§1)。
- 数据量表。它决定存储、索引与归档方案,而不是接口响应(§2.3)。
- 因为峰值和均值常差十倍以上,只报均值容量规划一定出问题。本例峰值在早上 9–10 点(客户上班前发现家里坏了),是日均的 5–7 倍(§3)。
- 要补上指标 + 条件 + 阈值三样,例如「月度可用性不低于 99.5%,故障恢复时间不超过 30 分钟」(§6.1)。
- 因为它是实现方案,不是需求。需求只说「要多快、多少量」;一旦写成 Redis,就提前锁死了技术选择(§7)。
- 说明两者不该共用同一套设计 —— 查询量是派单的 9 倍,若走同一条重查询链路会把派单拖慢。值得为查询单独做缓存或读库分离(§2.2)。
- 因为如果不先写清哪些不算不可用,可用性指标永远达不成。本例:月报打不开不算核心不可用,工程师 App 挂了但网页版能用只算部分不可用(§5.1)。
- 因为供应商接口的可用性你控制不了。这条降级把「外部依赖不可用」转化成「内部流程继续」(允许先提交、标记待确认),是本例最值得写的一条 NFR(§5.2)。
术语表
| 术语 | 中文 | 一句话解释 |
|---|---|---|
| non-functional requirement(NFR) | 非功能需求 | 描述系统做得多好的需求 |
| performance requirement | 性能需求 | 对响应时间、吞吐量等的要求 |
| response time | 响应时间 | 从发出请求到得到响应的时间 |
| throughput | 吞吐量 | 单位时间内能处理的请求数量 |
| concurrent user | 同时在线用户数 | 同一时刻活跃的用户数量 |
| user volume table | 用户量表 | 按类型统计用户规模的表 |
| business transaction volume table | 业务交易量表 | 按交易类型统计发生次数的表 |
| data volume table | 数据量表 | 按数据对象统计增长量的表 |
| peak load | 峰值负载 | 最繁忙时段承受的负载 |
| scalability | 可扩展性 | 规模增长时系统仍能保持表现的能力 |
| security requirement | 安全需求 | 对认证、授权、加密、审计等的要求 |
| authentication | 认证 | 确认「你是谁」 |
| authorization | 授权 | 确认「你能做什么」 |
| encryption | 加密 | 把敏感数据转换成人读不懂的形式 |
| audit log | 审计日志 | 记录关键操作的日志,用于事后追溯 |
| reliability requirement | 可靠性需求 | 对可用性与故障恢复的要求 |
| availability | 可用性 | 系统处于可服务状态的时间比例 |
| SLA | 服务级别协议 | 对服务水平的正式约定 |
| degradation | 降级 | 部分能力失效时保留核心功能运行 |
| maintainability | 可维护性 | 系统被理解和修改的难易程度 |