58.6 架构设计流程与三原则

一句话

架构设计不是 “画一张最好的图”,而是 四步流程(识别复杂度 → 3 个备选 → 360 度环评 → 详细设计)套上 三条原则(合适 > 演化 > 简单):流程防止你漏,原则防止你过度设计;两者缺一个,产出要么片面,要么昂贵。

先问:为什么要做架构设计

四个常见动机,三个是错的:

动机 问题 后果
“架构重要,所以要做” 小系统不做架构反而上线更快 花两个月设计一套 1 万用户用不上的东西
“照搬业界最佳实践” 大厂方案的规模前提与你无关 水土不服,最后推倒重来
“流程要求,必须有架构文档” 形式化产出,没人读 白耗人力
“为了三高(高并发/高可用/高性能)” 目标错位,每个功能都要为“可能的高并发”让路 加一个功能要改一个月

正确的目的只有一句:架构设计是为了解决软件系统复杂度带来的问题——没有复杂度,就不需要架构。

一个算例:学生信息管理系统,1–2 万用户、日均访问不到 1 次、宕机 2 小时没人投诉。

决策 该不该做 理由
缓存 不做 日均访问 < 1 次,缓存命中率没有意义
负载均衡 不做 宕机 2 小时可接受,冗余的收益小于运维成本
存储高可靠 做 数据丢了就是事故,主备 + 跨机房同步是底线

🔑 架构设计的第一个动作不是画图,而是给复杂度排序:哪个是真问题,哪个是你想象的问题。

三原则:合适 > 演化 > 简单

原则 含义 违反它的样子 量化判据
合适优于业界领先 方案匹配业务规模,不追最新最强 亿级平台一上手 40 多个子系统、开发近 1 年,号称 5 万 TPS,实际不到 500 目标规模 = 当前 ×2~4;一年内别超过 10 倍
演化优于一步到位 按阶段演进,每阶段可交付 一步到位照搬大厂架构,落地遥遥无期 每阶段 ≤ 6 个月能上线
简单优于复杂 组件越少越好,能用 3 个别用 5 个 把商品、搜索、订单、支付塞进一个千万行组件,clone 一次 30 分钟,改一行整站崩 组件故障率 10% 时:3 个组件可用性 72.9%,5 个 59%

优先级是 合适 > 演化 > 简单,不是并列:先保证方向对(合适),再保证走得动(演化),最后才追求简洁(简单)。反过来先追求 “简洁”,会砍掉必要的冗余;先追求 “先进”,会一步到位地死掉。

两个真实案例说明 “演化” 不是保守,而是按阶段换引擎:

阶段 淘宝(2003–2005) 手机 QQ
起步 2003-04-07 立项、2003-05-10 上线,只有 1 个月 → 买 一个轻量简单的 单用户在线约 2 KB,2 GB 内存只撑 100 万在线
第一次撞墙 2003 年底 MySQL 撑不住 → 换 Oracle + NAS + RAC(先买 “方案”,再买 “性能”) 亿级在线时:昵称加长要 2 个月、好友上限 500→1000 要 3 个月
结构性换引擎 PHP 连接池 SQL Relay 死锁必须重启 → 整体换 Java(热切换);2005 年商品 1663 万、PV 8931 万、会员 1390 万后 “买” 彻底不够,才去 IOE 自研 4.0 重写

用 0.9 的三次方算一遍:单组件可用性 90% 时,串联 3 个是 72.9%,5 个掉到 59%——每加一个组件就多一个故障点,这就是 “简单” 不是审美而是算术。

四步流程

第 1 步:识别复杂度——多数系统只有 1 个

复杂度个数 含义
1 个(多数) 正常:只抓主要矛盾,其余按常规做法
2 个(少数) 需要权衡(例如 “高性能 + 低成本”)
3 个以上 报警:要么系统原先设计有问题,要么架构师判断失误

把业务量翻译成技术指标,是这一步的核心动作(1000 万条/天、10 个子系统读取):

写:1000 万 / 86400        ≈ 115 次/秒
读:1 亿 / 86400           ≈ 1150 次/秒
峰值 ×3:TPS 345、QPS 3450
再留 4 倍:TPS 1380、QPS 13800

阈值:目标可以是当前的 2 倍,也可以是 8 倍(低基数乘 4、高基数乘 2),但不要按 10 倍以上设计,更不要一上来按 100 倍——那 40 个子系统、5 万 TPS 却只有 500 实际承载力的项目,就是这么来的。

第 2 步:设计备选方案——3 个,不是 1 个也不是 10 个

三个典型错误:

错误 表现 后果
只设计 “最优秀” 的方案 直接奔着 5 万 TPS 去 实际 500,推倒重来
只做一个方案 评审时因为一个缺点就被全盘否决 没有退路
备选做得过细 会上争 “健康检查 1 分钟还是 30 秒” 浪费两小时,架构差异没讨论

数量:3–5 个最佳,少于 3 个容易思维狭隘,多于 5 个差别不明显;实践中 3 个最顺手(保守 / 激进 / 折中)。

差异判据——什么才算 “两个不同方案”:

差异 例子 属于
架构差异 主备 vs 集群;ZooKeeper vs Keepalived 放进备选方案
细节差异 健康检查 1 分钟 vs 5 分钟;线程池 200 vs 300 留给详细设计

第 3 步:360 度环评——先过硬门槛,再按优先级

七个维度:性能、可用性、硬件成本、项目投入、复杂度、安全性、可扩展性。

两种常见的错打分:

错法 为什么错
数量对比(优点多者胜) 等于把七个维度等权,性能和成本一样重
加权打分(10/5/3 分) 权重没有客观依据,谁定权重谁赢

正解:先定这次最不能牺牲的 1–2 个维度当硬门槛,用它淘汰;通过门槛的方案再按优先级(例如 “可扩展优先、成本其次”)比较。下面这段能跑:

alts = {
    "A 直接用 MySQL 单库": dict(性能=2, 可用性=2, 成本=5, 投入=5, 复杂度=5, 安全=3, 可扩展=1),
    "B MySQL 主从 + 缓存":  dict(性能=4, 可用性=3, 成本=4, 投入=3, 复杂度=3, 安全=3, 可扩展=3),
    "C 分库分表 + 单元化":  dict(性能=5, 可用性=5, 成本=2, 投入=1, 复杂度=1, 安全=3, 可扩展=5),
}
must = {"性能": 4, "可用性": 3}      # 本次的硬门槛

print("方法一:数优点(等权)")
best = max(alts.items(), key=lambda kv: sum(kv[1].values()))
print(f"  -> 选 {best[0]}(总分 {sum(best[1].values())})")

print("方法二:先过硬门槛,再按优先级比")
cand = [(k, v) for k, v in alts.items() if all(v[d] >= th for d, th in must.items())]
for k, v in alts.items():
    ok = all(v[d] >= th for d, th in must.items())
    print(f"  {k:<22} {'通过' if ok else '淘汰'}")
cand.sort(key=lambda kv: (-kv[1]["可扩展"], kv[1]["成本"]))
print(f"  -> 选 {cand[0][0]}")

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

方法一:数优点(等权)
  -> 选 A 直接用 MySQL 单库(总分 23)
方法二:先过硬门槛,再按优先级比
  A 直接用 MySQL 单库         淘汰
  B MySQL 主从 + 缓存        通过
  C 分库分表 + 单元化         通过
  -> 选 C 分库分表 + 单元化

方法一选中的 A,性能与可用性根本不达标——它靠 “便宜、简单、好实施” 拿了总分第一。这就是 “数优点” 最危险的地方:它会把致命短板平均掉。

第 4 步:详细设计——落到能开发

到这一步才讨论参数与选型细节。以 Nginx 负载均衡策略为例:

策略 用在什么情况
轮询(默认,down 剔除) 后端同构、无状态
加权轮询 机器规格不同(32 核配权重 2、16 核配权重 1)
ip_hash 没做 session 共享,靠 IP 粘住
fair 后端响应时间差异大(按响应时间分配)
url_hash 缓存命中率优先(同一 URL 落到同一台)

极端情况:详细设计时才发现方案不可行,多半是漏了一个关键质量属性。典型案例:某项目整体排期 1 年,详细设计时才发现 “根本做不完”,只能废弃重排——漏的就是 “开发周期” 这个质量属性(把它列为必评项的做法见 4.6 决策框架与决策记录)。

三条规避:

规避 做法
架构师要懂关键细节 别把 “细节我们后面再讨论” 挂嘴边,否则不可行性会在最后一周暴露
分阶段、分系统 大系统拆成 2–3 个可独立交付的阶段,每阶段都可回退
把 “开发周期” 当质量属性 排期算不进质量属性的方案,不是方案

反模式

反模式 后果
没问题也做架构设计 小系统被设计成大系统,上线推迟数月
照搬大厂架构 规模不匹配,最后推倒重来
一上来按 100 倍规模设计 40 个子系统、实际承载力只有标称的 1/100
只做一个备选方案 评审因一个缺点全盘否决,没有退路
备选方案做到 1 分钟的细节 会上争论细节,架构差异没人看
用 “数优点” 或 “加权打分” 选方案 致命短板被平均掉,选中不达标的方案
同时解决 3 个以上复杂度 架构过度复杂,团队两年都在做减法
详细设计留到最后才发现不可行 项目整体重排,前面投入全部沉没

三条铁律

🔑 先算规模,再谈架构:当前规模 × 2~4 是合理目标;超过 10 倍就是在为想象中的流量付费。

🔑 备选方案 3 个:一个方案是赌博,两个是陪衬,三个才逼出真正的权衡。

🔑 评估先过硬门槛:把 “不能牺牲的 1–2 个维度” 当门槛,剩下的再比——数优点会把致命短板平均掉。

自检

Q1 学生系统(1 万用户、日均访问 < 1 次)要不要做缓存和负载均衡?为什么?

▸ 答案:都不做。日均访问不到 1 次,缓存命中率没有意义;宕机 2 小时没人投诉,冗余的收益小于运维成本。但存储高可靠要做(主备 + 跨机房同步),因为数据丢失是事故,和访问量无关。判据是 “复杂度在哪”,不是 “大厂都这么做”。

Q2 三原则的优先级为什么是 “合适 > 演化 > 简单”,而不是 “简单优先”?

▸ 答案:先错方向,越简单越糟(一个简单的错误架构上线快、死得也快);先保证方向合适,再保证每阶段能交付(演化),最后才在可行方案里追求最少组件(简单)。把 “简单” 放第一位,常见后果是砍掉必要冗余,故障时没有任何缓冲。

Q3 单组件可用性 90%,为什么 5 个组件的系统只有 59%?

▸ 答案:串联可用性是乘法:0.9^3 = 72.9%、0.9^5 = 59%。每加一个串联组件就多一个故障点,且是乘性衰减。这也是 “简单优于复杂” 的算术依据,而不是审美偏好——减少一个非必要组件,等于直接抬高整体可用性。

Q4 为什么 “多数系统只有 1 个主要复杂度”?出现 3 个以上说明什么?

▸ 答案:因为复杂度通常由业务形态决定,一个系统往往只在一个维度上真正吃紧(要么并发高、要么数据量大、要么一致性要求高)。同时要解决 3 个以上,往往说明系统原先的设计有问题(把不该耦合的东西耦合了),或者架构师判断失误(把想象的问题当成真问题)。

Q5 备选方案为什么是 3 个最佳?少于 3 个、多于 5 个分别会怎样?

▸ 答案:少于 3 个容易陷入 “锤子钉子”(熟悉的方案就是唯一方案),且没有折中选项;多于 5 个时,后面的方案与前面的差异已经不明显,讨论成本上升但信息量不增。3 个通常对应 “保守 / 激进 / 折中”,刚好覆盖权衡空间。

Q6 用 “数优点” 选方案,为什么会选中一个性能不达标的方案?

▸ 答案:因为等权求和会把致命短板平均掉。本机演示里方案 A(单库)在成本、投入、复杂度三项都拿 5 分,总分 23 排第一,但它的性能 2 分、可用性 2 分根本不达标。“便宜、简单、好实施” 这些优点与 “能不能扛住流量” 不是同一量级的问题,不能相加。

Q7 “详细设计阶段才发现方案不可行” 通常漏了什么?怎么提前发现?

▸ 答案:通常漏了一个质量属性,最常见的是 “开发周期/人力” 和 “运维复杂度”。提前发现的办法:在 360 环评阶段就把 “项目投入” 当成门槛维度之一(而不是只算硬件成本),并在详细设计前用 2–3 天做一次可行性走查(关键接口能不能实现、数据能不能迁、排期够不够)。

关联