Appearance
场景设计题
场景设计题考的是综合能力:需求分析 → 核心功能 → 架构设计 → 关键细节。
答题框架
系统设计面试没有标准答案,但有一套通用的答题套路,按以下步骤展开:
- 需求澄清(3-5 分钟):确认核心功能、用户量级、读写比、延迟要求、一致性要求。不要急于画图,先问清楚。
- 容量估算:QPS、存储量、带宽、内存。粗算即可,量级对了就行。
- 高层架构:画出核心组件和数据流(客户端 → 网关 → 服务 → 存储 → 缓存 → MQ)。
- 详细设计:数据模型、API 设计、核心算法、关键流程。
- 瓶颈与优化:高并发怎么扛?单点怎么消除?数据怎么扩展?最终一致还是强一致?
- 扩展讨论:监控、容灾、灰度发布、成本优化。
Q1: 设计一个短链系统 「🟡 中级」
考察点:经典设计题,考察 ID 生成、存储、跳转。
参考答案:
- 核心功能:长链 → 短链(6-8 位)、短链 → 长链(302 重定向)。
- 短链生成:
- 自增 ID + Base62 编码(简单高效)。
- 分布式 ID(Snowflake)+ Base62。
- 哈希(MD5/CRC32)+ 冲突处理。
- 存储:短码 → 长链的映射表(Redis 缓存热点短链)。
- 跳转:301(永久,浏览器缓存)vs 302(临时,可统计点击)。
- 扩展:过期时间、点击统计、自定义短码。
追问延伸:
- 301 和 302 怎么选?
- 怎么防止恶意短链?
Q2: 设计一个 Feed 流系统 「🔴 高级」
考察点:社交产品的核心场景。
参考答案:
- 推模式(Push):用户发布内容后,主动推送到所有粉丝的收件箱(写扩散)。优点:读取快;缺点:大 V 写扩散成本高。
- 拉模式(Pull):用户访问时实时拉取关注者的内容(读扩散)。优点:写入简单;缺点:读取时聚合开销大。
- 推拉结合:普通用户用推,大 V 用拉(访问时拉取大 V 内容)。
- 存储:收件箱用 Redis ZSet(score 为时间戳);内容存 MySQL/HBase。
- 分页:游标分页(避免 offset 性能问题)。
追问延伸:
- 大 V 的写扩散问题怎么解决?
- Feed 流的排序怎么做?
Q3: 设计一个秒杀系统 「🔴 高级」
考察点:高并发场景的综合设计(详见"高并发设计"篇)。
参考答案:
- 前端:按钮防重、验证码、静态化。
- 网关:限流、IP 黑名单、风控。
- 服务:库存预扣减(Redis Lua)、异步下单(MQ)、一人一单。
- 数据:库存分片、热点隔离。
- 兜底:降级页面、排队机制。
追问延伸:
- 怎么防止机器人刷单?
- 秒杀结束后怎么对账?
Q4: 设计一个订单超时取消功能 「🟡 中级」
考察点:延迟任务的实现。
参考答案:
- 延迟队列:RocketMQ 延迟消息、RabbitMQ TTL + 死信、Redis ZSet(score 为超时时间)。
- 定时任务:每分钟扫描超时订单(简单但有延迟)。
- 时间轮:Netty HashedWheelTimer(单机高效)。
- 推荐:MQ 延迟消息(可靠、可扩展)。
追问延伸:
- 订单量大时定时任务扫描的性能问题?
- 延迟消息的可靠性怎么保证?
Q5: 设计一个红包系统 「🔴 高级」
考察点:并发扣减、随机算法、资金安全。
参考答案:
- 发红包:预扣款(冻结金额)、生成红包记录(总金额、个数)。
- 抢红包:
- 二倍均值算法:剩余金额 / 剩余人数 * 2 为上限,保证公平。
- 预拆分:发红包时预先拆好每个红包的金额(存入 Redis List)。
- 并发控制:Redis Lua 原子拆红包、数据库乐观锁。
- 资金安全:对账机制、幂等设计、分布式事务。
追问延伸:
- 二倍均值算法怎么保证公平?
- 红包拆完后怎么通知?
Q6: 设计一个评论系统 「🟡 中级」
考察点:高并发读写、树形结构、审核。
参考答案:
- 存储:评论表(内容、用户、时间、父评论 ID)、点赞表。
- 读优化:热点评论缓存(Redis)、分页用游标。
- 写优化:异步写入(MQ)、批量入库。
- 树形结构:平铺 + 父 ID(简单)、嵌套集合(复杂查询)。
- 审核:先审后发 / 先发后审(MQ 异步审核)。
追问延伸:
- 评论的排序怎么做?(时间 / 热度)
- 怎么处理垃圾评论?
Q7: 设计一个通知系统 「🟡 中级」
考察点:多渠道、高并发、可靠性。
参考答案:
- 多渠道:短信、邮件、App 推送、站内信。
- 架构:通知服务 → 渠道路由 → 各渠道网关(短信网关、邮件服务器)。
- 高并发:异步发送(MQ)、批量发送、限流。
- 可靠性:重试机制、失败告警、送达回执。
- 模板管理:动态参数替换、多语言支持。
追问延伸:
- 怎么保证通知不丢失?
- 多渠道的优先级怎么设计?
Q8: 设计一个排行榜系统 「🟡 中级」
考察点:Redis 数据结构选型、实时排序、分页查询的综合能力。
参考答案:
- 核心功能:实时积分排行榜(Top N)、用户排名查询、分页浏览。
- 数据结构选型:
- Redis ZSet:score 存积分,member 存用户 ID,天然有序,
ZREVRANGEBYSCORE取 Top N。 - 粒度控制:按天/周/月/总榜分别建不同的 ZSet key。
- Redis ZSet:score 存积分,member 存用户 ID,天然有序,
- 实时更新:用户积分变化时
ZINCRBY原子更新;积分来源(签到、消费等)通过 MQ 异步写入。 - 查询优化:
- Top N:
ZREVRANGEO(logN+M)。 - 我的排名:
ZREVRANK。 - 分页:游标分页(用 score 做游标,避免 offset 深分页问题)。
- Top N:
- 量级与分片:
- 百万级用户单 ZSet 没问题(Redis 单 key 建议 < 100MB)。
- 千万级以上:按用户 ID 分片到多个 ZSet,查询时合并多分片 Top N(堆/归并)。
- 冷热分离:Top 1000 缓存在本地(Caffeine),其余走 Redis。
追问延伸:
- 相同积分怎么保证排名稳定?(score 拼接时间戳做辅助排序)
- 百亿级用户怎么做全局排行榜?(分片 + 预聚合 + 定时快照)
Q9: 设计一个限流系统 「🔴 高级」
考察点:限流算法、分布式限流、多级防护体系。
参考答案:
- 核心功能:单机限流 + 分布式限流 + 多维度(IP/用户/接口/全局)。
- 限流算法对比:
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 固定窗口 | 固定时间窗口内计数 | 简单 | 窗口边界突刺 |
| 滑动窗口 | 滑动时间窗口内计数 | 平滑 | 实现略复杂 |
| 漏桶 | 固定速率出水 | 流量均匀 | 无法应对突发 |
| 令牌桶 | 固定速率发令牌 | 允许突发 | 实现略复杂 |
| 滑动日志 | 记录每次请求时间戳 | 最精确 | 内存开销大 |
- 单机限流:Guava
RateLimiter(令牌桶)、Semaphore(并发数限制)。 - 分布式限流:
- Redis + Lua:用滑动窗口或令牌桶,Lua 保证原子性。
- Redis-Cell 模块:原生
CL.THROTTLE命令。 - 网关层限流:Sentinel、Nginx limit_req。
- 多级防护:
- CDN/WAF → IP 限流
- 网关 → 接口限流
- 服务 → 用户限流
- 底层 → 熔断降级(Hystrix/Sentinel)
- 动态规则:限流规则存配置中心(Nacos/Apollo),运行时热更新。
追问延伸:
- 滑动窗口怎么用 Redis 实现?(ZSet + ZREMRANGEBYSCORE)
- 限流和熔断的区别?(限流是"入口控制流量",熔断是"下游故障时自我保护")
Q10: 设计一个分布式唯一 ID 生成服务 「🟡 中级」
考察点:分布式 ID 的多种方案对比、Snowflake 原理。
参考答案:
- 核心需求:全局唯一、趋势递增、高性能、高可用。
- 方案对比:
| 方案 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| UUID | 随机生成 | 简单、无中心 | 无序、占空间、索引差 |
| 数据库自增 | AUTO_INCREMENT | 简单有序 | 单点瓶颈 |
| 号段模式 | 批量取号 | DB 压力小、高性能 | 需要中心化发号器 |
| Snowflake | 时间戳+机器ID+序列号 | 高性能、趋势递增 | 时钟回拨 |
| Redis INCR | 原子自增 | 简单高性能 | 依赖 Redis 可用性 |
| Zookeeper | 顺序节点 | 强一致 | 性能差 |
- 推荐方案:Snowflake(高性能)+ 号段模式(高可用兜底)。
- Snowflake 详解:
- 64 位:1 位符号 + 41 位时间戳(可用 69 年)+ 10 位机器 ID(1024 台)+ 12 位序列号(每毫秒 4096 个)。
- 时钟回拨问题:等待回拨时间过去、用历史最大时间戳、或切换备用机器 ID。
- 美团 Leaf:号段 + Snowflake 双 buffer,号段模式扛住性能,Snowflake 兜底高可用。
- 百度 UidGenerator:基于 Snowflake,用环形 buffer 预生成 ID。
追问延伸:
- Snowflake 的时钟回拨怎么解决?
- 号段模式下 DB 挂了怎么办?(双 buffer 预加载,一个用完切另一个)
Q11: 设计一个 IM 聊天系统 「🔴 高级」
考察点:实时通信、消息可靠性、多端同步、群聊扩散。
参考答案:
- 核心功能:单聊、群聊、消息推送、多端同步、已读回执、离线消息。
- 通信协议:WebSocket(长连接,浏览器友好)/ TCP 长连接(移动端)。
- 架构分层:
- 接入层:长连接网关(百万并发),负责连接管理和消息路由。
- 逻辑层:消息处理(路由、存储、推送)。
- 存储层:消息存储(MySQL/HBase)+ 离线消息(Redis)。
- 消息流转:
- 发送方通过长连接发消息到网关。
- 网关路由到消息服务。
- 消息服务存储消息(持久化)。
- 消息服务查接收方在线状态。
- 在线 → 推送到接收方网关 → 接收方;离线 → 存离线消息 + APNs/FCM 推送。
- 消息可靠性:
- 客户端:发送 → 等待 ACK → 超时重发。
- 服务端:存消息后才推,推失败存离线。
- 消息 ID 去重(幂等)。
- 群聊设计:
- 写扩散(Push):消息写入每个成员的收件箱(小群适用)。
- 读扩散(Pull):消息写入群的timeline,成员拉取(大群适用)。
- 混合:小群写扩散,大群读扩散。
- 多端同步:每个设备维护一个本地 sequence,服务端按 sequence 增量推送。
追问延伸:
- 百万群聊的消息扩散怎么优化?
- 消息的"已读回执"怎么设计不影响性能?
Q12: 设计一个搜索系统 「🔴 高级」
考察点:ES 倒排索引、分词、相关性排序、搜索架构。
参考答案:
- 核心功能:全文搜索、高亮、分类过滤、排序、聚合统计。
- 技术选型:Elasticsearch(倒排索引 + 分词 + 相关性打分)。
- 索引设计:
- 分词器:中文用 IK 分词器(ik_smart 粗粒度、ik_max_word 细粒度)。
- 字段映射:keyword(精确匹配,不分词)vs text(全文搜索,分词)。
- 多字段:同一字段既分词搜索又精确匹配(
field+field.keyword)。
- 查询优化:
- 全文搜索:
match/multi_match。 - 精确过滤:
term/terms(用 filter,不参与打分,可缓存)。 - 组合查询:
bool(must / should / filter / must_not)。 - 高亮:
highlight。
- 全文搜索:
- 架构:
- 数据同步:Canal 订阅 MySQL binlog → MQ → ES 同步(近实时)。
- 索引别名:零停机重建索引(新索引建好 → 切换别名 → 删旧索引)。
- 深分页:
search_after替代from + size(避免 deep paging 性能问题)。
- 相关性优化:
function_score:结合销量、评分、时间衰减做业务排序。- 自定义打分脚本。
追问延伸:
- ES 的深度分页为什么慢?(coordinate node 需要合并所有 shard 的结果排序)
- 怎么实现"搜索建议"(自动补全)?(completion suggester / edge ngram)
Q13: 设计一个日志系统 「🔴 高级」
考察点:日志采集、存储、查询、告警的完整链路。
参考答案:
- 核心功能:日志采集、实时查询、告警、可视化大盘。
- 架构(ELK / EFK):
- 采集层:Filebeat / Fluentd / Vector(轻量 agent 部署在每个机器)。
- 缓冲层:Kafka(削峰填谷,防日志暴增打爆 ES)。
- 处理层:Logstash / Fluentd(过滤、解析、富化)。
- 存储层:Elasticsearch(全文搜索 + 聚合分析)。
- 展示层:Kibana / Grafana(可视化、仪表盘、告警)。
- 日志类型与索引策略:
- 按天建索引(
logs-2026.08.23),便于按时间范围查询和删除过期索引。 - 不同日志类型分不同索引模板(access log、error log、业务 log)。
- ILM(Index Lifecycle Management):热-温-冷-删,自动管理索引生命周期。
- 按天建索引(
- 采集可靠性:
- Filebeat 有 ack 机制,至少一次送达。
- Kafka 作为缓冲,ES 不可用时日志不丢。
- 查询优化:
- 缩小时间范围(ES 按时间分片,时间范围小查的分片少)。
- 用 keyword 过滤代替 text 搜索。
- 大范围聚合用 datafeed + transform 预计算。
- 告警:ES Watcher / Kibana Alerting / Grafana Alerting。
追问延伸:
- ES 存日志太贵怎么办?(冷数据归档到 S3/OSS,用可搜索快照)
- 怎么防止日志暴增打爆系统?(Kafka 削峰 + ES 背压 + 采样降级)
Q14: 设计一个文件上传和下载系统 「🟡 中级」
考察点:大文件处理、断点续传、秒传、分布式存储。
参考答案:
- 核心功能:文件上传、下载、断点续传、秒传、分片上传。
- 小文件上传:
- 前端上传 → 后端接收 → 存对象存储(OSS/S3/MinIO)→ 返回 URL。
- 大文件分片上传:
- 前端计算文件 MD5,请求服务端检查是否已存在(秒传)。
- 不存在 → 文件分片(如每片 5MB)→ 并发上传分片。
- 服务端记录已上传分片(断点续传:中断后查已上传分片,只传缺失的)。
- 全部分片上传完 → 通知服务端合并分片。
- 断点续传:
- 前端上传前请求服务端获取已上传分片列表。
- 只上传缺失的分片。
- 用分片 MD5 校验完整性。
- 秒传:
- 文件 MD5 作为唯一标识,已存在则直接返回 URL,不重新上传。
- 存储:
- 对象存储(OSS/S3/MinIO):海量文件、按量计费、CDN 加速。
- 分片直接存对象存储,最后用
CompleteMultipartUpload合并。
- 下载:
- 小文件直接下载。
- 大文件支持 Range 请求(断点下载)。
- 私有文件用预签名 URL(有时效性)。
追问延伸:
- 大量并发上传同一个文件怎么处理?(MD5 去重 + 分布式锁防并发写入)
- 怎么保证上传的文件不含恶意内容?(文件类型校验、病毒扫描、内容审核)
Q15: 设计一个抢红包/优惠券发放系统 「🔴 高级」
考察点:高并发扣减、库存防超卖、公平性、资金安全。
参考答案:
- 核心功能:发券/发红包 → 抢券 → 核销/使用 → 对账。
- 发券阶段:
- 运营创建活动,预生成券码批次(批量写入 Redis + DB)。
- 优惠券总量存 Redis(库存计数器)。
- 抢券阶段(核心高并发):
- 前置校验:用户限流(一人一张)、活动时间校验、风控(黑名单、设备指纹)。
- 库存扣减:Redis Lua 原子扣减(
GET → 判断 → DECR → 记录用户),保证不超卖。 - 异步落库:扣减成功后发 MQ,异步写入 DB(券记录、用户券表)。
- 降级:Redis 挂了 → 降级到 DB 限流模式(串行扣减,性能差但保安全)。
- 库存防超卖:
- Redis Lua 脚本:判断 + 扣减原子执行。
- 兜底:DB 乐观锁(
UPDATE coupon_stock SET remain = remain - 1 WHERE id = ? AND remain > 0)。
- 公平性:
- 先到先得(Redis 排队)。
- 防刷:一人一券(Redis SETNX 标记)、IP 限流、设备指纹。
- 资金安全:
- 优惠券和红包涉及金额 → 对账机制(每日核对发放量和核销量)。
- 幂等设计(券码唯一、操作去重)。
- 审计日志(谁发的、谁抢的、谁核销的)。
追问延伸:
- Redis 挂了怎么办?(DB 乐观锁兜底 + 限流降级)
- 券码被预测/遍历怎么防?(随机券码 + 短时效 + 防枚举)