- 后端
- 文档
- 教程
【免费下载链接】system-design-101
Explain complex systems using visuals and simple terms. Help you prepare for system design interviews.
本文基于 system-design-101 仓库中的《10 System Design Tradeoffs You Cannot Ignore》一文展开,以该文档的 10 大权衡为主线,结合仓库内 CAP 定理、缓存策略、消息队列、数据库选型等姊妹文档,为你在面试与真实架构设计中反复面对的每一组"二选一"提供判断框架与实战决策参考。读完本文,你将掌握 10 组核心权衡的适用场景、取舍代价与典型反例,并能用同一套"选择—代价—场景"的思路去应对任何新出现的架构抉择。
在系统设计中,几乎没有"永远正确"的答案,只有"在当前约束下最合理"的选择。如果你不懂权衡(trade-offs),你就不算真正懂系统设计——这是本仓库《10 System Design Tradeoffs You Cannot Ignore》一文开篇就点明的核心观点。架构师的日常工作,本质上就是在多组互相冲突的目标之间做取舍。本文把这 10 组最常见的权衡逐一拆开:每组权衡讲清"它是什么、各自适用什么场景、代价是什么、如何在仓库中找到更多佐证",让你面对面试官的追问和真实项目决策时都有章可循。
1. 垂直扩展 vs 水平扩展(Vertical vs Horizontal Scaling)
垂直扩展(Vertical Scaling):给现有服务器增加更多资源(CPU、内存、磁盘),一台机器"变强"。
水平扩展(Horizontal Scaling):往服务器池里增加更多服务器,让多台机器"组团"分担负载。
这是一组成本、复杂度与上限的权衡:
| 维度 | 垂直扩展 | 水平扩展 |
|---|---|---|
| 操作方式 | 升级单机硬件 | 增加机器数量 |
| 实施难度 | 低,改配置即可 | 高,需要负载均衡、数据分片等配套 |
| 扩展上限 | 受单机硬件物理上限约束 | 理论上近乎无限 |
| 故障风险 | 单点故障影响面大 | 需要处理节点间的数据一致与协调 |
在本仓库的 8 Must-Know Scalability Strategies 中,水平扩展被列为必知的扩展策略之一,并且通常要配套负载均衡(Load Balancing)把请求均匀分发到多台服务器、数据库分片(Database Sharding)来分布数据,才能让新增的机器真正派上用场。也就是说:水平扩展从来不是"加机器"三个字这么简单,它是一整套分布式基础设施的投入。
决策建议:中小规模、预算有限、追求快速见效时先垂直扩展;当单机逼近硬件上限、或需要高可用与弹性伸缩时,必须转向水平扩展。
2. SQL vs NoSQL
SQL 数据库:把数据组织成行(row)与列(column)构成的表(table),通过结构化查询语言访问,强于事务与关联查询。
NoSQL 数据库:适合需要**灵活 schema(flexible schema)**的应用,文档、键值、宽列、图等模型各自擅长不同的访问模式。
这一权衡在仓库的姊妹篇 How to Decide Which Type of Database to Use 中给出了非常务实的判断框架:关系型数据库"几乎什么都能解决"(Almost anything could be solved by them);内存存储(in-memory store)以速度和有限的数据量见长,适合快速操作;时序数据库管理带时间戳的数据;图数据库适合非结构化对象之间的复杂关系;文档存储适合大批量不可变数据;宽列存储通常用于大数据、分析、报表等需要**反规范化数据(denormalized data)**的场景。
决策建议:
- 数据关系复杂、强事务、强约束 → SQL(MySQL、PostgreSQL 等);
- 海量写入、schema 频繁变化、水平扩展优先 → NoSQL;
- 不要只按类型选,要结合访问模式、一致性要求与团队熟悉度综合判断(详见第 5、6 组权衡)。
3. 批处理 vs 流处理(Batch vs Stream Processing)
批处理(Batch Processing):先收集数据,然后一次性统一处理。典型例子是每日账单处理(daily billing processes)——把一天的数据攒齐,在固定时间点集中结算。
流处理(Stream Processing):数据到达即处理,实时性高。典型例子是欺诈检测(fraud detection)——每一笔交易都必须立刻判断是否有风险,等不及"攒批"。
仓库的 Big Data Pipeline Cheatsheet 展示了现代数据管线的完整生命周期:采集(Ingestion)→ 数据湖(Data Lake)→ 计算(Computation)→ 数据仓库(Data Warehouse)→ 呈现(Presentation),而批与流正是在"计算"阶段的两条路径:AWS 以 Kinesis 负责流式数据、EMR 做批处理;GCP 则以 DataProc(批)/ DataFlow(流)分治;Azure 用 Databricks 统一处理。真实系统中二者常常并存,形成 Lambda / Kappa 风格架构。
决策建议:对实时性要求高、事件量连续到达 → 流处理;吞吐要求高、允许延迟、结果可离线重算 → 批处理(成本通常更低、更易容错重跑)。
4. 规范化 vs 反规范化(Normalization vs Denormalization)
规范化(Normalization):把数据拆分到多个关联表中,确保每条信息只存储一次(each piece of information is stored only once),消除冗余,保证写入一致性,但查询通常需要多表 JOIN。
反规范化(Denormalization):把数据合并进更少的表,以冗余换取查询性能(better query performance),减少 JOIN,加快读取,但带来了更新一致性的维护成本。
仓库的 Vertical vs Horizontal Partitioning 可作为本权衡的延伸参考:纵向分区(vertical partitioning)把部分列拆到新表,属于"按列拆分";横向分区(sharding)把一张表分成多个更小的表,属于"按行拆分"。无论是分表还是反规范化,本质上都是在用存储与一致性的复杂度换取读性能。
决策建议:写多读少、强一致性优先 → 规范化;读多写少、报表/聚合/大数据分析场景 → 反规范化(宽列存储正是为反规范化数据而生,见第 2 组)。
5. 一致性 vs 可用性(Consistency vs Availability)
一致性(Consistency):保证每次读到的都是最新数据(getting the most recent data every single time)。
可用性(Availability):保证系统始终在线可用(always up and running),即使部分组件出问题也能响应请求。
这正是 CAP 定理的核心张力。仓库的 CAP Theorem: One of the Most Misunderstood Terms 指出:CAP 定理表明一个分布式系统在一致性、可用性、分区容错性三者中最多同时满足两个。但该文档同时提醒我们警惕"2 选 3"的简化表述:
- 分区(partition)是常态而非罕见,所以现实中我们通常在"分区发生时"在 C 与 A 之间选择;
- CAP 讨论的是100% 的一致性与可用性,而更现实的讨论是没有分区时延迟与一致性的取舍——即 PACELC 定理;
- 选数据库不能只靠 CAP 背书,例如公司选 Cassandra 做聊天应用,不是因为"它是 AP 系统"这么简单,而是因为它在存储聊天消息方面有一整套优良特性。
决策建议:金融交易、库存扣减等场景重一致性;社交动态、消息推送、监控指标等场景可接受短暂不一致以换取可用性。详见第 6 组。
6. 强一致 vs 最终一致(Strong vs Eventual Consistency)
强一致性(Strong Consistency):数据更新**立即反映(immediately reflected)**到所有读取,用户永远看不到旧数据。
最终一致性(Eventual Consistency):数据更新在节点间存在延迟(delayed),但经过一段时间后最终会收敛一致。
仓库的 Top Eventual Consistency Patterns You Must Know 给出了 4 种工程化的最终一致实现模式:
- 基于事件的最终一致(Event-based):服务发事件、其他服务监听事件更新自己的数据库,服务间松耦合,但一致性有延迟;
- 后台同步(Background Sync):后台任务按固定计划拉齐多数据库数据,收敛速度取决于任务调度频率;
- Saga 模式:一系列本地事务串成的长事务序列,每个事务只更新单一服务的数据,用于管理长生命周期、最终一致的事务;
- CQRS 模式:把读写分离到不同数据库,读写模型各自按需求优化,最终收敛一致。
结合仓库的 Delivery Semantics 还能看到一致性与消息投递语义的交叉:at-most-once允许丢消息但不重发(适合监控指标这类可容忍少量丢失的场景);at-least-once不丢消息但可能重复(需消费端用唯一键去重);exactly-once对用户最友好但对系统性能和复杂度代价最高(支付、交易、账务等不允许重复的场景尤其依赖它)。
决策建议:先问"读到旧数据一秒,业务后果是什么"。后果不可接受 → 强一致;后果可控 → 用上述模式实现最终一致,换取吞吐与可用性。
7. REST vs GraphQL
REST:通过访问多个端点(multiple endpoints)聚合数据,每个端点对应一种资源操作。
GraphQL:通过特定查询(specific queries)实现更高效的数据获取,一次请求精准拿到所需字段,但设计成本更高(the design cost is higher)。
仓库的 REST API vs. GraphQL 将两组优缺点展开如下:
REST 的优势与代价
- 使用标准 HTTP 方法(GET/POST/PUT/DELETE)完成 CRUD,服务间接口简单统一;
- 缓存策略实现直观;
- 短板是组装关联数据可能需要多次往返(multiple roundtrips)访问多个端点。
GraphQL 的优势与代价
- 单一端点,客户端在嵌套查询中精确指定所需字段,服务端只返回优化后的载荷;
- 支持 Mutation 修改数据、Subscription 实时通知,善于聚合多数据源,适配快速演进的前端需求;
- 代价是复杂度转移到客户端、不当查询可能造成滥用(abusive queries),且缓存比 REST 更复杂。
决策建议:面向外部、契约简单稳定、缓存要求高 → REST;前端需求复杂多变、需要聚合多源数据、追求最小载荷 → GraphQL。可进一步参考仓库的 SOAP vs REST vs GraphQL vs RPC 了解 API 风格演进全景。
8. 有状态 vs 无状态(Stateful vs Stateless)
有状态系统(Stateful):记住过去的交互(remembers past interactions),例如会话中的登录态、购物车、游戏进度。
无状态系统(Stateless):不追踪过去的交互(does not keep track of past interactions),每个请求自带全部上下文,可被任意实例处理。
仓库的 8 Must-Know Scalability Strategies 把无状态服务(Stateless Services)列为头号扩展策略:无状态服务不依赖某台服务器的本地数据,因此更容易扩展(easier to scale)——任何请求可以被调度到任意新加的实例上,配合负载均衡即可平滑扩容。有状态则意味着"粘性会话"、状态迁移与复制等额外复杂度。
决策建议:默认把服务设计成无状态,把"必须记住的状态"外置到数据库、缓存或消息系统等共享存储中;仅在确实无法外置(如本地计算上下文极重)时才保留有状态节点,并为其配套一致性方案(见第 5、6 组)。
9. 读穿透缓存 vs 写穿透缓存(Read-Through vs Write-Through Cache)
读穿透缓存(Read-Through Cache):缓存未命中(cache miss)时,由缓存层负责从数据库加载数据(loads data from the database)并回填,应用无需感知数据源。
写穿透缓存(Write-Through Cache):写入时同步更新缓存与存储(simultaneously writes data updates to the cache and storage),保证二者始终一致,但每次写都多一次缓存写开销。
仓库的 Top 5 Caching Strategies 给出了完整的缓存策略谱系:读侧有Cache-Aside(应用自己查缓存、未命中则回源并回填)与Read-Through;写侧有Write-Around(直接写库、绕过缓存)、Write-Back(先写缓存、延迟落库)与Write-Through。文档特别强调:这些策略常常组合使用,例如 Write-Around 常与 Cache-Aside 搭配,以保证缓存内容不过期。更系统的缓存设计维度(缓存部署、分布式缓存、替换与失效、缓存挑战等)可参见仓库的 Learn Cache。
决策建议:
- 读多写少、希望缓存层自治 → Read-Through;
- 读写频繁且要求缓存与库强一致 → Write-Through(接受写路径变慢);
- 写多、可容忍短暂不一致 → Write-Around / Write-Back(注意 Write-Back 的宕机丢数据风险,需结合第 5、6 组的一致性权衡评估)。
10. 同步 vs 异步处理(Sync vs Async Processing)
同步处理(Synchronous Processing):任务一个接一个地执行(tasks are performed one after another),前一个完成才能开始下一个,调用方阻塞等待结果。
异步处理(Asynchronous Processing):任务可以在后台运行(run in the background),新任务无需等待前一个任务完成即可启动,调用方不被阻塞。
仓库的 8 Must-Know Scalability Strategies 指出:把耗时的、资源密集型的任务用异步方式移到后台 worker 处理,是支撑新请求横向扩展的关键手段。而异步的基础设施正是消息队列——仓库的 Types of Message Queues 列出了选型时要评估的关键特征:速度(Speed)、可扩展性(Scalability)、可靠性(Reliability)、持久性(Durability)、易用性(Ease of Use)、生态(Ecosystem)、集成能力(Integration)、协议支持(Protocol Support)。从 IBM MQ → RabbitMQ → Kafka → Pulsar 的演进可以看到:Kafka 以高吞吐、低延迟的分布式事件流平台著称,Pulsar 则更云原生、原生支持分层存储。
决策建议:
- 需要即时结果(读请求、支付确认)→ 同步,但要控制耗时;
- 可容忍延迟的耗时任务(发邮件、生成报表、图片处理、通知推送)→ 异步 + 消息队列,让主链路快速返回;同时根据业务容忍度选择投递语义(见第 6 组)。
总结:把 10 组权衡变成一套决策心法
把这 10 组权衡连起来看,会发现它们其实环环相扣:
- 扩展(第 1 组)决定了服务形态,而服务形态牵动状态设计(第 8 组,无状态更容易水平扩展);
- 存储选型(第 2、4 组)决定了数据如何组织,进而决定了你能提供哪种一致性(第 5、6 组);
- 处理模式(第 3、10 组)决定了系统的延迟画像与吞吐能力,中间往往需要消息队列做解耦;
- API 风格(第 7 组)决定了客户端与服务端的复杂度分布;
- 缓存策略(第 9 组)则是在前面所有决策之上做性能优化的"最后一公里",且必须与一致性模型对齐。
面试官真正想看到的,不是你背下"选 A 不选 B"的结论,而是你能说清:我面对什么约束 → 我选了哪一侧 → 我付出了什么代价 → 我怎么缓解这个代价。把本文的 10 组权衡连同本仓库的 CAP Theorem、Top 5 Caching Strategies、Top Eventual Consistency Patterns、Delivery Semantics、How to Decide Which Type of Database to Use 等文档一起研读,就能在面试和真实架构评审中把每一组"二选一"讲成有理有据的工程决策。
- 后端
- 文档
- 教程
【免费下载链接】system-design-101
Explain complex systems using visuals and simple terms. Help you prepare for system design interviews.
相关推荐
EnergyBar深度评测:为什么它是Mac Touch Bar的最佳替代方案
EnergyBar深度评测:为什么它是Mac Touch Bar的最佳替代方案 EnergyBar是一款能够为Mac Touch Bar带来全新生命力的实用工具
System Design 101:系统设计面试终极指南
System Design 101:系统设计面试终极指南 系统设计面试已成为现代技术招聘流程中不可或缺的一环,它不仅是评估候选人技术深度的关键环节,更是衡量其解
后端文档教程如何设计高性能IoT系统架构:初学者必知的核心组件与实践指南
如何设计高性能IoT系统架构:初学者必知的核心组件与实践指南 在当今万物互联的时代,物联网(IoT)系统已经渗透到智能家居、工业监控、智慧城市等各个领域。 Io
后端文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考