系统设计案例
通过实际案例学习系统设计是最有效的方法。本章将深入分析几个经典的系统设计案例,帮助你理解如何将理论知识应用到实际设计中。
案例 1:设计 Twitter
需求分析
功能需求:
- 用户可以发布推文
- 用户可以关注其他用户
- 用户可以查看时间线(自己的推文和关注的人的推文)
非功能需求:
- 高可用性(99.99%)
- 低延迟(时间线加载 < 200ms)
- 高吞吐量(支持数亿用户)
容量估算
- 用户数:10 亿用户
- 日活用户:2 亿(20%)
- 平均每个用户:每天发布 2 条推文,关注 200 人
- 推文读取/写入比:100:1
- 峰值 QPS:
- 写:2 亿 × 2 / 86400 ≈ 4,630 QPS
- 读:4,630 × 100 ≈ 463,000 QPS
高层设计
用户 → 负载均衡 → API 服务器 → 推文服务
→ 用户服务
→ 时间线服务
→ 关注服务
详细设计
1. 数据模型
用户表:
- user_id (主键)
- username
- created_at
推文表:
- tweet_id (主键)
- user_id
- content
- created_at
关注关系表:
- user_id (主键)
- follower_id (主键)
- created_at
2. 时间线生成
方案 1:推模式(Fan-out on Write)
用户发布推文时,推送到所有关注者的时间线。
优点:
- 读取时间线快(O(1))
缺点:
- 写入慢(需要写入所有关注者的时间线)
- 大 V 用户发布推文时压力大
方案 2:拉模式(Fan-out on Read)
读取时间线时,实时聚合关注的人的推文。
优点:
- 写入快(O(1))
缺点:
- 读取慢(需要查询多个用户)
方案 3:混合模式(推荐)
- 普通用户:推模式
- 大 V 用户(关注者 > 1000):拉模式
3. 数据库设计
- 用户数据:SQL 数据库(MySQL)
- 推文数据:NoSQL 数据库(Cassandra)
- 时间线数据:Redis(缓存)+ Cassandra(持久化)
- 关注关系:图数据库(Neo4j)或 SQL 数据库
4. 缓存策略
- 用户信息缓存:Redis,TTL 1 小时
- 时间线缓存:Redis,TTL 5 分钟
- 热门推文缓存:Redis,TTL 1 小时
扩展性设计
- 水平扩展:无状态 API 服务器,可以轻松扩展
- 数据库分片:按 user_id 分片
- CDN:静态资源(图片、视频)
案例 2:设计 URL 短链服务
需求分析
功能需求:
- 将长 URL 转换为短 URL
- 短 URL 重定向到长 URL
- 统计短 URL 的访问次数
非功能需求:
- 高可用性
- 低延迟(重定向 < 100ms)
- 高吞吐量
容量估算
- 短 URL 生成:100M/天
- 短 URL 读取:100M × 100 = 10B/天
- 峰值 QPS:
- 写:100M / 86400 ≈ 1,157 QPS
- 读:1,157 × 100 ≈ 115,700 QPS
高层设计
用户 → 负载均衡 → API 服务器 → 短链生成服务
→ 重定向服务
→ 统计服务
详细设计
1. 短 URL 生成算法
方案 1:哈希 + Base62 编码
import hashlib
import base64
def generate_short_url(long_url):
hash_value = hashlib.md5(long_url.encode()).hexdigest()
# 取前 6 个字符
short_code = base64.b64encode(hash_value[:6].encode()).decode()[:6]
return short_code
方案 2:自增 ID + Base62 编码
def base62_encode(num):
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
result = []
while num > 0:
result.append(chars[num % 62])
num //= 62
return ''.join(reversed(result))
2. 数据库设计
URL 映射表:
- short_code (主键)
- long_url
- created_at
- expiration_date
统计表:
- short_code (主键)
- click_count
- last_accessed_at
3. 缓存策略
- URL 映射缓存:Redis,TTL 1 年
- 热门 URL:Redis,TTL 1 小时
扩展性设计
- 数据库分片:按 short_code 的第一个字符分片
- 缓存层:Redis 集群
- CDN:静态重定向页面
案例 3:设计分布式缓存
需求分析
功能需求:
- 存储键值对
- 支持 TTL(过期时间)
- 高可用性
非功能需求:
- 低延迟(< 1ms)
- 高吞吐量
- 可扩展性
高层设计
客户端 → 一致性哈希 → 缓存节点
详细设计
1. 一致性哈希
- 将节点和键映射到哈希环
- 键存储在顺时针方向的第一个节点
优点:
- 节点增减时,只需要迁移少量数据
- 负载均衡
2. 数据复制
- 主从复制:每个节点有多个副本
- 故障转移:主节点故障时,从节点接管
3. 缓存策略
- LRU:最近最少使用
- LFU:最不经常使用
- TTL:基于过期时间
案例 4:设计聊天系统
需求分析
功能需求:
- 一对一聊天
- 群聊
- 消息推送
- 在线状态
非功能需求:
- 低延迟(消息传递 < 100ms)
- 高可用性
- 消息持久化
高层设计
客户端 → WebSocket 服务器 → 消息队列 → 消息存储
→ 推送服务
详细设计
1. 消息传递
- WebSocket:实时双向通信
- 消息队列:解耦和削峰
- 消息存储:Cassandra(按时间分区)
2. 在线状态
- Redis:存储在线用户
- 心跳机制:定期更新在线状态
3. 消息推送
- APNs(iOS)
- FCM(Android)
- Web Push(Web)
总结
系统设计案例展示了:
- 需求分析:明确功能和非功能需求
- 容量估算:估算用户量、QPS、存储量
- 架构设计:高层设计和详细设计
- 技术选型:选择合适的数据库、缓存、消息队列
- 扩展性设计:考虑系统的扩展性
通过学习和实践这些案例,你将能够设计出优秀的系统。
系统设计部分的学习就到这里了!接下来,让我们学习行为面试,这是面试中的重要环节。
