系统设计实战题(System Design)

系统设计是架构师面试的重头戏。本模块包含20+个完整的系统设计题,每道题都是一个真实的业务场景,考察候选人将理论知识组装成完整方案的能力。每道题包含需求分析、架构设计、核心难点、数据模型、容量评估等完整维度。


难度标记

  • 🔴 专家(Expert):需要深入的实战经验
  • ⚫ 大师(Master):开放性设计题,考察架构哲学和权衡能力

一、经典系统设计(1-10题)

1. ⚫ 设计一个短链系统(URL Shortener)

答:短链系统看似简单,但涉及高并发读写、分布式ID生成、缓存策略等核心问题。

需求分析:

  • 功能需求:长链转短链、短链跳转长链、自定义短链、过期时间、访问统计
  • 非功能需求:日均生成1亿短链、读写比100:1、跳转延迟<50ms、99.99%可用性

容量评估:

  • 写QPS:1亿/86400 ≈ 1200,峰值 ≈ 6000
  • 读QPS:1200 × 100 = 12万,峰值 ≈ 60万
  • 存储:每条记录约500B,5年 = 1亿×365×5×500B ≈ 91TB
  • 短链长度:62^7 ≈ 3.5万亿,足够使用

核心设计:

  1. 短链生成算法:

    • 方案A:自增ID + Base62编码(简单,但ID可预测)
    • 方案B:MD5/MurmurHash取前7位(可能冲突,需要冲突处理)
    • 方案C:预生成ID池(Snowflake/Leaf)+ Base62(推荐)
    • 冲突处理:布隆过滤器快速判断 + DB唯一索引兜底
  2. 数据模型:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    CREATE TABLE short_url (
    id BIGINT PRIMARY KEY,
    short_code VARCHAR(10) UNIQUE,
    long_url VARCHAR(2048) NOT NULL,
    user_id BIGINT,
    expire_time DATETIME,
    created_at DATETIME,
    INDEX idx_short_code(short_code)
    );
  3. 架构设计:

    1
    2
    3
    4
    5
    客户端 → CDN(缓存热门短链的301跳转)
    → Nginx(负载均衡)
    → 短链服务(无状态,水平扩展)
    → Redis缓存(热门短链,命中率>95%)
    → MySQL集群(分库分表,按short_code哈希)
  4. 关键决策:

    • 301 vs 302跳转:301永久重定向(浏览器缓存,减少服务端压力),302临时重定向(每次都经过服务端,便于统计)。推荐302,因为需要统计
    • 缓存策略:LRU缓存热门短链,写入时同步写缓存
    • 分库分表:按short_code哈希分16库×16表 = 256张表
  5. 访问统计:

    • 异步写入:跳转时发送Kafka消息,消费者异步写入统计表
    • 统计维度:PV/UV、地域、设备、Referer
    • 实时统计:Redis HyperLogLog统计UV

面试追问点:

  • 如何防止恶意刷短链?(限流 + 验证码)
  • 如何处理热点短链?(多级缓存 + CDN)
  • 短链过期如何清理?(定时任务 + 懒删除)

2. ⚫ 设计一个秒杀系统

答:秒杀是高并发场景的极端案例,核心挑战是在极短时间内处理海量请求,同时保证数据一致性。

需求分析:

  • 商品数量:1000件
  • 预计参与人数:100万
  • 秒杀持续时间:瞬间(<10秒内完成)
  • 核心要求:不超卖、不少卖、高可用、公平

流量分析:

  • 瞬时QPS:100万/10秒 = 10万QPS
  • 实际有效请求:远超库存,99.9%的请求注定失败

核心设计思路:层层过滤,将请求漏斗化

1
2
3
4
100万请求 → 前端限流(50%过滤)→ 50万
→ Nginx限流(80%过滤)→ 10万
→ 应用层库存校验(99%过滤)→ 1000
→ Redis原子扣减 → 成功下单

分层设计:

  1. 前端层:

    • 按钮置灰 + 倒计时(防止提前点击)
    • 点击后按钮禁用(防止重复提交)
    • 前端加随机延迟(打散请求)
    • 答题/验证码(削峰 + 防机器人)
  2. 接入层(Nginx):

    • 限流:令牌桶限流,超过阈值直接返回”活动太火爆”
    • 负载均衡:一致性哈希(同一用户打到同一台机器,利于本地缓存)
  3. 应用层:

    • 本地缓存库存标记(库存为0后直接拒绝,不访问Redis)
    • 用户去重(Redis Set记录已参与用户)
    • 请求排队(内存队列,控制并发)
  4. Redis层(核心):

    1
    2
    3
    4
    5
    -- 原子扣减库存
    local stock = redis.call('GET', KEYS[1])
    if tonumber(stock) <= 0 then return -1 end
    redis.call('DECR', KEYS[1])
    return 1
    • 库存预热:秒杀开始前将库存加载到Redis
    • 原子操作:Lua脚本保证扣减的原子性
    • 扣减成功后发送MQ消息,异步创建订单
  5. 订单层:

    • MQ异步下单:削峰填谷
    • 订单超时未支付自动取消(延迟队列)
    • 取消后库存回补

关键问题处理:

  • 超卖:Redis原子扣减 + 数据库乐观锁兜底
  • 少卖:订单超时回补 + 定时对账
  • 热点Key:Redis集群 + 本地缓存 + 库存分片(1000件分到10个Key,每个100件)
  • 机器人:风控系统 + 设备指纹 + 行为分析

3. ⚫ 设计一个即时通讯系统(IM)

答:IM系统是长连接、实时性、消息可靠性的综合考验。

需求分析:

  • 用户规模:1000万DAU
  • 消息类型:文本、图片、语音、视频、文件
  • 功能:单聊、群聊(最大500人)、已读回执、消息撤回、离线消息
  • 非功能:消息延迟<200ms、消息不丢失、消息有序

容量评估:

  • 日消息量:1000万 × 50条/人 = 5亿条
  • 峰值QPS:5亿/86400 × 10 ≈ 58000
  • 长连接数:1000万同时在线
  • 单机长连接:约50万(需要20+台连接服务器)

核心架构:

1
2
3
4
客户端 → LB → 连接层(WebSocket/TCP长连接)
→ 逻辑层(消息处理、群组管理)
→ 存储层(消息存储、索引)
→ 推送层(离线推送APNs/FCM)

关键设计:

  1. 连接管理:

    • 协议选择:WebSocket(Web端)、TCP自定义协议(移动端)
    • 连接服务器:Netty实现,单机50万连接
    • 连接路由:用户→连接服务器的映射存储在Redis
    • 心跳机制:客户端30秒心跳,服务端90秒超时断开
  2. 消息模型:

    1
    2
    3
    消息ID:Snowflake生成(全局有序)
    消息流程:
    A发送 → 服务端收到(ACK1)→ 存储 → 推送给B → B收到(ACK2)→ 通知A已送达(ACK3)
    • 三次确认保证消息可靠性
    • 消息ID用于去重和排序
  3. 单聊消息存储(写扩散 vs 读扩散):

    • 写扩散:每个用户一个收件箱,发送时写入接收方收件箱
    • 读扩散:消息只存一份,读取时聚合
    • 单聊推荐写扩散(实现简单,读取快)
  4. 群聊消息存储:

    • 群消息只存一份(群消息表)
    • 每个用户维护一个已读偏移量(群成员表中的last_read_msg_id)
    • 拉取未读:WHERE msg_id > last_read_msg_id
  5. 消息存储方案:

    • 近期消息(3个月):MySQL分库分表(按用户ID哈希)
    • 历史消息:HBase/TiDB(海量存储)
    • 消息索引:Elasticsearch(全文搜索)
    • 媒体文件:对象存储(OSS/S3)
  6. 离线消息:

    • 用户不在线时,消息存入离线消息队列
    • 同时触发APNs/FCM推送通知
    • 用户上线后拉取离线消息

面试追问点:

  • 如何保证消息的顺序性?(单聊:发送方序号;群聊:服务端序号)
  • 万人群怎么处理?(读扩散 + 分页拉取 + 增量同步)
  • 如何实现消息撤回?(发送撤回指令,客户端删除本地消息)
  • 多端同步怎么做?(每个设备维护同步位点,增量拉取)

4. ⚫ 设计一个Feed流系统(朋友圈/微博)

答:Feed流是社交产品的核心,核心挑战是在海量数据下实现高效的信息分发。

需求分析:

  • 用户规模:5000万DAU
  • 关注关系:平均每人关注200人
  • 发布频率:平均每人每天0.5条
  • 核心功能:发布动态、查看Feed流(按时间倒序)、点赞、评论

容量评估:

  • 日发布量:5000万 × 0.5 = 2500万条
  • Feed流读取QPS:5000万 × 10次/天 / 86400 ≈ 5800,峰值 ≈ 30000
  • 写QPS:2500万 / 86400 ≈ 290,峰值 ≈ 1500

核心问题:推模式 vs 拉模式 vs 推拉结合

  1. 推模式(写扩散):

    • 用户发布时,将内容写入所有粉丝的Feed收件箱
    • 优点:读取快(直接读收件箱)
    • 缺点:大V发布时写放大严重(1000万粉丝 = 1000万次写入)
    • 适合:粉丝数少的普通用户
  2. 拉模式(读扩散):

    • 用户查看Feed时,实时聚合关注人的最新内容
    • 优点:写入简单
    • 缺点:读取慢(需要聚合200个关注人的内容)
    • 适合:大V
  3. 推拉结合(推荐方案):

    • 普通用户(粉丝<1万):推模式
    • 大V(粉丝>1万):拉模式
    • 用户读取Feed时:收件箱内容 + 实时拉取关注的大V内容 → 合并排序

数据模型:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
-- 发布表(所有动态)
CREATE TABLE feed_post (
id BIGINT PRIMARY KEY,
user_id BIGINT,
content TEXT,
media_urls JSON,
created_at DATETIME,
INDEX idx_user_time(user_id, created_at DESC)
);

-- 收件箱(推模式写入)
CREATE TABLE feed_inbox (
user_id BIGINT,
post_id BIGINT,
author_id BIGINT,
created_at DATETIME,
PRIMARY KEY(user_id, post_id),
INDEX idx_user_time(user_id, created_at DESC)
);

Feed流生成流程:

1
2
3
4
5
6
7
8
9
10
11
12
用户A发布动态
→ 写入feed_post表
→ 查询A的粉丝列表
→ 粉丝数<1万:异步写入每个粉丝的feed_inbox(MQ)
→ 粉丝数≥1万:不写扩散,粉丝读取时实时拉取

用户B查看Feed流
→ 从feed_inbox读取推送的内容
→ 查询B关注的大V列表
→ 从feed_post拉取大V的最新内容
→ 合并排序,返回前20条
→ 缓存结果到Redis(下次翻页直接读缓存)

性能优化:

  • Redis缓存:每个用户的Feed流缓存最近200条ID
  • 分页:基于游标分页(created_at + post_id),避免深分页
  • 预加载:用户打开App时预加载Feed流

5. ⚫ 设计一个分布式文件存储系统

答:分布式文件存储是基础设施级别的系统设计题,考察对存储、网络、一致性的深入理解。

需求分析:

  • 存储容量:PB级
  • 文件大小:KB到GB不等
  • 核心功能:上传、下载、删除、元数据查询
  • 非功能:高可用(99.99%)、数据不丢失(11个9持久性)、高吞吐

架构设计(参考GFS/HDFS):

1
2
客户端 → NameNode(元数据服务)
→ DataNode集群(数据存储)
  1. NameNode(元数据管理):

    • 维护文件→块的映射关系
    • 维护块→DataNode的映射关系
    • 高可用:Active-Standby + ZooKeeper选主
    • 元数据存储:内存 + EditLog(WAL)+ 定期Checkpoint
  2. DataNode(数据存储):

    • 存储实际的数据块(默认64MB/128MB一个块)
    • 定期向NameNode发送心跳和块报告
    • 数据校验:每个块存储Checksum
  3. 数据写入流程:

    1
    2
    3
    4
    客户端 → NameNode请求写入
    ← 返回3个DataNode地址(副本放置策略)
    → 数据流水线写入:Client→DN1→DN2→DN3
    → 所有副本写入成功后,通知NameNode
  4. 副本策略:

    • 默认3副本
    • 第1副本:客户端所在机架的节点
    • 第2副本:不同机架的节点
    • 第3副本:第2副本同机架的不同节点
    • 保证机架级容灾
  5. 小文件优化:

    • 问题:大量小文件会导致NameNode内存压力
    • 方案:小文件合并存储(类似HBase的HFile)
    • 或使用对象存储方案(类似S3),元数据存数据库
  6. 纠删码(Erasure Coding):

    • 3副本存储开销200%
    • 纠删码(如RS(6,3))存储开销50%,同样容忍3节点故障
    • 适合冷数据,降低存储成本

面试追问点:

  • 如何处理DataNode故障?(NameNode检测心跳超时,自动复制副本到其他节点)
  • 如何处理NameNode单点?(HA方案:Active-Standby + 共享EditLog)
  • 大文件上传如何断点续传?(分块上传,记录已上传的块)

6. ⚫ 设计一个分布式任务调度系统

答:分布式任务调度是后端基础设施的核心组件。

需求分析:

  • 支持Cron定时任务和一次性任务
  • 任务量:10万+定时任务
  • 核心要求:准时触发、不重复执行、失败重试、任务依赖

架构设计:

1
2
3
4
5
6
7
8
9
┌─────────────┐     ┌──────────────┐     ┌──────────────┐
│ 管理控制台 │────→│ 调度中心 │────→│ 执行器集群 │
│ 任务CRUD │ │ 触发/分片/路由 │ │ 执行任务 │
└─────────────┘ └──────────────┘ └──────────────┘

┌──────┴──────┐
│ MySQL/Redis │
│ 任务元数据 │
└─────────────┘

核心设计:

  1. 调度中心高可用:

    • 多节点部署,通过数据库行锁或Redis分布式锁竞争调度权
    • 任务分片:将任务按ID范围分配给不同调度节点
    • 故障转移:节点宕机后,其他节点接管
  2. 触发机制:

    • 时间轮算法(HashedWheelTimer):高效的定时触发
    • 预读:提前5秒从DB加载即将触发的任务到内存
    • Cron解析:计算下次触发时间
  3. 执行策略:

    • 单机串行 / 单机并行 / 广播执行 / 分片执行
    • 分片执行:大任务拆分为多个子任务,分发到多个执行器并行处理
  4. 失败处理:

    • 重试策略:固定间隔/指数退避,最大重试次数
    • 故障转移:执行器超时后转移到其他执行器
    • 告警:失败后通知相关人员
  5. 任务依赖(DAG):

    • 支持任务间的依赖关系(A完成后触发B)
    • DAG调度:拓扑排序确定执行顺序
    • 子任务全部完成后触发父任务的回调

对标产品: XXL-Job、ElasticJob、Airflow

7. ⚫ 设计一个实时搜索引擎

答:搜索引擎是信息检索的核心系统,考察倒排索引、分布式检索、相关性排序等知识。

需求分析:

  • 数据量:10亿文档
  • 查询QPS:1万
  • 延迟要求:P99 < 200ms
  • 功能:全文搜索、过滤、排序、聚合、高亮、自动补全

架构设计(参考Elasticsearch):

  1. 数据模型:

    • 索引(Index)→ 分片(Shard)→ 段(Segment)
    • 每个分片是一个独立的Lucene索引
    • 分片数在创建时确定,不可修改(需要Reindex)
  2. 倒排索引:

    1
    2
    3
    4
    5
    6
    7
    8
    文档1: "Java并发编程实战"
    文档2: "Java虚拟机原理"

    倒排索引:
    Java → [文档1, 文档2]
    并发 → [文档1]
    编程 → [文档1]
    虚拟机 → [文档2]
  3. 写入流程:

    • 文档写入 → 分词 → 构建倒排索引 → 写入内存Buffer
    • Buffer满或定时 → Refresh到Segment(近实时搜索,默认1秒)
    • Segment定期Merge(合并小段为大段,清理删除文档)
    • Translog保证数据不丢失(类似WAL)
  4. 查询流程(Scatter-Gather):

    1
    2
    3
    4
    5
    6
    协调节点收到查询
    → 将查询分发到所有相关分片
    → 每个分片本地查询,返回Top N的文档ID和分数
    → 协调节点合并排序,取全局Top N
    → 根据文档ID获取完整文档内容
    → 返回结果
  5. 相关性排序:

    • BM25算法(TF-IDF的改进版)
    • 自定义评分:Function Score Query
    • 向量搜索:kNN + HNSW索引(语义搜索)
  6. 性能优化:

    • 路由:相关文档路由到同一分片,减少跨分片查询
    • 缓存:Query Cache、Request Cache、Fielddata Cache
    • 预热:常用查询预热缓存

8. ⚫ 设计一个分布式配置中心

答:配置中心是微服务架构的基础设施。

需求分析:

  • 配置数量:10万+配置项
  • 客户端数量:1万+服务实例
  • 核心要求:实时推送、灰度发布、版本管理、权限控制

架构设计:

  1. 数据模型:

    1
    2
    Namespace(环境)→ Group(应用)→ Key-Value(配置项)
    每个配置项有版本号,支持历史回溯
  2. 配置推送机制:

    • 方案A:客户端轮询(简单但延迟高)
    • 方案B:长轮询(Nacos方案,客户端发起长连接,服务端有变更时立即返回)
    • 方案C:WebSocket/gRPC Stream(实时性最好)
    • 推荐:长轮询(兼顾实时性和兼容性)
  3. 长轮询实现:

    1
    2
    3
    4
    5
    客户端发起请求,携带当前配置的MD5
    → 服务端比较MD5
    → 相同:Hold住请求(最长30秒)
    → 不同:立即返回变更的配置
    → 配置变更时:唤醒所有Hold的请求
  4. 灰度发布:

    • 按IP灰度:指定IP列表使用新配置
    • 按比例灰度:10%的实例使用新配置
    • 按标签灰度:指定标签的实例使用新配置
  5. 高可用:

    • 服务端集群:多节点 + 数据库存储
    • 客户端本地缓存:服务端不可用时使用本地缓存
    • 本地文件快照:客户端定期将配置写入本地文件

对标产品: Nacos、Apollo、Spring Cloud Config

9. ⚫ 设计一个权限系统(通用RBAC平台)

答:权限系统是企业级应用的基础设施,参考11-project.md中IAM部分的深入设计。

核心架构要点:

  1. 权限模型:RBAC + ABAC混合

    • 粗粒度:RBAC(用户→角色→权限)
    • 细粒度:ABAC(基于属性的动态权限)
    • 数据权限:基于组织架构的行级权限
  2. 性能设计:

    • 每个请求都需要鉴权,必须高性能
    • 多级缓存:JWT本地解析 → 本地缓存 → Redis → DB
    • 权限变更通过MQ广播,各服务异步更新缓存
  3. SDK设计:

    • 提供Java/Go/Python SDK
    • SDK内置缓存和降级逻辑
    • 注解式鉴权:@RequirePermission("user:read")
  4. 多租户:

    • 每个租户独立的角色和权限体系
    • 租户管理员可以自定义角色
    • 数据隔离:tenant_id贯穿所有表

10. ⚫ 设计一个全局唯一ID生成系统

答:分布式ID是分布式系统的基础问题。

需求分析:

  • 全局唯一、趋势递增、高性能(>10万/秒)、高可用

方案对比:

方案 优点 缺点
UUID 简单,无依赖 无序,存储大,索引性能差
数据库自增 简单,有序 性能瓶颈,单点故障
数据库号段 性能好 实现复杂
Redis INCR 性能好 依赖Redis
Snowflake 性能极高,趋势递增 时钟回拨问题
Leaf 号段+Snowflake双模式 需要部署服务

Snowflake算法详解:

1
2
0 | 41位时间戳 | 10位机器ID | 12位序列号
| 69年 | 1024台机器 | 4096/毫秒
  • 单机QPS:409.6万/秒
  • 时钟回拨处理:等待时钟追上 / 使用扩展位记录回拨次数

号段模式(Leaf-Segment):

1
2
3
4
5
6
CREATE TABLE leaf_alloc (
biz_tag VARCHAR(128) PRIMARY KEY,
max_id BIGINT NOT NULL,
step INT NOT NULL, -- 每次获取的号段大小
update_time DATETIME
);
  • 每次从DB获取一个号段(如1000-2000)
  • 在内存中分配,用完再取下一个号段
  • 双Buffer:当前号段用到10%时,异步加载下一个号段

二、进阶系统设计(11-20题)

11. ⚫ 设计一个电商订单系统

答:订单系统是电商的核心,涉及分布式事务、状态机、幂等性等关键问题。

核心设计:

  1. 订单状态机:

    1
    2
    3
    待支付 → 已支付 → 待发货 → 已发货 → 已签收 → 已完成
    ↓ ↓ ↓
    已取消 退款中 → 已退款 售后中
  2. 下单流程(分布式事务):

    1
    创建订单 → 扣减库存 → 扣减优惠券 → 创建支付单
    • 方案:基于MQ的最终一致性
    • 订单服务创建订单(本地事务)
    • 发送MQ消息通知库存服务扣减
    • 库存扣减失败:补偿回滚订单
  3. 幂等设计:

    • 订单号作为幂等键
    • 支付回调幂等:支付单号 + 状态校验
    • 数据库唯一索引兜底
  4. 订单超时取消:

    • 方案A:延迟队列(RocketMQ延迟消息)
    • 方案B:Redis过期事件
    • 方案C:定时任务扫描(兜底)
    • 推荐:延迟队列 + 定时任务兜底
  5. 分库分表:

    • 按用户ID分库分表(用户查自己的订单)
    • 订单号中嵌入用户ID的分片信息(通过订单号反查分片)
    • 商家查询:异构索引(按商家ID建立索引表)

12. ⚫ 设计一个支付系统

答:支付系统对数据一致性和安全性要求极高。

核心设计:

  1. 支付流程:

    1
    2
    用户下单 → 创建支付单 → 调用支付渠道 → 等待回调
    → 收到回调 → 验签 → 更新支付状态 → 通知业务系统
  2. 关键问题:

    • 掉单处理:支付成功但回调失败 → 定时轮询支付渠道查询
    • 重复支付:支付单号唯一 + 支付前校验状态
    • 对账:每日与支付渠道对账,发现差异人工处理
    • 退款:异步处理,退款状态独立管理
  3. 资金安全:

    • 所有资金操作记录流水(不可修改)
    • 余额变更使用乐观锁(version字段)
    • 日终对账:系统余额 vs 银行余额
    • 风控:大额交易人工审核、异常交易拦截
  4. 多渠道管理:

    • 策略模式封装不同支付渠道(支付宝、微信、银联)
    • 渠道路由:根据金额、成功率、费率选择渠道
    • 渠道降级:主渠道不可用时自动切换

13. ⚫ 设计一个监控告警系统

答:监控告警是保障系统稳定性的基础设施。

架构设计:

1
2
数据采集 → 数据传输 → 数据存储 → 数据查询 → 告警引擎 → 通知渠道
Agent Kafka TSDB Grafana 规则引擎 钉钉/邮件/电话
  1. 数据采集:

    • 基础指标:Node Exporter(CPU/内存/磁盘/网络)
    • 应用指标:Micrometer/Prometheus Client
    • 日志:Filebeat/Fluentd
    • 链路:OpenTelemetry SDK
  2. 时序数据库选型:

    方案 特点
    Prometheus 拉模式,适合K8s
    VictoriaMetrics 高性能,兼容Prometheus
    InfluxDB 推模式,SQL-like查询
    TDengine 国产,高压缩比
  3. 告警引擎:

    • 阈值告警:CPU > 80%持续5分钟
    • 趋势告警:错误率环比增长200%
    • 智能告警:基于历史数据的异常检测
    • 告警收敛:相同告警合并,避免告警风暴
    • 告警升级:5分钟未处理 → 升级通知上级
  4. SLO驱动的告警:

    • 定义SLO:可用性99.9%、P99延迟<500ms
    • 基于Error Budget告警:当Error Budget消耗速度过快时告警
    • 比基于资源指标的告警更有业务意义

14. ⚫ 设计一个多租户SaaS平台

答:多租户是SaaS产品的核心架构挑战。

隔离方案对比:

方案 隔离性 成本 运维复杂度 适用场景
共享一切 最低 小租户
共享应用+独立Schema 中等租户
共享应用+独立数据库 大租户
独立部署 最高 最高 最高 VIP租户

核心设计:

  1. 租户识别:

    • 域名识别:tenant1.saas.com → tenant1
    • Header识别:X-Tenant-Id
    • Token中携带:JWT中包含tenant_id
  2. 数据隔离:

    • 所有表增加tenant_id字段
    • MyBatis拦截器自动注入tenant_id过滤条件
    • 防止跨租户数据访问(安全红线)
  3. 资源隔离:

    • 计算资源:K8s Namespace + ResourceQuota
    • 存储资源:按租户配额限制
    • API限流:按租户独立限流
  4. 租户配置:

    • 功能开关:不同套餐开放不同功能
    • UI定制:Logo、主题色、域名
    • 流程定制:审批流、通知规则

15. ⚫ 设计一个日志收集与分析系统

答:日志系统是可观测性的基础。

架构设计(ELK/EFK):

1
应用日志 → Agent(Filebeat)→ Kafka → Logstash/Flink → Elasticsearch → Kibana

核心设计:

  1. 日志规范:

    • 统一JSON格式
    • 必须字段:timestamp、level、traceId、service、message
    • 日志级别规范:ERROR(需要处理)、WARN(需要关注)、INFO(关键流程)、DEBUG(调试)
  2. 采集方案:

    • Sidecar模式:每个Pod一个Filebeat容器
    • DaemonSet模式:每个Node一个Filebeat(推荐,资源消耗少)
    • SDK直推:应用直接发送到Kafka(延迟最低)
  3. 存储优化:

    • 索引策略:按天创建索引(logs-2024.01.01)
    • 生命周期:热数据(7天SSD)→ 温数据(30天HDD)→ 冷数据(归档到S3)
    • 索引模板:合理设置字段类型,keyword vs text
  4. 查询优化:

    • 按traceId查询完整链路
    • 按时间范围 + 关键字搜索
    • 聚合分析:错误趋势、Top N错误

16. ⚫ 设计一个API网关

答:API网关是微服务架构的流量入口。

核心功能:

  1. 路由转发: 根据URL/Header将请求路由到后端服务
  2. 认证鉴权: JWT验证、OAuth2、API Key
  3. 限流熔断: 令牌桶限流、熔断降级
  4. 协议转换: HTTP→gRPC、REST→GraphQL
  5. 请求改写: Header注入、参数转换、请求/响应改写
  6. 可观测性: 访问日志、指标采集、链路追踪

架构设计:

1
2
3
4
5
6
7
客户端 → DNS/CDN → LB → API Gateway集群 → 后端微服务

┌─────┴─────┐
│ 插件链 │
│ 认证→限流 │
│ →路由→改写 │
└───────────┘

插件化架构:

  • 请求处理链:Pre-Filter → Route → Post-Filter
  • 插件热加载:不重启网关即可更新插件
  • 自定义插件:业务方可以开发自定义插件

高性能设计:

  • 异步非阻塞:Netty/WebFlux
  • 连接池复用:与后端服务保持长连接
  • 本地缓存:路由规则、限流配置缓存在本地

对标产品: Kong、APISIX、Spring Cloud Gateway、Envoy

17. ⚫ 设计一个分布式缓存系统

答:分布式缓存是高性能系统的核心组件。

核心设计(参考Redis Cluster):

  1. 数据分片:

    • 哈希槽:16384个槽,每个节点负责一部分
    • 客户端路由:客户端缓存槽→节点映射,直连目标节点
    • MOVED重定向:槽迁移时返回MOVED,客户端更新路由
  2. 高可用:

    • 主从复制:每个主节点至少一个从节点
    • 故障检测:节点间Gossip协议互相探测
    • 自动故障转移:主节点故障后从节点自动提升
  3. 一致性:

    • 异步复制:主节点写入成功即返回,异步复制到从节点
    • 可能丢数据:主节点写入后宕机,从节点未收到复制
    • WAIT命令:等待指定数量的从节点确认(牺牲性能换一致性)
  4. 热点Key处理:

    • 本地缓存:热点Key缓存在应用本地(Caffeine)
    • Key分片:将热点Key拆分为多个子Key,分散到不同节点
    • 读写分离:读请求分散到从节点

18. ⚫ 设计一个消息推送系统(Push Notification)

答:消息推送是移动互联网应用的基础能力。

架构设计:

1
2
3
4
5
6
业务系统 → 推送平台 → 推送通道
→ APNs(iOS)
→ FCM(Android海外)
→ 厂商通道(小米/华为/OPPO/vivo)
→ WebSocket(Web端)
→ 短信(兜底)

核心设计:

  1. 推送策略:

    • 在线推送:通过长连接直接推送
    • 离线推送:通过厂商通道推送通知栏消息
    • 智能路由:优先在线推送,失败后走离线通道
  2. 大规模推送:

    • 全量推送(1亿用户):分批推送,控制速率
    • 标签推送:按用户标签筛选目标用户
    • 个性化推送:不同用户推送不同内容
  3. 推送质量:

    • 到达率监控:推送→到达→展示→点击的漏斗
    • 频率控制:每个用户每天最多N条推送
    • 免打扰:夜间不推送(或静默推送)

19. ⚫ 设计一个数据同步系统(CDC)

答:数据同步是数据架构的基础设施。

核心场景:

  • 数据库→数据库(主从同步、异构同步)
  • 数据库→缓存(DB变更同步到Redis)
  • 数据库→搜索引擎(DB变更同步到ES)
  • 数据库→数据仓库(实时数仓)

架构设计(基于CDC):

1
2
3
4
MySQL Binlog → Canal/Debezium → Kafka → 消费者
→ Redis更新
→ ES索引更新
→ 数据仓库写入

核心设计:

  1. Binlog解析:

    • Canal伪装为MySQL Slave,接收Binlog
    • 解析Binlog为结构化的变更事件(INSERT/UPDATE/DELETE)
    • 支持DDL变更感知
  2. 数据一致性:

    • 至少一次语义:消费者需要幂等处理
    • 顺序保证:同一行的变更按顺序消费(Kafka按主键分区)
    • 全量+增量:先全量同步,再增量追Binlog
  3. Schema演进:

    • 源表加字段:下游需要兼容处理
    • 源表改字段类型:需要停止同步,修改下游Schema
    • Schema Registry管理Schema版本

20. ⚫ 设计一个灰度发布系统

答:灰度发布是保障发布安全的核心能力。

核心设计:

  1. 灰度维度:

    • 按用户ID:指定用户或用户ID尾号
    • 按地域:先发布某个城市
    • 按流量比例:1% → 5% → 20% → 50% → 100%
    • 按设备:iOS先发,Android后发
    • 按渠道:内部员工先用
  2. 技术实现:

    1
    2
    3
    请求 → 网关 → 灰度规则引擎 → 匹配灰度规则?
    → 是:路由到新版本
    → 否:路由到旧版本
  3. 灰度规则引擎:

    • 规则存储在配置中心
    • 支持多条件组合(AND/OR)
    • 实时生效,无需重启
  4. 灰度监控:

    • 新旧版本的核心指标对比(错误率、延迟、业务指标)
    • 自动回滚:新版本指标异常时自动回滚
    • 灰度报告:灰度结束后生成对比报告
  5. 全链路灰度:

    • 请求染色:在Header中标记灰度标签
    • 链路传递:灰度标签在微服务间透传
    • 中间件灰度:MQ消息也需要灰度隔离

三、系统设计方法论(21-25题)

21. ⚫ 系统设计面试的通用框架是什么?

答:系统设计面试有一套通用的思考框架。

四步法:

  1. 需求澄清(5分钟):

    • 功能需求:核心功能是什么?MVP是什么?
    • 非功能需求:QPS、延迟、可用性、数据量
    • 约束条件:预算、团队规模、时间限制
  2. 高层设计(10分钟):

    • 画出核心组件和数据流
    • 确定API设计
    • 确定数据模型
  3. 详细设计(20分钟):

    • 深入核心组件的设计
    • 讨论关键技术决策和权衡
    • 处理边界情况
  4. 扩展讨论(10分钟):

    • 扩展性:如何应对10倍流量增长?
    • 可用性:单点故障如何处理?
    • 监控:如何发现和定位问题?

22. ⚫ 如何做容量评估(Back-of-the-envelope Estimation)?

答:容量评估是系统设计的基础技能。

常用数据:

  • 日活用户 → QPS:DAU / 86400 × 平均请求数 × 峰值系数
  • 峰值系数:通常3-10倍
  • 存储:每条记录大小 × 记录数 × 保留时间 × 副本数
  • 带宽:QPS × 请求大小 × 2(请求+响应)

延迟数据(架构师必须记住):

操作 延迟
L1缓存 0.5ns
L2缓存 7ns
内存访问 100ns
SSD随机读 150μs
HDD随机读 10ms
同机房网络往返 0.5ms
Redis GET 0.5-1ms
MySQL简单查询 1-5ms
跨机房网络往返 30-100ms

23. ⚫ 如何在系统设计中做技术选型?

答:技术选型是架构师最重要的决策之一。

选型原则:

  1. 满足需求:能解决当前问题
  2. 团队熟悉:团队能驾驭
  3. 社区活跃:有足够的社区支持和文档
  4. 可运维:运维成本可接受
  5. 可演进:不会成为未来的瓶颈

选型反模式:

  • 简历驱动开发(为了学新技术而选新技术)
  • 银弹思维(一个技术解决所有问题)
  • 跟风选型(别人用什么我就用什么)
  • 过度设计(日活1000用微服务+K8s+Service Mesh)

24. ⚫ 如何设计系统的高可用方案?

答:高可用是系统设计的核心非功能需求。

可用性级别:

级别 年停机时间 适用场景
99.9% 8.76小时 内部系统
99.99% 52.6分钟 核心业务
99.999% 5.26分钟 支付/金融

高可用手段:

  1. 冗余:多实例、多副本、多机房
  2. 故障检测:健康检查、心跳、超时
  3. 故障转移:自动切换、DNS切换
  4. 降级:非核心功能降级、返回兜底数据
  5. 限流:保护系统不被流量压垮
  6. 熔断:快速失败,避免级联故障

25. ⚫ 如何设计系统的可扩展性?

答:可扩展性决定系统能否应对业务增长。

扩展维度:

  1. 水平扩展(Scale Out): 加机器

    • 无状态服务:直接加实例
    • 有状态服务:分片(数据库分库分表、Redis Cluster)
  2. 垂直扩展(Scale Up): 升配置

    • 简单但有上限
    • 适合短期应急
  3. 功能拆分: 微服务化

    • 按业务域拆分
    • 独立部署、独立扩展

扩展性设计原则:

  • 无状态设计:状态外部化(Redis/DB)
  • 异步解耦:MQ削峰填谷
  • 缓存加速:多级缓存减少DB压力
  • 分库分表:数据量大时水平拆分
  • 读写分离:读多写少时分离读写