主题
案例串讲:读多、写多、低延迟、强一致四条主线
本文是系统设计系统学习的 L3 实战篇。前置:31. high-performance-patterns|32. high-availability-patterns。 本系列 27 个案例的全景总串讲,读完可以对 27 篇的归属和套路一览无余。
为什么需要这根主线
27 个分布式系统案例,表面看各有各的解法:短链用哈希、Feed 用推拉结合、秒杀用削峰、IM 用长连接……但仔细看,它们的核心矛盾只有四种:
- 读多写少:瓶颈在缓存
- 写多:瓶颈在写路径
- 低延迟:瓶颈在网络和状态
- 强一致:瓶颈在协调
一个系统设计有没有"章法",就看你能不能把新需求快速对号入座到某条主线,复用该主线的成熟套路。
案例归类地图
| 主线 | 流量特征 | 核心瓶颈 | 对应案例编号 |
|---|---|---|---|
| 读多写少 | 读:写 ≥ 10:1 | 缓存命中率 + 回源压力 | 01/03/10/11/14/13/24 |
| 写多 | 写 QPS > 10k/s | 写入吞吐 + 持久化 | 02/06/07/12/15/25 |
| 低延迟 | P99 ≤ 200ms | 传输路径 + 状态外置 | 04/05/08/16/23/27 |
| 强一致 | 读写涉及金钱/库存 | 锁 + 事务 + 协调 | 09/17/18/19/20/21/22/26 |
27 篇全部可以归入这四条线,没有例外。
读多写少线:多级缓存 + 预热 + 兜底
代表案例:短链服务(01)、Feed 流(03)、排行榜(11)、CDN 设计(23)、推荐系统(24)
读多写少的系统,核心战术是"把数据放到离用户最近的地方"。
多级缓存的漏斗层级:
用户请求 -> 浏览器缓存 -> CDN -> 网关缓存 -> 本地缓存 -> Redis -> DB每一层拦截一部分流量。设计时只需要回答三个问题:
- 每层拦多少?——短链的 302 重定向中,CDN 拦掉 80% 的请求,回源请求只有 20%
- 缓存什么?——Feed 流里,用户的 timeline 列表缓存在 Redis,但具体每条内容从 DB 读(混合存储)
- 缓存满了怎么办?——LRU 淘汰 + 异步预热(排行榜的定时重算)
常见陷阱:缓存穿透(大量不存在的 key 绕过缓存打 DB)——短链的 hash 冲突用布隆过滤器拦住;缓存雪崩(大量 key 同时过期)——过期时间加随机偏移。
写多线:削峰 + 批量 + 异步落库
代表案例:秒杀(02)、日志收集(12)、支付(15)、实时管道(25)
写多的系统,核心矛盾是"瞬间峰值远超平均"。系统按平均 QPS 建,但峰值可能 10 倍。
削峰三板斧:
请求 -> 限流(拦掉超量) -> MQ 削峰(排队) -> 批量刷盘(写合并) -> DB秒杀案例的典型数字:限流前 100w QPS,限流后 1w 放行,MQ 以 5000/s 的速度消费,DB 批量插入 5000/s。用户收到"排队中"的反馈,不需要实时知道结果——这就是"异步化"。
支付场景更严格:不能丢也不能重。幂等表 + 事务消息 + 对账兜底是标准组合。支付系统不会只靠 DB 事务保证,一定有一个离线对账程序,每天跑一遍"交易对不上的单子"。
低延迟线:长连接 + 地理分片 + 就近接入
代表案例:网约车(04/27)、IM(05)、配置中心(08)、通知系统(16)
低延迟系统,P99 比平均 QPS 更重要。用户能接受 1% 的请求挂掉,但不能接受 1% 的请求卡 5 秒。
IM 的架构骨架:WebSocket 长连接(避免 TCP 三次握手开销) -> 连接管理器(路由表) -> 消息分片(按用户 ID 哈希) -> 持久化与推拉结合。
网约车的难点在不同子问题有不同的延迟要求:
- 乘客下单:写多线,接受 1-2 秒
- 司机接单推送:低延迟线,P99 < 500ms
- 轨迹上传:写多线,接受 5 秒
- ETA 查询:读多写少线,P99 < 200ms
同一个系统,不同子模块走不同主线。这不是混合,这是按功能分治。
强一致线:幂等 + 事务消息 + 对账
代表案例:分布式锁(09)、权限系统(17)、数据隐私(18)、多活(19)、KV 存储(21)、对象存储(22)、分布式文件系统(26)
强一致的核心矛盾是"分布式环境下,两个节点没法同时看到最新的全局状态"。CAP 定理把这个矛盾说透了,但 CAP 是分析工具,不是设计工具。
支付系统的实际做法:
扣款请求 -> 本地事务写扣款记录 -> 发事务消息 -> 下游消费 -> 幂等去重不是"分布式事务 XA",而是"本地事务 + 可靠消息 + 幂等消费"。最终一致 + 对账兜底,比强一致分布式事务更可靠,也更简单。
对账才是真正的"最终一致性警察"。支付系统每天凌晨跑一次对账脚本,把"本系统认为成功"的单子和"外部认为成功"的单子逐条对比,不一致的走人工处理。没有任何分布式事务协议能覆盖这个场景。
实战:迁移学习法
拿到一道新系统设计题,拆解步骤:
- 归类:读多写少?写多?低延迟?强一致?还是混合(如网约车有多个子问题)
- 套骨架:走对应主线的基础架构模板
- 加差异:这个系统的特殊约束是什么?数据量级?一致性要求?读写比例?
- 深挖瓶颈:按四步分析法(28 篇)的估算,找出哪个组件会先挂
新题套用模板:
需求澄清:用户规模、读写比、一致性、延迟
主线判断:____
估算:QPS ____、存储 ____、带宽 ____
架构骨架:[对应主线的标准架构图]
差异点:[特殊约束对应的定制化方案]
瓶颈分析:哪个组件先扛不住,怎么优化举例:设计一个"在线文档协作系统"。
- 读多写少(大部分时间用户只看文档) + 低延迟(编辑时要实时同步)
- 读多线骨架:CDN 加速静态内容 + 缓存文档元数据
- 低延迟线骨架:WebSocket + 操作转换(OT/CRDT)
- 差异点:多人同时编辑的冲突解决,比 IM 的简单消息传递复杂得多
- 瓶颈:写冲突的合并效率,不是缓存
迁移学习法的核心不是背模板,而是知道"这个新问题跟哪个老问题最像,哪里不一样"。
常见误区与小结
- 误区 1:以为缓存能解决所有问题。缓存只解决读多写少,写多场景缓存会拖后腿
- 误区 2:所有系统都想要强一致。92% 的互联网业务最终一致就够,引入强一致方案的成本是 3-5 倍开发量
- 误区 3:秒杀系统和支付系统都用 MQ 削峰,但支付对 MQ 的可靠性要求高一个量级
- 误区 4:低延迟只靠网络优化。更常见的是减少不必要的 RTT(合并请求、批量处理)
- 误区 5:对账是"最后手段",实际上它应该从第一天就设计到系统里
小结:系统设计没有银弹。四条主线 + 迁移学习法,让你面对任何新题都能快速定位到"这条线用什么套路"。"读多看缓存、写多削峰、低延迟近端、强一致对账"——记住这四句话,再难的系统也能拆出骨架。
本系列 27 个案例的完整索引:01-短链服务|02-秒杀|03-Feed 流|04-网约车|05-IM|06-ID 生成器|07-限流系统|08-配置中心|09-分布式锁|10-ES|11-排行榜|12-日志收集|13-评论系统|14-直播|15-支付|16-通知|17-权限|18-数据隐私|19-多活|20-取舍|21-Dynamo|22-S3|23-CDN|24-推荐|25-实时管道|26-HDFS|27-网约车调度
参考
参考:本系列 28-32 篇(system-design 学习篇)的全部内容,以及 01-27 篇案例