跳转到内容

系统设计工具箱

系统设计的公共词汇:负载均衡、多级缓存、消息队列、限流熔断、水平扩展、容量估算。本章是面试与真实架构评审的公共工具箱。

把请求分发到多台机器:

策略做法适用
轮询/加权依次分发均匀负载
最少连接发给连接最少的请求耗时差异大
一致性哈希按键哈希到节点(→ 哈希表)缓存命中敏感(同 key 同节点)
地理 DNS/Anycast最近机房(→ DNS)全球分布

无状态设计是水平扩展的前提:会话状态外置(Redis)、文件外置(对象存储)——任何一台都能处理任何请求。四层(L4,快)与七层(L7,灵活:按路径/头分流)是两个均衡点。

缓存失效是两大难题之一("计算机科学只有两大难题:缓存失效与命名")——缓存的三个经典问题:

  • 穿透:查询不存在的 key,全部打到数据库——对策:空值缓存、布隆过滤器(先问"可能存在吗");
  • 击穿:热点 key 过期瞬间并发重建——对策:互斥重建(单飞 singleflight)、逻辑过期;
  • 雪崩:大量 key 同时过期——对策:过期时间加随机抖动、多级缓存。

缓存一致性:旁路缓存(cache-aside)是默认模式——读先查缓存、未命中查库回填;写先更新库、再失效缓存(失效而非更新:更新会引入并发竞态)。**主动失效(invalidation)**用发布订阅(→ 架构与权衡)。

缓存的判断标准:读多写少 + 可容忍陈旧——两者缺一,缓存是负债(一致性问题大于性能收益)。

队列(Kafka/RabbitMQ)的三个语义层:

  • 投递语义:至多一次(不重试)、至少一次(重试,可能重复)、精确一次(至少一次 + 幂等,→ 协调服务与实践模式);
  • 顺序保证:按 key 分区内有序(Kafka)——全局有序牺牲吞吐;
  • 堆积与背压:消费跟不上生产 → 堆积监控、消费者扩容、背压(生产端限速)。

队列的核心价值是削峰与解耦(时间解耦,→ 架构与权衡);代价是复杂度与最终一致。

  • 限流保护自己:固定窗口(简单、边界突刺)、滑动窗口、令牌桶(匀速发放、允许突发)是标准答案。按调用方/接口分层限流;
  • 熔断保护下游:错误率超阈值就"断开",直接快速失败——给下游喘息、避免级联超时(级联失败是分布式系统的死亡螺旋:超时 → 重试 → 更慢 → 更多重试)。半开探测恢复;
  • 舱壁(bulkhead):资源隔离(连接池按下游划分)——一个下游耗尽不拖垮全部。

限流保护自己,熔断保护下游——两者都是为级联失败设计的。

  • 垂直扩展(更强机器)有物理上限与单点风险;
  • 水平扩展(更多机器)的前提是无状态:会话外置、请求可路由到任意节点;
  • 扩展顺序:先无状态化,再读写分离(→ 复制与分片),再缓存,最后才分片(分片是最痛的一步,能不动就不动)。

先估算 QPS 与数据量,再画架构图——数量级的心算:

  • QPS:日活 DD、人均请求 rr → 平均 QPS ≈Dr/86400\approx Dr / 86400,峰值 ×3–10;
  • 读放大:1 次页面请求 → 10–100 次后端调用 → 100–1000 次数据库查询(N+1 的来源);
  • 存储:每条记录大小 × 增速 × 保留期;索引与副本 ×2–3;
  • 带宽:QPS × 响应大小。

估算的作用不是精确,是排除不可行:1 万 QPS × 10 KB 响应 = 100 MB/s 出口、每天 TB 级日志——这些数量级决定"要不要缓存、要不要分片",比架构图先算。

  • 无状态设计是水平扩展的前提;一致性哈希让缓存命中不因扩容失效
  • 缓存三问题:穿透(空值/布隆)、击穿(单飞)、雪崩(过期抖动);写用失效不用更新
  • 缓存失效是两大难题之一:读多写少 + 可容忍陈旧才值得缓存
  • 队列削峰解耦,代价是重复与乱序(消费端幂等)
  • 限流保护自己,熔断保护下游;先估算 QPS 与数据量,再画架构图