系统设计常见“学习”题总结(付费)
title: 系统设计常见“学习”题总结
description: 涵盖系统设计基础理论、核心组件(负载均衡、网关、缓存、消息队列)、分布式技术(锁、事务、ID)及高频设计场景题(短链、秒杀、IM)。
category: 系统设计
icon: design
系统设计常见“学习”题总结
系统设计(System Design)是后端“学习”中区分度最高的部分,考察候选人的架构思维、技术广度及解决复杂问题的能力。本文整理了 30+ 道高频系统设计“学习”题,涵盖理论基础、组件原理及典型场景设计。
基础理论
1. 什么是 CAP 定理?
- Consistency(一致性):所有节点在同一时间具有相同的数据。
- Availability(可用性):保证每个请求不管成功或者失败都有响应。
- Partition Tolerance(分区容错性):系统中任意信息的丢失或失败不会影响系统的继续运作。
- 结论:分布式系统无法同时满足这三个指标,只能满足其中两个。通常在 CP(强一致)和 AP(高可用)之间做取舍。
📝 通俗解释
CAP 定理:鱼和熊掌不可兼得。
- C (一致性):所有节点数据时刻保持一致。
- A (可用性):只要活着就能响应(不管数据对不对)。
- P (分区容错):网线断了(分区)系统还能跑。
- 分布式系统 P 是必须的。所以只能在 C(强一致,但可能不可用)和 A(高可用,但数据可能暂时不一致)之间二选一。
2. 什么是 BASE 理论?
- Basically Available(基本可用):系统在出现故障时,允许损失部分可用性(如响应时间增加、功能降级)。
- Soft State(软状态):允许系统存在中间状态,而该中间状态不会影响系统整体可用性。
- Eventually Consistent(最终一致性):所有数据副本经过一段时间的同步后,最终能够达到一致的状态。
- 核心:是对 CAP 中 AP 的扩展,通过牺牲强一致性来获得高可用性。
📝 通俗解释
BASE 理论:退一步海阔天空。
- 既然强一致性(CP)太难,那就追求最终一致性。
- BA (基本可用):允许响应慢一点,或者降级(比如双11只让买不让退)。
- S (软状态):允许数据中间有个过渡状态(比如支付中)。
- E (最终一致性):只要最后数据是对的就行。
3. 水平扩展(Horizontal Scaling)和垂直扩展(Vertical Scaling)的区别?
- 垂直扩展(Scale Up):增强单机的性能(加 CPU、内存、磁盘)。有物理极限,成本呈指数级增长。
- 水平扩展(Scale Out):增加机器数量。理论上无极限,成本线性增长,但需要复杂的分布式架构支持。
📝 通俗解释
堆机器 vs 换强机。
- 垂直扩展:给电脑加内存、换更快的 CPU。容易遇到瓶颈,单机再强也有上限。
- 水平扩展:买更多的普通电脑,组成集群。互联网公司都选这个,人多力量大。
核心组件
4. 常见的负载均衡算法有哪些?
- 轮询(Round Robin):按顺序分配。
- 加权轮询(Weighted Round Robin):根据机器性能分配权重。
- 最小连接数(Least Connections):分配给当前连接数最少的服务器。
- 一致性哈希(Consistent Hashing):保证当节点增加或减少时,只有少量请求会重新分配,适合缓存场景。
- IP Hash:根据源 IP 计算 Hash,保证同一个 IP 请求落到同一台服务器(会话粘滞)。
📝 通俗解释
发牌员。
- 轮询:每人一张,公平。
- 加权:胖子(性能好)多发两张,瘦子(性能差)少发一张。
- 一致性哈希:适合缓存。机器挂了,只影响一小部分数据,不用全体大洗牌。
5. API 网关(Gateway)的作用是什么?
- 请求路由:将请求转发到对应的微服务。
- 身份认证/鉴权:统一处理登录、权限校验。
- 限流熔断:保护后端服务。
- 日志监控:统一记录请求日志。
- 协议转换:如 HTTP 转 RPC。
📝 通俗解释
大堂经理。
- 客户端(客人)来了,先见网关。
- 网关负责:验票(鉴权)、导流(路由)、限流(人太多了排队)、记账(日志)。
- 后面的微服务(厨师)只管做菜,不用管这些杂事。
6. 服务发现(Service Discovery)的两种模式?
- 客户端发现(Client-side):客户端直接查询注册中心,获取可用服务列表,自己做负载均衡(如 Eureka + Ribbon)。
- 服务端发现(Server-side):客户端请求负载均衡器(如 Nginx),由负载均衡器查询注册中心并转发请求。
📝 通俗解释
查电话本。
- 客户端发现:我自己有一本电话本(注册中心缓存),我要找“支付服务”,我自己翻电话本,直接打过去。
- 服务端发现:我打给总机(Nginx),说“转接支付服务”,总机帮我转过去。
7. 缓存的读写策略有哪些?
- Cache Aside(旁路缓存):最常用。读:先读缓存,命中返回;未命中读 DB 并写入缓存。写:先更新 DB,再删除缓存。
- Read/Write Through:应用只操作缓存,缓存组件负责同步 DB。
- Write Behind(异步回写):应用只更新缓存,缓存异步批量更新 DB(性能最高,但可能丢数据)。
📝 通俗解释
Cache Aside (旁路缓存):标准姿势。
- 读:先看缓存,没有就查库,查到写回缓存。
- 写:先改库,再删缓存(注意是删不是改)。让下一次读去重新加载,防止并发导致数据不一致。
分布式技术
8. 分布式 ID 生成方案有哪些?
- UUID:本地生成,无序,太长,不适合做主键(影响 B+ 树性能)。
- 数据库自增 ID:依赖 DB,性能受限,分库分表麻烦。
- 号段模式:从 DB 批量获取 ID 段,本地自增。
- 雪花算法(Snowflake):时间戳 + 机器 ID + 序列号,生成 Long 类型有序 ID,性能高,依赖机器时钟。
📝 通俗解释
发号器。
- UUID:太长且无序,数据库索引不喜欢。
- 数据库自增:单点瓶颈,分库分表麻烦。
- 雪花算法:最佳实践。利用时间戳和机器ID生成一个 64 位的长整数。有序、不重复、生成快。
9. 分布式锁的实现方式及优缺点?
- Redis(Redisson):基于
setnx+lua脚本。性能高,实现简单。缺点:主从切换可能丢锁(Redlock 解决)。 - Zookeeper:基于临时顺序节点。可靠性高,支持 Watch 机制。缺点:性能不如 Redis。
- 数据库:基于唯一索引。性能差,容易死锁,不推荐。
📝 通俗解释
抢坑位。
- Redis:用
setnx抢坑。速度快,但如果 Redis 挂了可能丢锁。- Zookeeper:排队。创建临时顺序节点。可靠性高,但性能比 Redis 慢。
- 结论:高性能选 Redis,高可靠选 Zookeeper。
10. 分布式事务的解决方案?
- 2PC(两阶段提交):强一致,性能差,容易阻塞。
- TCC(Try-Confirm-Cancel):应用层两阶段,灵活性高,但代码侵入大。
- Saga:长事务拆分为多个本地事务,有补偿机制。
- 本地消息表:利用本地事务写业务数据和消息表,轮询发送消息。
- 事务消息(RocketMQ):保证消息发送与本地事务执行的原子性,实现最终一致性。
📝 通俗解释
一起干或者都不干。
- 2PC:队长喊口号“预备”,大家回“好了”,队长喊“跑”。太慢,容易卡死。
- TCC:先试探(Try),没问题就确认(Confirm),有问题就撤销(Cancel)。要写三个方法,累死人。
- 事务消息:最常用。本地事务成功了,才发消息通知下游。下游保证一定消费成功(重试)。
11. 什么是 Session 共享问题?如何解决?
- Sticky Session(会话粘滞):Nginx 配置 IP Hash,同一用户总是访问同一台机器。缺点:单机故障会丢失 Session。
- Session Replication(会话复制):Tomcat 集群同步 Session。缺点:网络开销大,延迟高。
- Session Storage(集中存储):将 Session 存入 Redis(最推荐)。
📝 通俗解释
存包处。
- Session 复制:A 机器把 Session 抄送给 B、C、D... 机器。太累,带宽爆炸。
- 集中存储:大家都把 Session 存在 Redis(公共存包处)。不管请求打到哪台机器,都去 Redis 拿。
高可用与高并发
12. 常见的限流算法?
- 固定窗口:统计固定时间内的请求数。有临界问题。
- 滑动窗口:平滑统计。
- 漏桶算法(Leaky Bucket):平滑流出速率,无法处理突发流量。
- 令牌桶算法(Token Bucket):允许突发流量,互联网常用。
📝 通俗解释
限流算法:安检闸机。
- 固定窗口:一分钟放 60 个人。如果在第 59 秒来了 60 个,第 61 秒又来 60 个,那这两秒就进了 120 个,可能会把系统冲垮。
- 滑动窗口:把一分钟切成 60 份,每秒滑动一格,统计更精准。
- 漏桶:不管你怎么倒水,底下流出的速度是固定的。适合平滑流量。
- 令牌桶:按固定速度发令牌。想通过得先拿令牌。如果令牌桶里有存货,可以允许突发流量(一次拿好几个)。
13. 什么是数据库分库分表?
- 垂直分库:按业务拆分(用户库、订单库)。
- 垂直分表:按列拆分(大字段单独拆表)。
- 水平分库/分表:按数据行拆分(如
user_id % 10),解决单表数据量过大问题。
📝 通俗解释
分库分表:大柜子拆分。
- 垂直分:把衣服和书分开两个柜子放(按业务拆)。或者把书的目录和正文分开(大字段拆分)。
- 水平分:书太多了,一个柜子装不下。买 10 个一样的柜子,按编号(ID)模 10,把书分散到这 10 个柜子里。
14. 读写分离的原理和延迟问题?
- 原理:主库写,从库读,通过 Binlog 同步。
- 延迟解决:
- 强制读主库(关键业务)。
- 延迟双删。
- 判断主从延迟时间。
📝 通俗解释
读写分离:主仆模式。
- 原理:主人(Master)负责写,仆人(Slave)负责抄(同步)。客人来了,大部分只是参观(读),找仆人就行;要改东西,找主人。
- 延迟:仆人抄写需要时间。如果刚改完就马上问仆人,可能仆人还没抄到。
- 解决:重要的事直接问主人(强制读主),或者等一会儿再问。
场景设计题(Design Cases)
15. 如何设计一个短链接系统(URL Shortener)?
- 核心:长 URL 映射到短 Key。
- 算法:Hash(MD5)截取或自增 ID 转 62 进制(0-9, a-z, A-Z)。
- 存储:KV 数据库(Redis/HBase)或 MySQL(id, long_url, short_url)。
- 冲突处理:如果 Hash 冲突,加盐重试。
- 重定向:301(永久)还是 302(临时)?通常 302 以便统计数据。
📝 通俗解释
存包牌。
- 映射:把长的 URL(大衣)存起来,换一个短的 Key(手牌)。
- 重定向:下次拿手牌来,系统查一下,把大衣还给你(302 跳转到原 URL)。
- 生成:可以用 Hash(可能会冲突),也可以用发号器(62 进制)。
16. 如何设计一个唯一 ID 生成器?
- 参考 Snowflake 算法:1 位符号位 + 41 位时间戳 + 10 位机器 ID + 12 位序列号。
- 考虑时钟回拨问题(等待或借用未来时间)。
- 高可用部署(ZooKeeper 管理机器 ID)。
📝 通俗解释
不重复的号码牌。
- 核心是不重复。
- Snowflake:利用时间 + 机器号 + 序列号。
- 时钟回拨:如果时间倒流了,ID 可能会重复。解决办法是等待或者报错。
17. 如何设计一个 KV 存储系统(类似 Redis)?
- 数据结构:Hash Map, Skip List。
- 内存管理:Slab Allocation 减少碎片。
- 持久化:WAL (Write Ahead Log), Snapshot。
- 高可用:Replication, Partitioning (Sharding)。
📝 通俗解释
造一个 Redis。
- 内存:为了快,数据放内存。用 HashMap 存。
- 持久化:内存断电就没了,所以要记日记(WAL),或者定期拍照(Snapshot)。
- 高可用:多搞几台备份(Replication),或者分片存(Sharding)。
18. 如何设计一个网络爬虫(Crawler)?
- 组件:URL Frontier(待抓取队列), DNS Resolver, Fetcher(下载器), Parser(解析器), Storage。
- 去重:Bloom Filter。
- 礼貌性:Robots.txt, Rate Limiting。
- 分布式:Hash URL 到不同节点。
📝 通俗解释
图书管理员。
- 待抓队列:采购清单(URL Frontier)。
- 下载器:去书店把书买回来(Fetcher)。
- 解析器:看书,提取内容,发现新书名(URL)加到清单里。
- 去重:买过的书别再买了(Bloom Filter)。
- 礼貌:别跑太快把书店门槛踩破了(Rate Limiting)。
19. 如何设计一个通知系统(Notification System)?
- 支持渠道:SMS, Email, Push, App 内信。
- 架构:服务 -> 消息队列 -> 发送 Worker -> 第三方服务。
- 功能:模板管理、优先级控制、重试机制、免打扰设置、用户偏好。
📝 通俗解释
群发助手。
- 这里的核心是解耦。
- 业务方只要发个消息给 MQ。
- 通知系统从 MQ 拿消息,根据类型(短信/邮件/App推送)调用不同的服务商接口。
- 还要考虑防骚扰(一天只能发 3 条)和重试(发失败了再试)。
20. 如何设计一个 IM 聊天系统?
- 协议:WebSocket, TCP, UDP (音视频)。
- 模型:用户在线状态管理(Redis)、消息存储(写扩散 vs 读扩散)。
- 写扩散(Timeline):发消息给每个好友的收件箱(适合好友少)。
- 读扩散(Mailbox):发消息到自己的发件箱,好友拉取(适合群聊)。
📝 通俗解释
传纸条。
- WebSocket:建立一条长长的管道,随时可以丢纸条过去。
- 写扩散:我写一张条子,复印 10 份,塞到 10 个朋友的抽屉里。读取快,发送慢。
- 读扩散:我写一张条子,贴自己脑门上。朋友们想看,自己来我这儿抄。发送快,读取慢。
21. 如何设计一个限流器(Rate Limiter)?
- 模式:单机限流(Guava RateLimiter) vs 分布式限流(Redis Lua)。
- 配置:动态规则配置中心。
- 算法:令牌桶(Token Bucket)。
📝 通俗解释
限流器。
- 单机:Guava RateLimiter。
- 分布式:Redis + Lua 脚本。
- 就像地铁站的闸机,一秒钟只放行 5 个人。
22. 如何设计一个排行榜(Leaderboard)?
- 核心:Redis ZSet (Sorted Set)。
- 操作:
zadd(更新分数),zrevrange(获取排名)。 - 大数据量:分桶策略(按分数段或 Hash),最后汇总。
📝 通俗解释
Redis ZSet。
- Redis 的 ZSet 天生就是做排行榜的。
zadd加分,zrevrange拿前 10 名。- 如果人太多(几亿),就把他们分到不同的桶里(比如按分数段分),每个桶排好序,最后再合并。
23. 如何设计一个秒杀系统(Flash Sale)?
- 前端:按钮置灰、静态资源 CDN、限流。
- 网关:IP 限流、用户限流、黑名单。
- 后端:
- 预扣库存(Redis Lua)。
- 异步下单(MQ)。
- 数据库乐观锁(
update stock set num = num - 1 where num > 0)。
- 防刷:验证码、隐藏 URL。
📝 通俗解释
防崩。
- 前端:按钮变灰,不让疯狂点。
- CDN:静态资源(图片、JS)存 CDN,别打到服务器。
- Redis:扣库存全在 Redis 里做,数据库扛不住。
- MQ:削峰。下单请求先扔 MQ,慢慢消费写库。
24. 如何设计一个搜索自动补全(Autocomplete)?
- 数据结构:Trie 树(前缀树)。
- 优化:在 Trie 节点缓存 Top K 热门词。
- 存储:Elasticsearch (Completion Suggester), Redis, Cassandra。
📝 通俗解释
Trie 树 (前缀树)。
- 也就是输入
a提示apple,输入ap提示app。- 用 Trie 树 存这些词,查找速度极快。
- 也可以用 Elasticsearch 的自动补全功能。
25. 如何设计 YouTube/Netflix(视频流媒体)?
- 上传:分片上传,断点续传。
- 转码:DAG 任务流,转成不同分辨率/格式(HLS, DASH)。
- 存储:对象存储(S3)。
- 分发:CDN。
- 推荐:协同过滤、深度学习模型。
📝 通俗解释
流媒体。
- 分片:把大视频切成几秒钟一段的小切片(ts 文件)。
- CDN:把这些小切片推到全国各地的 CDN 节点。
- 自适应:网速好就下载高清切片,网速差就下载标清切片。
26. 如何设计 Google Drive(网盘)?
- 分块:大文件切块(Block),计算 Hash 去重(秒传)。
- 同步:增量同步,版本控制。
- 存储:元数据存 DB,文件块存对象存储。
- 传输:长连接,断点续传。
📝 通俗解释
秒传。
- 块存储:把大文件切成小块。
- 哈希去重:上传前先算一下哈希值,如果服务器上已经有这个块了,就不用传了(秒传)。
- 增量同步:改了文件,只传改动的那一块。
27. 如何设计 Twitter(Feed 流)?
- 推模式(Push):发推时写入所有粉丝的 Feed(写扩散),读取快,发推慢。
- 拉模式(Pull):读取时拉取关注人的推文并聚合(读扩散),发推快,读取慢。
- 混合模式:大 V 发推用拉模式,普通人发推用推模式。
📝 通俗解释
推拉结合。
- 推模式:适合粉丝少的。我发微博,直接塞到你列表里。
- 拉模式:适合大 V。大 V 发微博,不塞给几千万粉丝(太慢)。粉丝来看列表时,去大 V 的发件箱里拉取。
28. 系统设计“学习”的“套路”?
- 需求分析:明确功能需求(Functional)和非功能需求(Non-functional,如 QPS、延迟、存储量)。
- 估算:计算 QPS、带宽、存储容量。
- API 设计:定义接口。
- 数据库设计:Schema 设计,SQL vs NoSQL。
- 概要设计:画出核心组件图。
- 详细设计:深入关键组件(如缓存、分库分表)。
- 瓶颈分析:单点故障、扩展性问题。
📝 通俗解释
八股文。
- 系统设计“学习”是有套路的:
- 问清楚需求(QPS 多少?)。
- 算一下(要多少机器?硬盘要多大?)。
- 画个大概架构图。
- 细化核心组件(Redis 怎么用?DB 怎么分?)。
- 找茬(哪里会挂?怎么优化?)。
29. 什么时候用 SQL,什么时候用 NoSQL?
- SQL:结构化数据、强一致性要求(事务)、复杂关联查询(Join)。
- NoSQL:非结构化/半结构化数据、高并发读写、海量数据、灵活 Schema、高可扩展性。
📝 通俗解释
看菜吃饭。
- SQL:钱、订单、用户信息。这种结构固定,必须要事务,不能丢的数据。
- NoSQL:日志、评论、点赞、大文本。这种数据量大,结构不固定,写得贼快。
30. 如何保证缓存和数据库的一致性?
- 强一致性:很难,通常追求最终一致性。
- 方案:
- 延时双删。
- 监听 Binlog 异步删除缓存(Canal)。
- 增加重试机制。
📝 通俗解释
最终一致性。
- 想要缓存和数据库每时每刻都一样,很难,会严重拖慢性能。
- 只要保证最终一样就行。
- Canal:伪装成 MySQL 的从库,监听 Binlog,发现数据库变了,就自动去删缓存。这种方式代码侵入最小。
31. 如何设计一个秒杀系统的限流策略?
- 分层限流:
- Nginx:IP 限流(限制单 IP 访问频率)。
- 网关:Token Bucket 算法,限制总 QPS。
- 应用层:Semaphore / Guava RateLimiter,限制单机并发数。
- 黑名单:识别异常流量(如爬虫、脚本),直接拒绝。
📝 通俗解释
漏斗。
- Nginx:第一层。按 IP 限,比如一个 IP 一秒只能访 10 次。
- 网关:第二层。按总流量限,比如系统一秒只能扛 10 万次。
- 应用:第三层。按业务限,比如每个商品一秒只能卖 100 个。
32. 如何设计一个高可用的分布式任务调度系统?
- 选型:XXL-JOB, Elastic-Job, Quartz (Cluster)。
- 核心:
- 调度中心:HA 部署(多节点),基于数据库锁或 Zookeeper 选主。
- 执行器:自动注册到调度中心,支持分片广播。
- 幂等性:任务执行需保证幂等。
- 失败重试:记录日志,告警,自动/人工重试。
📝 通俗解释
包工头。
- 调度中心:包工头。负责派活,还负责盯着(HA)。如果一个包工头倒了,另一个立马顶上。
- 执行器:工人。负责干活。
- 幂等:任务可能会重复执行(比如网络超时),所以业务逻辑必须保证“做一次”和“做十次”结果一样。
33. 如何设计一个红包雨系统?
- 预分配:红包金额提前计算好,存入 Redis List。
- 抢红包:用户请求 -> Redis Pop(原子操作) -> 抢到则异步写入数据库。
- 并发:前端拦截大部分无效请求(倒计时、点击频率限制)。
- 兜底:如果 Redis 挂了,降级为数据库乐观锁(性能差,慎用)。
📝 通俗解释
撒钱。
- 预分配:红包不是实时算的,是提前算好放在 Redis 队列里的。
- 抢红包:就是从 Redis 队列里
pop一个出来。原子操作,不会超发。- 异步入库:抢到了,先把名字记下来(异步),慢慢写到数据库里。
34. 如何设计一个千万级用户的积分系统?
- 表结构:用户积分表(user_id, total_score),积分流水表(log_id, user_id, type, score, create_time)。
- 分库分表:按 user_id Hash 分表。
- 热点账户:如果某个用户积分变动极其频繁(如大 V),考虑 Redis 缓存 + 异步刷盘。
- 一致性:积分变动必须有流水,通过事务或 MQ 保证一致性。
📝 通俗解释
记账。
- 积分就是钱。
- 流水:每次加分减分,都要记一笔流水。
- 分库分表:用户太多,按用户 ID 拆分到不同的表里。
- 热点:如果有大 V 积分变动太快,先在 Redis 里记,攒一攒再刷到数据库。
35. 如何设计一个支持多租户(Multi-Tenant)的 SaaS 系统?
- 隔离级别:
- 共享库共享表:tenant_id 字段区分(成本低,隔离性差)。
- 共享库独立表:每个租户一张表(成本中,隔离性中)。
- 独立库:每个租户一个数据库(成本高,隔离性好,适合大客户)。
- 动态数据源:根据请求中的 tenant_id 动态切换 JDBC 连接。
📝 通俗解释
二房东。
- SaaS:一套代码卖给好多公司用。
- 隔离:
- 穷方案:大家都用一张表,加个
tenant_id区分。便宜,但数据容易混。- 富方案:一个公司一个数据库。安全,但贵,运维麻烦。
总结:系统设计没有标准答案,关键在于权衡(Trade-off)。“学习”时要多与“学习”官沟通,明确约束条件,展示你的分析过程。
