AI 能写代码,但"这个系统该怎么拆、数据怎么流、瓶颈在哪里"必须你来判断。 这份文档覆盖主流系统的核心设计思路,建立系统思维的基础。
不追求每个系统的完整设计,而是抓住每个系统最核心的设计挑战和解法。
目录
- 一、电商系统
- 二、社交 / 内容平台
- 三、即时通讯(IM)
- 四、视频平台(YouTube / B站)
- 五、直播系统
- 六、云文档(飞书文档 / Google Docs)
- 七、项目管理(Jira / Linear)
- 八、搜索系统
- 九、通知系统
- 十、文件存储与 CDN
- 附:系统设计万能框架
一、电商系统
电商是系统设计的集大成者,几乎涵盖所有核心问题。
1.1 商品系统
核心挑战: 读多写少,SKU 数量巨大,需要多维度搜索。
架构:
写入: 运营后台 → MySQL(主库)→ 同步到 ES + Redis
读取: 商品列表/搜索 → Elasticsearch
商品详情 → Redis 缓存 → 缓存未命中 → MySQL
技术选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 主存储 | MySQL | 商品数据需要事务(上下架、价格变更) |
| 搜索 | Elasticsearch | 多维度搜索(品类、价格区间、关键词) |
| 缓存 | Redis | 商品详情页 QPS 极高,必须缓存 |
关键概念:
- 读写分离:写 MySQL,读 ES + Redis
- 数据同步:MySQL → ES 用 Canal(binlog 监听),MySQL → Redis 用 Cache Aside 模式
- SPU / SKU 模型:SPU 是商品(iPhone 16),SKU 是具体规格(iPhone 16 黑色 256G)
1.2 购物车
核心挑战: 登录/未登录状态合并,高频读写。
架构:
未登录: 购物车存浏览器 localStorage
登录后: localStorage 数据合并到服务端
服务端: Redis Hash 存储(key=userId, field=skuId, value=数量)
为什么用 Redis 不用 MySQL:
- 购物车是高频读写(用户频繁加减商品)
- 不需要复杂查询
- Redis Hash 天然适合这个数据结构
- 持久化:Redis RDB/AOF + 定期同步到 MySQL 兜底
关键概念:
- 登录态合并:未登录加了 3 件,登录后要合并到账号下
- 购物车上限:防止恶意用户加几万件商品
- 价格实时性:展示时要查最新价格,不能用加入时的价格
1.3 订单系统
核心挑战: 数据一致性、状态机、高并发下单。
状态机:
待支付 → 已支付 → 待发货 → 已发货 → 已收货 → 已完成
↓ ↓
已取消 已退款
架构:
下单: 前端 → 订单服务 → 创建订单(MySQL)→ 发 MQ → 库存服务扣减
支付: 支付回调 → 订单服务更新状态 → 发 MQ → 通知/物流
技术选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 主存储 | MySQL | 订单需要强一致性和事务 |
| 订单号 | 雪花算法 / 分布式 ID | 全局唯一、有序、不可猜测 |
| 状态流转 | MQ | 解耦订单和下游(库存、支付、物流) |
关键概念:
- 幂等性:支付回调可能重复,用订单号去重
- 超时取消:待支付订单 30 分钟未支付自动取消(延迟队列 / 定时任务)
- 分库分表:订单量大了按 userId 分表
1.4 支付系统
核心挑战: 钱不能多不能少,必须强一致。
核心流程:
用户点支付 → 创建支付单 → 调第三方支付(微信/支付宝)→ 等待回调
第三方回调 → 验签 → 更新支付单状态 → 更新订单状态
关键概念:
- 幂等性:回调可能重复,必须用支付单号去重
- 对账:每天和第三方对账,发现差异人工处理
- 退款:退款是独立流程,不是支付的逆操作
- 掉单处理:回调没收到怎么办?定时轮询第三方查询接口
安全:
- 回调验签:确认回调确实来自第三方,不是伪造的
- 金额校验:回调金额必须和订单金额一致
- HTTPS:所有支付相关通信必须加密
1.5 库存系统
核心挑战: 超卖问题——100 件库存,200 人同时下单。
方案对比:
| 方案 | 原理 | 优缺点 |
|---|---|---|
| 数据库乐观锁 | UPDATE SET stock=stock-1 WHERE stock>0 | 简单,但高并发下大量失败重试 |
| Redis 预扣减 | DECR stock,原子操作 | 快,但 Redis 和 DB 可能不一致 |
| 分布式锁 | 加锁 → 扣减 → 释放 | 安全,但性能差 |
标准方案: Redis 预扣减 + MQ 异步落库
下单 → Redis DECR(原子操作,不会超卖)→ 返回成功
→ MQ → 库存服务 → MySQL UPDATE
二、社交 / 内容平台
2.1 Feed 流(朋友圈 / 小红书 / Instagram)
核心挑战: 用户打开首页,看到关注的人发的内容,按时间排序。怎么高效生成这个列表?
两种模式:
| 模式 | 原理 | 适用场景 |
|---|---|---|
| 推模式(写扩散) | 发布时推送到所有粉丝的收件箱 | 粉丝少(朋友圈) |
| 拉模式(读扩散) | 读取时从所有关注人的发件箱拉取合并 | 粉丝多(微博大V) |
| 推拉结合 | 普通用户推,大V拉 | 大规模社交平台 |
推模式架构:
用户 A 发帖 → 写入 A 的发件箱(MySQL)
→ 查 A 的粉丝列表
→ 写入每个粉丝的收件箱(Redis Sorted Set,score=时间戳)
用户 B 刷 Feed → 读 B 的收件箱(Redis ZREVRANGE)→ 分页返回
技术选型:
- 收件箱:Redis Sorted Set(按时间排序,分页查询 O(logN))
- 发件箱:MySQL(持久化,支持查看"我的发布")
- 内容存储:MySQL + 对象存储(图片/视频)
关键概念:
- 写扩散的成本:一个有 100 万粉丝的大V发一条,要写 100 万次
- Timeline vs Rank Feed:按时间排序 vs 按算法推荐排序
- 游标分页:用 lastId 而不是 offset,避免翻页时数据偏移
2.2 评论系统
核心挑战: 树形结构(评论的回复的回复),高效查询和分页。
数据模型:
comments (
id, content, user_id,
parent_id, -- 直接父评论(NULL 表示一级评论)
root_id, -- 根评论 ID(用于查询某条评论的所有回复)
target_id, -- 被评论的对象(帖子/商品)
created_at
)
查询策略:
一级评论: WHERE target_id=? AND parent_id IS NULL ORDER BY created_at LIMIT 20
二级回复: WHERE root_id=? ORDER BY created_at LIMIT 10
为什么不用邻接表递归查询: 深层嵌套递归查询性能差。用 root_id 扁平化,前端自己组装树形结构。
技术选型:
- 存储:MySQL(需要事务,评论数不算特别大)
- 计数:Redis(评论数缓存,避免 COUNT 查询)
- 敏感词过滤:前置过滤服务
2.3 点赞 / 收藏 / 关注
核心挑战: 高频操作,需要快速判断"我是否已点赞"。
架构:
点赞: Redis SET(key=post:123:likes, value=userId)
→ 异步 MQ → MySQL 持久化
查询"我是否点赞": Redis SISMEMBER O(1)
查询"点赞数": Redis SCARD 或单独计数器
为什么不直接用 MySQL:
- "我是否点赞"是每次渲染都要查的,QPS 极高
- MySQL 每次
SELECT WHERE post_id=? AND user_id=?太慢 - Redis SET 的 SISMEMBER 是 O(1)
关键概念:
- 计数器:单独维护,不要每次 COUNT
- 取消点赞:SREM + 计数器 DECR
- 数据一致性:Redis 和 MySQL 最终一致,不需要强一致
三、即时通讯(IM)
核心挑战: 实时性、消息可靠投递、离线消息、群聊扩散。
架构:
客户端 A ←→ WebSocket ←→ Gateway → MQ → 消息服务 → 存储
↓
客户端 B ←→ WebSocket ←→ Gateway ← 推送 ←──┘
技术选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 长连接 | WebSocket | 全双工,实时推送 |
| 网关 | 自研 / Netty | 维护百万级长连接 |
| 消息存储 | MySQL + HBase | 近期消息 MySQL,历史消息 HBase |
| 消息队列 | Kafka | 群聊消息扩散、异步投递 |
| 推送 | APNs / FCM | 离线推送 |
关键概念:
- 消息 ID:全局有序(雪花算法),用于排序和去重
- 已读回执:客户端上报已读的最大消息 ID
- 离线消息:用户上线后拉取离线期间的消息(收件箱模式)
- 群聊扩散:写扩散(小群)vs 读扩散(大群)
- 消息漫游:多端同步,换设备能看到历史消息
难点:
- 百万级长连接管理(单机 10 万连接,需要多台 Gateway)
- 消息的有序性(同一个会话内消息必须有序)
- 多端同步(手机、电脑、平板同时在线)
四、视频平台(YouTube / B站)
核心挑战: 大文件存储、转码、CDN 分发、推荐算法。
上传 → 播放全链路:
上传: 客户端分片上传 → 对象存储(原始文件)
→ MQ → 转码服务(多分辨率:1080p/720p/480p)
→ 转码完成 → 对象存储(转码后文件)→ CDN 预热
播放: 客户端请求 → CDN 边缘节点(命中缓存直接返回)
→ CDN 未命中 → 回源到对象存储
→ 自适应码率(根据网络质量切换分辨率)
技术选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 存储 | S3 / OSS | 海量视频文件 |
| 转码 | FFmpeg 集群 | CPU 密集型,需要水平扩展 |
| 分发 | CDN | 全球加速,减少延迟 |
| 播放协议 | HLS / DASH | 自适应码率,分片加载 |
| 推荐 | 协同过滤 + 深度学习 | 个性化推荐 |
关键概念:
- HLS(HTTP Live Streaming):视频切成小片段(.ts),按需加载
- 自适应码率:网络好播 1080p,网络差自动降到 480p
- 预加载:播放当前片段时预加载下一个片段
- 冷热分离:热门视频放 CDN,冷门视频放对象存储
五、直播系统
核心挑战: 超低延迟(< 3 秒)、高并发观看、弹幕实时同步。
架构:
主播推流 → RTMP → 流媒体服务器 → 转码(多码率)
↓
观众拉流 ← CDN ← HLS/FLV ← 流媒体服务器
弹幕: 观众发送 → WebSocket → 弹幕服务 → 广播给同房间所有观众
协议选择:
| 协议 | 延迟 | 适用场景 |
|---|---|---|
| RTMP | 1-3 秒 | 推流端(主播) |
| HLS | 5-30 秒 | 大规模分发(CDN 友好) |
| HTTP-FLV | 1-3 秒 | 低延迟拉流 |
| WebRTC | < 1 秒 | 连麦、视频通话 |
关键概念:
- 推流 vs 拉流:主播推,观众拉
- 房间模型:每个直播间是一个"房间",弹幕只在房间内广播
- 连麦:WebRTC P2P 或通过服务器中转
- 礼物系统:本质是支付系统 + 动画渲染
六、云文档(飞书文档 / Google Docs)
核心挑战: 多人实时协同编辑,冲突解决。
两种协同算法:
| 算法 | 原理 | 代表产品 |
|---|---|---|
| OT(Operational Transformation) | 操作转换,服务端协调 | Google Docs |
| CRDT(Conflict-free Replicated Data Type) | 无冲突数据结构,去中心化 | Figma、Yjs |
OT 简单理解:
用户 A 在位置 3 插入 "X"
用户 B 在位置 1 插入 "Y"
→ 服务端收到 A 的操作后,转换 B 的操作:位置 1 → 还是 1(在 A 插入点之前)
→ 服务端收到 B 的操作后,转换 A 的操作:位置 3 → 变成 4(B 在前面插入了一个字符)
架构:
客户端 A ←→ WebSocket ←→ 协同服务(OT/CRDT)←→ WebSocket ←→ 客户端 B
↓
文档存储(快照 + 操作日志)
技术选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 通信 | WebSocket | 实时双向同步 |
| 协同算法 | OT 或 CRDT | 冲突解决 |
| 存储 | 快照 + Op Log | 快照用于加载,Op Log 用于回放和协同 |
| 富文本模型 | Slate / ProseMirror / 自研 | 结构化文档模型 |
关键概念:
- 操作日志(Op Log):记录每一次编辑操作,可以回放
- 快照(Snapshot):定期保存文档完整状态,加速加载
- 光标同步:其他人的光标位置实时显示
- 离线编辑:离线时本地编辑,上线后合并(CRDT 更擅长)
七、项目管理(Jira / Linear)
核心挑战: 灵活的数据模型(不同团队的工作流不同)、权限控制、实时协作。
数据模型:
Workspace → Project → Board → Column → Issue
↓
Comment / Attachment / Activity Log
关键设计:
- 自定义字段:Issue 的字段不固定(有的团队要"优先级",有的要"故事点")→ EAV 模型或 JSON 字段
- 工作流引擎:每个 Board 的列(Column)代表状态,拖拽 = 状态流转,可自定义
- 权限模型:RBAC(角色:Admin / Member / Viewer)+ 项目级权限
- 活动日志:所有操作记录(谁在什么时候改了什么),用于审计和通知
技术选型:
| 组件 | 选型 | 理由 |
|---|---|---|
| 主存储 | PostgreSQL | 支持 JSONB(灵活字段)、全文搜索 |
| 实时更新 | WebSocket / SSE | 看板拖拽实时同步 |
| 搜索 | PostgreSQL 全文搜索 或 ES | 数据量不大时 PG 够用 |
| 文件 | S3 | 附件存储 |
八、搜索系统
核心挑战: 海量数据中毫秒级返回相关结果。
架构:
数据写入 → MySQL → Canal(binlog 监听)→ Elasticsearch
搜索请求 → API → Elasticsearch → 返回结果
Elasticsearch 核心概念:
- 倒排索引:传统数据库是"文档 → 词",ES 是"词 → 文档列表"
- 分词器:中文用 IK 分词,英文用标准分词
- 相关性评分:TF-IDF / BM25,决定结果排序
- 分片:数据水平拆分到多个节点
搜索优化:
- 搜索建议(Suggest):用户输入时实时提示
- 拼写纠错:"iphoen" → "iphone"
- 同义词:搜"手机"也能搜到"电话"
- 热搜:Redis 计数器统计搜索频率
九、通知系统
核心挑战: 多渠道(站内信、推送、邮件、短信)、不重复、不遗漏。
架构:
业务事件 → MQ → 通知服务 → 路由(根据用户偏好选择渠道)
├→ 站内信 → MySQL + WebSocket 推送
├→ App 推送 → APNs / FCM
├→ 邮件 → SMTP 服务
└→ 短信 → 短信网关
关键概念:
- 通知模板:不同事件用不同模板("{{user}} 评论了你的帖子")
- 聚合通知:"张三等 5 人赞了你的帖子"(不是发 5 条通知)
- 用户偏好:用户可以关闭某类通知
- 限流:防止通知轰炸(同一用户同一类型 1 小时最多 N 条)
- 已读管理:标记已读、全部已读
十、文件存储与 CDN
核心挑战: 海量文件的存储、分发、访问控制。
架构分层:
上传: 客户端 → Presigned URL → 对象存储(S3)
访问: 客户端 → CDN(边缘缓存)→ 对象存储(回源)
管理: 后端 → 对象存储 SDK(删除、移动、权限)
存储选型:
| 场景 | 选型 | 理由 |
|---|---|---|
| 用户上传的图片/视频 | 对象存储(S3) | 海量、低成本、高可用 |
| 需要文件系统语义 | 分布式文件系统(JuiceFS) | POSIX 接口,可挂载 |
| 临时文件 | 本地磁盘 / tmpfs | 处理完就删 |
| 静态资源(JS/CSS) | CDN | 全球加速 |
关键概念:
- Presigned URL:后端生成签名 URL,前端直传,后端不承受流量
- CDN 缓存策略:Cache-Control、ETag、缓存刷新
- 图片处理:对象存储通常支持 URL 参数裁剪/压缩(
?w=200&h=200) - 冷热分离:热数据标准存储,冷数据归档存储(成本低 10 倍)
- 生命周期策略:自动删除 N 天前的临时文件
附:系统设计万能框架
面试时拿到任何系统设计题,都可以用这个框架:
Step 1: 需求澄清(2 分钟)
- 核心功能是什么?(不要试图设计所有功能)
- 用户量级?QPS?数据量?
- 读多还是写多?
- 一致性要求?(强一致 vs 最终一致)
- 延迟要求?(实时 vs 秒级 vs 分钟级)
Step 2: 高层架构(5 分钟)
- 画出核心组件和数据流
- 确定同步/异步边界
- 确定存储选型
Step 3: 核心设计(15 分钟)
- 数据模型(表结构 / Schema)
- API 设计(核心接口)
- 最难的那个问题怎么解(每个系统都有一个核心难点)
Step 4: 扩展与优化(5 分钟)
- 瓶颈在哪里?怎么扩展?
- 缓存加在哪一层?
- 如果数据量增长 10 倍怎么办?
每个系统的核心难点速查
| 系统 | 核心难点 |
|---|---|
| 电商-订单 | 分布式事务(下单扣库存扣余额) |
| 电商-库存 | 超卖(高并发扣减) |
| 电商-支付 | 幂等性 + 对账 |
| Feed 流 | 推拉模式选择 + 大V扩散 |
| 评论 | 树形结构高效查询 |
| IM | 消息有序性 + 百万级长连接 |
| 视频 | 转码 + CDN 分发 + 自适应码率 |
| 直播 | 超低延迟 + 弹幕广播 |
| 云文档 | 协同冲突解决(OT/CRDT) |
| 项目管理 | 灵活数据模型 + 自定义工作流 |
| 搜索 | 倒排索引 + 相关性排序 |
| 通知 | 多渠道路由 + 聚合 + 限流 |