系统设计实战题(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万亿,足够使用
核心设计:
短链生成算法:
- 方案A:自增ID + Base62编码(简单,但ID可预测)
- 方案B:MD5/MurmurHash取前7位(可能冲突,需要冲突处理)
- 方案C:预生成ID池(Snowflake/Leaf)+ Base62(推荐)
- 冲突处理:布隆过滤器快速判断 + DB唯一索引兜底
数据模型:
1
2
3
4
5
6
7
8
9CREATE 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)
);架构设计:
1
2
3
4
5客户端 → CDN(缓存热门短链的301跳转)
→ Nginx(负载均衡)
→ 短链服务(无状态,水平扩展)
→ Redis缓存(热门短链,命中率>95%)
→ MySQL集群(分库分表,按short_code哈希)关键决策:
- 301 vs 302跳转:301永久重定向(浏览器缓存,减少服务端压力),302临时重定向(每次都经过服务端,便于统计)。推荐302,因为需要统计
- 缓存策略:LRU缓存热门短链,写入时同步写缓存
- 分库分表:按short_code哈希分16库×16表 = 256张表
访问统计:
- 异步写入:跳转时发送Kafka消息,消费者异步写入统计表
- 统计维度:PV/UV、地域、设备、Referer
- 实时统计:Redis HyperLogLog统计UV
面试追问点:
- 如何防止恶意刷短链?(限流 + 验证码)
- 如何处理热点短链?(多级缓存 + CDN)
- 短链过期如何清理?(定时任务 + 懒删除)
2. ⚫ 设计一个秒杀系统
答:秒杀是高并发场景的极端案例,核心挑战是在极短时间内处理海量请求,同时保证数据一致性。
需求分析:
- 商品数量:1000件
- 预计参与人数:100万
- 秒杀持续时间:瞬间(<10秒内完成)
- 核心要求:不超卖、不少卖、高可用、公平
流量分析:
- 瞬时QPS:100万/10秒 = 10万QPS
- 实际有效请求:远超库存,99.9%的请求注定失败
核心设计思路:层层过滤,将请求漏斗化
1 | 100万请求 → 前端限流(50%过滤)→ 50万 |
分层设计:
前端层:
- 按钮置灰 + 倒计时(防止提前点击)
- 点击后按钮禁用(防止重复提交)
- 前端加随机延迟(打散请求)
- 答题/验证码(削峰 + 防机器人)
接入层(Nginx):
- 限流:令牌桶限流,超过阈值直接返回”活动太火爆”
- 负载均衡:一致性哈希(同一用户打到同一台机器,利于本地缓存)
应用层:
- 本地缓存库存标记(库存为0后直接拒绝,不访问Redis)
- 用户去重(Redis Set记录已参与用户)
- 请求排队(内存队列,控制并发)
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消息,异步创建订单
订单层:
- 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 | 客户端 → LB → 连接层(WebSocket/TCP长连接) |
关键设计:
连接管理:
- 协议选择:WebSocket(Web端)、TCP自定义协议(移动端)
- 连接服务器:Netty实现,单机50万连接
- 连接路由:用户→连接服务器的映射存储在Redis
- 心跳机制:客户端30秒心跳,服务端90秒超时断开
消息模型:
1
2
3消息ID:Snowflake生成(全局有序)
消息流程:
A发送 → 服务端收到(ACK1)→ 存储 → 推送给B → B收到(ACK2)→ 通知A已送达(ACK3)- 三次确认保证消息可靠性
- 消息ID用于去重和排序
单聊消息存储(写扩散 vs 读扩散):
- 写扩散:每个用户一个收件箱,发送时写入接收方收件箱
- 读扩散:消息只存一份,读取时聚合
- 单聊推荐写扩散(实现简单,读取快)
群聊消息存储:
- 群消息只存一份(群消息表)
- 每个用户维护一个已读偏移量(群成员表中的last_read_msg_id)
- 拉取未读:
WHERE msg_id > last_read_msg_id
消息存储方案:
- 近期消息(3个月):MySQL分库分表(按用户ID哈希)
- 历史消息:HBase/TiDB(海量存储)
- 消息索引:Elasticsearch(全文搜索)
- 媒体文件:对象存储(OSS/S3)
离线消息:
- 用户不在线时,消息存入离线消息队列
- 同时触发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 推拉结合
推模式(写扩散):
- 用户发布时,将内容写入所有粉丝的Feed收件箱
- 优点:读取快(直接读收件箱)
- 缺点:大V发布时写放大严重(1000万粉丝 = 1000万次写入)
- 适合:粉丝数少的普通用户
拉模式(读扩散):
- 用户查看Feed时,实时聚合关注人的最新内容
- 优点:写入简单
- 缺点:读取慢(需要聚合200个关注人的内容)
- 适合:大V
推拉结合(推荐方案):
- 普通用户(粉丝<1万):推模式
- 大V(粉丝>1万):拉模式
- 用户读取Feed时:收件箱内容 + 实时拉取关注的大V内容 → 合并排序
数据模型:
1 | -- 发布表(所有动态) |
Feed流生成流程:
1 | 用户A发布动态 |
性能优化:
- Redis缓存:每个用户的Feed流缓存最近200条ID
- 分页:基于游标分页(created_at + post_id),避免深分页
- 预加载:用户打开App时预加载Feed流
5. ⚫ 设计一个分布式文件存储系统
答:分布式文件存储是基础设施级别的系统设计题,考察对存储、网络、一致性的深入理解。
需求分析:
- 存储容量:PB级
- 文件大小:KB到GB不等
- 核心功能:上传、下载、删除、元数据查询
- 非功能:高可用(99.99%)、数据不丢失(11个9持久性)、高吞吐
架构设计(参考GFS/HDFS):
1 | 客户端 → NameNode(元数据服务) |
NameNode(元数据管理):
- 维护文件→块的映射关系
- 维护块→DataNode的映射关系
- 高可用:Active-Standby + ZooKeeper选主
- 元数据存储:内存 + EditLog(WAL)+ 定期Checkpoint
DataNode(数据存储):
- 存储实际的数据块(默认64MB/128MB一个块)
- 定期向NameNode发送心跳和块报告
- 数据校验:每个块存储Checksum
数据写入流程:
1
2
3
4客户端 → NameNode请求写入
← 返回3个DataNode地址(副本放置策略)
→ 数据流水线写入:Client→DN1→DN2→DN3
→ 所有副本写入成功后,通知NameNode副本策略:
- 默认3副本
- 第1副本:客户端所在机架的节点
- 第2副本:不同机架的节点
- 第3副本:第2副本同机架的不同节点
- 保证机架级容灾
小文件优化:
- 问题:大量小文件会导致NameNode内存压力
- 方案:小文件合并存储(类似HBase的HFile)
- 或使用对象存储方案(类似S3),元数据存数据库
纠删码(Erasure Coding):
- 3副本存储开销200%
- 纠删码(如RS(6,3))存储开销50%,同样容忍3节点故障
- 适合冷数据,降低存储成本
面试追问点:
- 如何处理DataNode故障?(NameNode检测心跳超时,自动复制副本到其他节点)
- 如何处理NameNode单点?(HA方案:Active-Standby + 共享EditLog)
- 大文件上传如何断点续传?(分块上传,记录已上传的块)
6. ⚫ 设计一个分布式任务调度系统
答:分布式任务调度是后端基础设施的核心组件。
需求分析:
- 支持Cron定时任务和一次性任务
- 任务量:10万+定时任务
- 核心要求:准时触发、不重复执行、失败重试、任务依赖
架构设计:
1 | ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ |
核心设计:
调度中心高可用:
- 多节点部署,通过数据库行锁或Redis分布式锁竞争调度权
- 任务分片:将任务按ID范围分配给不同调度节点
- 故障转移:节点宕机后,其他节点接管
触发机制:
- 时间轮算法(HashedWheelTimer):高效的定时触发
- 预读:提前5秒从DB加载即将触发的任务到内存
- Cron解析:计算下次触发时间
执行策略:
- 单机串行 / 单机并行 / 广播执行 / 分片执行
- 分片执行:大任务拆分为多个子任务,分发到多个执行器并行处理
失败处理:
- 重试策略:固定间隔/指数退避,最大重试次数
- 故障转移:执行器超时后转移到其他执行器
- 告警:失败后通知相关人员
任务依赖(DAG):
- 支持任务间的依赖关系(A完成后触发B)
- DAG调度:拓扑排序确定执行顺序
- 子任务全部完成后触发父任务的回调
对标产品: XXL-Job、ElasticJob、Airflow
7. ⚫ 设计一个实时搜索引擎
答:搜索引擎是信息检索的核心系统,考察倒排索引、分布式检索、相关性排序等知识。
需求分析:
- 数据量:10亿文档
- 查询QPS:1万
- 延迟要求:P99 < 200ms
- 功能:全文搜索、过滤、排序、聚合、高亮、自动补全
架构设计(参考Elasticsearch):
数据模型:
- 索引(Index)→ 分片(Shard)→ 段(Segment)
- 每个分片是一个独立的Lucene索引
- 分片数在创建时确定,不可修改(需要Reindex)
倒排索引:
1
2
3
4
5
6
7
8文档1: "Java并发编程实战"
文档2: "Java虚拟机原理"
倒排索引:
Java → [文档1, 文档2]
并发 → [文档1]
编程 → [文档1]
虚拟机 → [文档2]写入流程:
- 文档写入 → 分词 → 构建倒排索引 → 写入内存Buffer
- Buffer满或定时 → Refresh到Segment(近实时搜索,默认1秒)
- Segment定期Merge(合并小段为大段,清理删除文档)
- Translog保证数据不丢失(类似WAL)
查询流程(Scatter-Gather):
1
2
3
4
5
6协调节点收到查询
→ 将查询分发到所有相关分片
→ 每个分片本地查询,返回Top N的文档ID和分数
→ 协调节点合并排序,取全局Top N
→ 根据文档ID获取完整文档内容
→ 返回结果相关性排序:
- BM25算法(TF-IDF的改进版)
- 自定义评分:Function Score Query
- 向量搜索:kNN + HNSW索引(语义搜索)
性能优化:
- 路由:相关文档路由到同一分片,减少跨分片查询
- 缓存:Query Cache、Request Cache、Fielddata Cache
- 预热:常用查询预热缓存
8. ⚫ 设计一个分布式配置中心
答:配置中心是微服务架构的基础设施。
需求分析:
- 配置数量:10万+配置项
- 客户端数量:1万+服务实例
- 核心要求:实时推送、灰度发布、版本管理、权限控制
架构设计:
数据模型:
1
2Namespace(环境)→ Group(应用)→ Key-Value(配置项)
每个配置项有版本号,支持历史回溯配置推送机制:
- 方案A:客户端轮询(简单但延迟高)
- 方案B:长轮询(Nacos方案,客户端发起长连接,服务端有变更时立即返回)
- 方案C:WebSocket/gRPC Stream(实时性最好)
- 推荐:长轮询(兼顾实时性和兼容性)
长轮询实现:
1
2
3
4
5客户端发起请求,携带当前配置的MD5
→ 服务端比较MD5
→ 相同:Hold住请求(最长30秒)
→ 不同:立即返回变更的配置
→ 配置变更时:唤醒所有Hold的请求灰度发布:
- 按IP灰度:指定IP列表使用新配置
- 按比例灰度:10%的实例使用新配置
- 按标签灰度:指定标签的实例使用新配置
高可用:
- 服务端集群:多节点 + 数据库存储
- 客户端本地缓存:服务端不可用时使用本地缓存
- 本地文件快照:客户端定期将配置写入本地文件
对标产品: Nacos、Apollo、Spring Cloud Config
9. ⚫ 设计一个权限系统(通用RBAC平台)
答:权限系统是企业级应用的基础设施,参考11-project.md中IAM部分的深入设计。
核心架构要点:
权限模型:RBAC + ABAC混合
- 粗粒度:RBAC(用户→角色→权限)
- 细粒度:ABAC(基于属性的动态权限)
- 数据权限:基于组织架构的行级权限
性能设计:
- 每个请求都需要鉴权,必须高性能
- 多级缓存:JWT本地解析 → 本地缓存 → Redis → DB
- 权限变更通过MQ广播,各服务异步更新缓存
SDK设计:
- 提供Java/Go/Python SDK
- SDK内置缓存和降级逻辑
- 注解式鉴权:
@RequirePermission("user:read")
多租户:
- 每个租户独立的角色和权限体系
- 租户管理员可以自定义角色
- 数据隔离:tenant_id贯穿所有表
10. ⚫ 设计一个全局唯一ID生成系统
答:分布式ID是分布式系统的基础问题。
需求分析:
- 全局唯一、趋势递增、高性能(>10万/秒)、高可用
方案对比:
| 方案 | 优点 | 缺点 |
|---|---|---|
| UUID | 简单,无依赖 | 无序,存储大,索引性能差 |
| 数据库自增 | 简单,有序 | 性能瓶颈,单点故障 |
| 数据库号段 | 性能好 | 实现复杂 |
| Redis INCR | 性能好 | 依赖Redis |
| Snowflake | 性能极高,趋势递增 | 时钟回拨问题 |
| Leaf | 号段+Snowflake双模式 | 需要部署服务 |
Snowflake算法详解:
1 | 0 | 41位时间戳 | 10位机器ID | 12位序列号 |
- 单机QPS:409.6万/秒
- 时钟回拨处理:等待时钟追上 / 使用扩展位记录回拨次数
号段模式(Leaf-Segment):
1 | CREATE TABLE leaf_alloc ( |
- 每次从DB获取一个号段(如1000-2000)
- 在内存中分配,用完再取下一个号段
- 双Buffer:当前号段用到10%时,异步加载下一个号段
二、进阶系统设计(11-20题)
11. ⚫ 设计一个电商订单系统
答:订单系统是电商的核心,涉及分布式事务、状态机、幂等性等关键问题。
核心设计:
订单状态机:
1
2
3待支付 → 已支付 → 待发货 → 已发货 → 已签收 → 已完成
↓ ↓ ↓
已取消 退款中 → 已退款 售后中下单流程(分布式事务):
1
创建订单 → 扣减库存 → 扣减优惠券 → 创建支付单
- 方案:基于MQ的最终一致性
- 订单服务创建订单(本地事务)
- 发送MQ消息通知库存服务扣减
- 库存扣减失败:补偿回滚订单
幂等设计:
- 订单号作为幂等键
- 支付回调幂等:支付单号 + 状态校验
- 数据库唯一索引兜底
订单超时取消:
- 方案A:延迟队列(RocketMQ延迟消息)
- 方案B:Redis过期事件
- 方案C:定时任务扫描(兜底)
- 推荐:延迟队列 + 定时任务兜底
分库分表:
- 按用户ID分库分表(用户查自己的订单)
- 订单号中嵌入用户ID的分片信息(通过订单号反查分片)
- 商家查询:异构索引(按商家ID建立索引表)
12. ⚫ 设计一个支付系统
答:支付系统对数据一致性和安全性要求极高。
核心设计:
支付流程:
1
2用户下单 → 创建支付单 → 调用支付渠道 → 等待回调
→ 收到回调 → 验签 → 更新支付状态 → 通知业务系统关键问题:
- 掉单处理:支付成功但回调失败 → 定时轮询支付渠道查询
- 重复支付:支付单号唯一 + 支付前校验状态
- 对账:每日与支付渠道对账,发现差异人工处理
- 退款:异步处理,退款状态独立管理
资金安全:
- 所有资金操作记录流水(不可修改)
- 余额变更使用乐观锁(version字段)
- 日终对账:系统余额 vs 银行余额
- 风控:大额交易人工审核、异常交易拦截
多渠道管理:
- 策略模式封装不同支付渠道(支付宝、微信、银联)
- 渠道路由:根据金额、成功率、费率选择渠道
- 渠道降级:主渠道不可用时自动切换
13. ⚫ 设计一个监控告警系统
答:监控告警是保障系统稳定性的基础设施。
架构设计:
1 | 数据采集 → 数据传输 → 数据存储 → 数据查询 → 告警引擎 → 通知渠道 |
数据采集:
- 基础指标:Node Exporter(CPU/内存/磁盘/网络)
- 应用指标:Micrometer/Prometheus Client
- 日志:Filebeat/Fluentd
- 链路:OpenTelemetry SDK
时序数据库选型:
方案 特点 Prometheus 拉模式,适合K8s VictoriaMetrics 高性能,兼容Prometheus InfluxDB 推模式,SQL-like查询 TDengine 国产,高压缩比 告警引擎:
- 阈值告警:CPU > 80%持续5分钟
- 趋势告警:错误率环比增长200%
- 智能告警:基于历史数据的异常检测
- 告警收敛:相同告警合并,避免告警风暴
- 告警升级:5分钟未处理 → 升级通知上级
SLO驱动的告警:
- 定义SLO:可用性99.9%、P99延迟<500ms
- 基于Error Budget告警:当Error Budget消耗速度过快时告警
- 比基于资源指标的告警更有业务意义
14. ⚫ 设计一个多租户SaaS平台
答:多租户是SaaS产品的核心架构挑战。
隔离方案对比:
| 方案 | 隔离性 | 成本 | 运维复杂度 | 适用场景 |
|---|---|---|---|---|
| 共享一切 | 低 | 最低 | 低 | 小租户 |
| 共享应用+独立Schema | 中 | 中 | 中 | 中等租户 |
| 共享应用+独立数据库 | 高 | 高 | 高 | 大租户 |
| 独立部署 | 最高 | 最高 | 最高 | VIP租户 |
核心设计:
租户识别:
- 域名识别:tenant1.saas.com → tenant1
- Header识别:X-Tenant-Id
- Token中携带:JWT中包含tenant_id
数据隔离:
- 所有表增加tenant_id字段
- MyBatis拦截器自动注入tenant_id过滤条件
- 防止跨租户数据访问(安全红线)
资源隔离:
- 计算资源:K8s Namespace + ResourceQuota
- 存储资源:按租户配额限制
- API限流:按租户独立限流
租户配置:
- 功能开关:不同套餐开放不同功能
- UI定制:Logo、主题色、域名
- 流程定制:审批流、通知规则
15. ⚫ 设计一个日志收集与分析系统
答:日志系统是可观测性的基础。
架构设计(ELK/EFK):
1 | 应用日志 → Agent(Filebeat)→ Kafka → Logstash/Flink → Elasticsearch → Kibana |
核心设计:
日志规范:
- 统一JSON格式
- 必须字段:timestamp、level、traceId、service、message
- 日志级别规范:ERROR(需要处理)、WARN(需要关注)、INFO(关键流程)、DEBUG(调试)
采集方案:
- Sidecar模式:每个Pod一个Filebeat容器
- DaemonSet模式:每个Node一个Filebeat(推荐,资源消耗少)
- SDK直推:应用直接发送到Kafka(延迟最低)
存储优化:
- 索引策略:按天创建索引(logs-2024.01.01)
- 生命周期:热数据(7天SSD)→ 温数据(30天HDD)→ 冷数据(归档到S3)
- 索引模板:合理设置字段类型,keyword vs text
查询优化:
- 按traceId查询完整链路
- 按时间范围 + 关键字搜索
- 聚合分析:错误趋势、Top N错误
16. ⚫ 设计一个API网关
答:API网关是微服务架构的流量入口。
核心功能:
- 路由转发: 根据URL/Header将请求路由到后端服务
- 认证鉴权: JWT验证、OAuth2、API Key
- 限流熔断: 令牌桶限流、熔断降级
- 协议转换: HTTP→gRPC、REST→GraphQL
- 请求改写: Header注入、参数转换、请求/响应改写
- 可观测性: 访问日志、指标采集、链路追踪
架构设计:
1 | 客户端 → DNS/CDN → LB → API Gateway集群 → 后端微服务 |
插件化架构:
- 请求处理链:Pre-Filter → Route → Post-Filter
- 插件热加载:不重启网关即可更新插件
- 自定义插件:业务方可以开发自定义插件
高性能设计:
- 异步非阻塞:Netty/WebFlux
- 连接池复用:与后端服务保持长连接
- 本地缓存:路由规则、限流配置缓存在本地
对标产品: Kong、APISIX、Spring Cloud Gateway、Envoy
17. ⚫ 设计一个分布式缓存系统
答:分布式缓存是高性能系统的核心组件。
核心设计(参考Redis Cluster):
数据分片:
- 哈希槽:16384个槽,每个节点负责一部分
- 客户端路由:客户端缓存槽→节点映射,直连目标节点
- MOVED重定向:槽迁移时返回MOVED,客户端更新路由
高可用:
- 主从复制:每个主节点至少一个从节点
- 故障检测:节点间Gossip协议互相探测
- 自动故障转移:主节点故障后从节点自动提升
一致性:
- 异步复制:主节点写入成功即返回,异步复制到从节点
- 可能丢数据:主节点写入后宕机,从节点未收到复制
- WAIT命令:等待指定数量的从节点确认(牺牲性能换一致性)
热点Key处理:
- 本地缓存:热点Key缓存在应用本地(Caffeine)
- Key分片:将热点Key拆分为多个子Key,分散到不同节点
- 读写分离:读请求分散到从节点
18. ⚫ 设计一个消息推送系统(Push Notification)
答:消息推送是移动互联网应用的基础能力。
架构设计:
1 | 业务系统 → 推送平台 → 推送通道 |
核心设计:
推送策略:
- 在线推送:通过长连接直接推送
- 离线推送:通过厂商通道推送通知栏消息
- 智能路由:优先在线推送,失败后走离线通道
大规模推送:
- 全量推送(1亿用户):分批推送,控制速率
- 标签推送:按用户标签筛选目标用户
- 个性化推送:不同用户推送不同内容
推送质量:
- 到达率监控:推送→到达→展示→点击的漏斗
- 频率控制:每个用户每天最多N条推送
- 免打扰:夜间不推送(或静默推送)
19. ⚫ 设计一个数据同步系统(CDC)
答:数据同步是数据架构的基础设施。
核心场景:
- 数据库→数据库(主从同步、异构同步)
- 数据库→缓存(DB变更同步到Redis)
- 数据库→搜索引擎(DB变更同步到ES)
- 数据库→数据仓库(实时数仓)
架构设计(基于CDC):
1 | MySQL Binlog → Canal/Debezium → Kafka → 消费者 |
核心设计:
Binlog解析:
- Canal伪装为MySQL Slave,接收Binlog
- 解析Binlog为结构化的变更事件(INSERT/UPDATE/DELETE)
- 支持DDL变更感知
数据一致性:
- 至少一次语义:消费者需要幂等处理
- 顺序保证:同一行的变更按顺序消费(Kafka按主键分区)
- 全量+增量:先全量同步,再增量追Binlog
Schema演进:
- 源表加字段:下游需要兼容处理
- 源表改字段类型:需要停止同步,修改下游Schema
- Schema Registry管理Schema版本
20. ⚫ 设计一个灰度发布系统
答:灰度发布是保障发布安全的核心能力。
核心设计:
灰度维度:
- 按用户ID:指定用户或用户ID尾号
- 按地域:先发布某个城市
- 按流量比例:1% → 5% → 20% → 50% → 100%
- 按设备:iOS先发,Android后发
- 按渠道:内部员工先用
技术实现:
1
2
3请求 → 网关 → 灰度规则引擎 → 匹配灰度规则?
→ 是:路由到新版本
→ 否:路由到旧版本灰度规则引擎:
- 规则存储在配置中心
- 支持多条件组合(AND/OR)
- 实时生效,无需重启
灰度监控:
- 新旧版本的核心指标对比(错误率、延迟、业务指标)
- 自动回滚:新版本指标异常时自动回滚
- 灰度报告:灰度结束后生成对比报告
全链路灰度:
- 请求染色:在Header中标记灰度标签
- 链路传递:灰度标签在微服务间透传
- 中间件灰度:MQ消息也需要灰度隔离
三、系统设计方法论(21-25题)
21. ⚫ 系统设计面试的通用框架是什么?
答:系统设计面试有一套通用的思考框架。
四步法:
需求澄清(5分钟):
- 功能需求:核心功能是什么?MVP是什么?
- 非功能需求:QPS、延迟、可用性、数据量
- 约束条件:预算、团队规模、时间限制
高层设计(10分钟):
- 画出核心组件和数据流
- 确定API设计
- 确定数据模型
详细设计(20分钟):
- 深入核心组件的设计
- 讨论关键技术决策和权衡
- 处理边界情况
扩展讨论(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. ⚫ 如何在系统设计中做技术选型?
答:技术选型是架构师最重要的决策之一。
选型原则:
- 满足需求:能解决当前问题
- 团队熟悉:团队能驾驭
- 社区活跃:有足够的社区支持和文档
- 可运维:运维成本可接受
- 可演进:不会成为未来的瓶颈
选型反模式:
- 简历驱动开发(为了学新技术而选新技术)
- 银弹思维(一个技术解决所有问题)
- 跟风选型(别人用什么我就用什么)
- 过度设计(日活1000用微服务+K8s+Service Mesh)
24. ⚫ 如何设计系统的高可用方案?
答:高可用是系统设计的核心非功能需求。
可用性级别:
| 级别 | 年停机时间 | 适用场景 |
|---|---|---|
| 99.9% | 8.76小时 | 内部系统 |
| 99.99% | 52.6分钟 | 核心业务 |
| 99.999% | 5.26分钟 | 支付/金融 |
高可用手段:
- 冗余:多实例、多副本、多机房
- 故障检测:健康检查、心跳、超时
- 故障转移:自动切换、DNS切换
- 降级:非核心功能降级、返回兜底数据
- 限流:保护系统不被流量压垮
- 熔断:快速失败,避免级联故障
25. ⚫ 如何设计系统的可扩展性?
答:可扩展性决定系统能否应对业务增长。
扩展维度:
水平扩展(Scale Out): 加机器
- 无状态服务:直接加实例
- 有状态服务:分片(数据库分库分表、Redis Cluster)
垂直扩展(Scale Up): 升配置
- 简单但有上限
- 适合短期应急
功能拆分: 微服务化
- 按业务域拆分
- 独立部署、独立扩展
扩展性设计原则:
- 无状态设计:状态外部化(Redis/DB)
- 异步解耦:MQ削峰填谷
- 缓存加速:多级缓存减少DB压力
- 分库分表:数据量大时水平拆分
- 读写分离:读多写少时分离读写