系统设计原则

核心原则

经过三年的实战,我总结出这些系统设计原则。

1. 渐进式演进

原则

不要过度设计,从简单开始,逐步演进。

实践

渐进式演进不是一句口号,它要求每个设计决策都能回答「现在真的需要吗」。判断标准是当前的流量、团队和可靠性要求:单机扛得住就不要先做服务拆分,一条缓存规则让数据库吃力了就别等到压测报警才动手。演进路径还要留好退路,新旧方案必须能并存一段时间,真出问题能切回旧的那条。

设计流程

实践

  1. 步骤 1:写下当前阶段的业务目标、流量规模和团队规模,作为判断基线
  2. 步骤 2:逐项检查每个组件,指出在当前规模下并不必要的复杂度
  3. 步骤 3:排出引入复杂度的顺序,标明触发条件和验证指标
  4. 步骤 4:确认新旧方案可以并存与切换,为演进留下可回退路径

关注点:当前规模的必要性、引入复杂度的时机与回退成本。

时机

何时引入复杂度:
- 简单方案无法满足需求时
- 性能成为瓶颈时
- 可靠性要求提高时
- 团队规模扩大时

2. 服务降级

原则

保证核心功能,必要时牺牲非核心功能。

实践

降级的本质是提前决定「丢掉什么」。做法是先把功能按重要性排序,再为每一档写清楚降级后返回什么、什么时候触发、由谁来触发。触发条件要能自动判断,不能靠人盯着告警临时决定;降级结果要让用户仍然拿到可用的东西,而不是一个错误页面。恢复时同样有顺序,先放少量流量验证,再逐步放开。

设计流程

实践

  1. 步骤 1:列出全部对外功能并标注 P0 到 P3 的优先级
  2. 步骤 2:为每一档定义降级后的返回内容和可接受的体验差异
  3. 步骤 3:设定自动触发的阈值和依赖健康信号,明确触发后的执行动作
  4. 步骤 4:验证降级可恢复:先放开少量流量观察,再逐步回到正常档位

关注点:功能优先级、触发阈值与用户可见的降级结果。

降级策略

功能优先级:
P0:核心功能(用户认证、API 调用)
P1:重要功能(查询、统计)
P2:次要功能(推荐、分析)
P3:可选功能(报表、导出)

降级顺序:
P3 → P2 → P1 → P0

3. 幂等性设计

原则

同一个操作执行多次,结果与执行一次相同。

实践

幂等不是所有接口都需要,但它必须落在会重复执行的动作上:扣费、下单、发消息、回调重试。做法是给每次业务操作一个稳定的幂等键,同一个键重复到达时,服务端要么返回第一次的结果,要么明确拒绝,而不是再做一遍。判断幂等做得好不好有个粗暴但有效的标准:把同一次请求重放三遍,结果必须和执行一次完全一样。

设计流程

实践

  1. 步骤 1:列出所有会产生副作用的接口,标出哪些可能被重复调用
  2. 步骤 2:为每个副作用操作选定幂等键,明确键的生成方和有效期
  3. 步骤 3:在写路径上先查幂等记录,命中则返回首次结果而不重复执行
  4. 步骤 4:用重放同一请求三次的方式验证,副作用只发生一次

关注点:幂等键选取、重复请求识别与副作用去重。

4. 故障隔离

原则

单点故障不影响整体。

实践

隔离要回答两个问题:谁坏了,以及坏了能影响到谁。做法是把依赖关系画出来,标出每个调用点失败时该怎么办——重试、降级还是直接快速失败,然后按资源划分隔离域,让一个实例的连接池耗尽不会拖垮整条链路。判断隔离是否到位,可以主动打掉一个依赖,看故障有没有停在该依赖的边界内。

设计流程

实践

  1. 步骤 1:梳理服务之间的调用链,标出共享资源,并为每个依赖点定义失败行为
  2. 步骤 2:按实例、连接池和线程资源划分隔离域,避免共享资源被拖垮
  3. 步骤 3:主动切断一个依赖做故障演练,确认影响止步于该边界

关注点:故障边界、依赖失败时的处理方向与爆炸半径控制。

5. 监控先行

原则

没有监控的系统是盲目的。

必须监控的指标

没有监控的系统无法判断自己是否健康,而无效的监控只是噪音。指标要覆盖三个维度:流量(请求量、成功率)、延迟(各环节耗时分布)、饱和度(连接池、队列、内存使用率)。每个指标都要写清统计口径和告警阈值,阈值来自容量测算而不是拍脑袋,告警必须能指向一个具体的处置动作,否则半夜叫醒值班的人也没有用。

设计流程

必须监控的指标

  1. 步骤 1:按流量、延迟、饱和度三类列出必须监控的指标和统计口径
  2. 步骤 2:为每个指标设定告警阈值,阈值由容量上限和故障表现推导
  3. 步骤 3:为每条告警写明对应的处置动作和责任角色
  4. 步骤 4:做一次告警演练,确认阈值触发时能收到并定位到具体组件

关注点:指标口径、告警阈值与告警的可执行性。

6. 简单性优先

原则

能用简单方案解决的,就不要用复杂的。

对比

简单和简单化不是一回事:不必要地少做是偷懒,不必要地多加是浪费。这里要做的是对比——同一个问题摆出两种可行方案,把复杂度、维护成本和未来扩展的空间写在一起,再选当下更划算的那个。留下的方案不一定是技术最先进的那个,而是团队能真正维护、出了问题能查清的。

设计流程

对比

  1. 步骤 1:针对同一个问题列出两种可行方案,写清各自的关键差异
  2. 步骤 2:对比两者的实现复杂度、运维成本和故障排查难度
  3. 步骤 3:结合当前业务目标和关键约束,选出团队能长期维护的方案
  4. 步骤 4:记录取舍理由和放弃方案的原因,便于后续重新评估

关注点:方案复杂度、团队维护成本与长期演进空间。

7. 容错设计

原则

凡事都要考虑失败的情况。

实践

容错的前提是先承认失败一定会发生。要做的是把每个环节可能失败的方式列出来:超时、限流、依赖返回错误、数据写不进去,然后逐条给出对策——重试要带退避和次数上限,失败要有降级返回,真正不可恢复的要留下足够的日志。容错不等于掩盖错误,恢复之后的问题必须能被追溯。

设计流程

实践

  1. 步骤 1:枚举每个环节可能的失败模式:超时、限流、错误返回和写入失败
  2. 步骤 2:为每种失败定义对策:重试次数与退避间隔、降级返回或直接报错
  3. 步骤 3:在关键路径上接入熔断,避免失败时继续消耗下游资源
  4. 步骤 4:注入一次可控故障,验证重试、降级和告警都能按预期生效

关注点:失败模式枚举、重试退避策略与兜底返回内容。

8. 数据一致性

原则

根据业务需求选择合适的一致性级别。

一致性级别

强一致性:
- 适用:金融交易、库存扣减
- 代价:性能、可用性
- 实现:分布式事务、2PC

最终一致性:
- 适用:社交网络、推荐系统
- 代价:可能读到旧数据
- 实现:异步复制、事件溯源

弱一致性:
- 适用:统计、分析
- 代价:数据可能不准确
- 实现:定期同步

总结

系统设计 checklist

功能性:
✓ 核心功能完整
✓ 边界情况处理
✓ 错误处理

性能:
✓ 响应时间可接受
✓ 并发处理能力
✓ 缓存策略

可靠性:
✓ 故障自动恢复
✓ 数据备份
✓ 容灾方案

可维护性:
✓ 职责清晰
✓ 文档完善
✓ 监控告警

可扩展性:
✓ 模块化设计
✓ 接口规范
✓ 易于修改

最后的话

系统设计是一个持续学习和实践的过程。

记住:

  • 没有完美的架构,只有合适的架构
  • 技术服务于业务
  • 简单往往最好
  • 监控和日志很重要

继续学习:

  • 阅读大厂技术博客
  • 研究开源项目
  • 参与系统设计讨论
  • 实践是最好的老师

恭喜完成整个课程!

亲爱的读者,到现在你已经掌握了构建一个完整 API 平台所需的系统设计知识。

请继续学习和实践,成为一名优秀的系统设计师!

发现内容有问题?

告诉我们哪里需要改进。