跳到正文
Java 入门到面试指南
返回

03-系统设计场景题

阶段:分布式与高并发篇 | 用法:三面/架构面的弹药库。每题按固定框架答:澄清需求 → 估算量级 → 存储设计 → 核心链路 → 难点与取舍。秒杀已在《02-高并发系统设计》串讲,本篇补另外 6 个高频场景。冲刺期每个场景对着朋友讲一遍,讲不下去的就是漏洞。

答题通用框架 ⭐

  1. 澄清:读多还是写多?数据量级?一致性要求高吗?有哪些隐藏约束(会过期?要统计?)——先问再画,直接开画是减分项。
  2. 估算:QPS = 日活 × 人均次数 ÷ 86400,再乘 10 倍峰值;存储 = 条数 × 单条大小 × (1+冗余),一年翻几倍要留量。
  3. 最小可用架构:客户端 → LB → 无状态服务 → 缓存 → DB,先跑通再谈优化。
  4. 难点:每个场景真正的考点(下表「考点」列),主动讲出来,别等面试官挖。

场景一:短链接系统 ⭐(读多写少 + 发号器)

  • 澄清:要自定义后缀吗?要点击统计吗?链接会过期吗?
  • 估算:1 亿条/年 ≈ 300 万/日,读多写少 100:1;单条约 200B → 存储无压力,瓶颈在读。
  • 核心链路:长链 → 发号器取 ID → Base62 编码(0-9A-Za-z,62 进制)得 6~7 位短码 → 存 short → long 映射 → 跳转用 301(永久重定向,浏览器缓存,别用 302 每次回源)。
  • 发号器三选一:DB 号段(批量取段,防单点)、雪花(呼应《01-分布式理论基础》)、Redis INCR。自定义后缀走业务表唯一键,撞了就重试。
  • 考点:为什么 301 vs 302;缓存热点(LRU + 布隆过滤不存在短码);点击统计异步 MQ 上报不阻塞跳转;防刷(同 IP 限流)。

场景二:海量数据去重(10 亿 URL 判断是否出现过)

  • 精确方案:Redis SET/BitMap——内存算账:10 亿 × 32B ≈ 32GB 起步,太贵,除非量级小。
  • 考点核心:布隆过滤器:k 个哈希 + 位数组,说「没有」一定准,说「有」可能假阳性;容量与误判率定 m = -n·ln p/(ln2)²、k = m/n·ln2;不能删除(计数布隆可)。10 亿 + 1% 误判 ≈ 1.2GB,这就是它赢的地方。
  • 大数据视角:离线场景直接 Spark/Hive distinct,或 bitmap 精确去重(RoaringBitmap);实时场景布隆 + 定期重建。
  • 加分:UV 统计进一步用 HyperLogLog(12KB 误差 0.81%),去重要精确吗?先问指标精度要求。

场景三:排行榜(游戏/热榜/直播间)

  • ZSET 起步:ZADD rank {score} {member}、ZREVRANGE 取 TopN、ZREVRANK 查自己名次——单机 Redis 一题解决。
  • 考点:① 分数相同怎么排(score 拼时间戳取负,双写一次排序键);② 数据量大分片:按分区/赛区拆多个 ZSET,Top 汇总归并;③ 实时更新频率高:客户端轮询改服务端推(WebSocket/SSE)、写合并(多次变更攒批 ZADD)。
  • 亿级:分桶 + 近似(Count-Min 估热度)+ 定时快照,讲清「精确名次」与「TopN 列表」是两个量级的问题。

场景四:延迟任务 / 订单超时关闭 ⭐

  • 方案四对比(面试就爱问这个):
方案原理问题
定时任务扫表每分钟 WHERE status=待付 AND create<now-15min延迟大、扫表压力、分片麻烦
JDK DelayQueue内存优先级队列重启全丢,单机
Redis ZSETscore=执行时间,轮询 ZRANGEBYSCORE需自己保证消费幂等与不丢
RabbitMQ 死信/TTL队列设 TTL,过期进 DLX,消费 DLX队头阻塞(用延迟插件解决)
RocketMQ 延迟等级/定时消息 5.xBroker 原生支持版本与运维成本
  • 口径:小任务用 MQ 延迟消息(业务解耦 + 不丢),量大用时间轮(HashedWheelTimer 内存版 / 容器化调度平台)。超时关单必须与支付回调并发安全:关单前查最新状态(CAS update … where status=待付),呼应秒杀防超卖。

场景五:Feed 流(朋友圈/微博首页)

  • 推拉模型:推(写扩散,发布时写进所有粉丝收件箱,读 O(1) 写爆炸)、拉(读扩散,读时现查关注列表聚合,写 O(1) 读慢)、推拉结合(普通用户拉、大 V 推)是标准答案。
  • 收件箱存储:Redis List/ZSET 存最近 N 条 ID + 回源 DB 翻页;粉丝千万级写扩散要分批 + 异步。
  • 考点:删除与可见性(读时过滤被删内容)、计数的最终一致、大 V 发文的削峰(呼应 MQ 削峰填谷)。

场景六:附近的人 / GEO(一句话了解)

  • Redis GEOADD(底层 ZSET + GeoHash)→ GEORADIUS 圈范围;GeoHash 原理:经纬度交叠编码成字符串,前缀相同≈距离近,用空间索引换掉全表算距离。
  • 讲清 GeoHash 方块边界问题(相邻可能跨格子,查周围 8 格)即可,不深入。

场景七:IM 消息系统(了解框架即可)

  • 链路:客户端长连接(WebSocket,呼应《IO模型与网络编程》Netty)→ 接入层(连接散列保持)→ 逻辑层 → 存储(Timeline:每会话一个 seq 递增)。
  • 难点四件套:不丢(ACK + 重传 + 离线拉取)、不乱(会话内 seq)、不重(幂等 msgId,呼应 MQ 消费幂等)、在线状态(临时节点/心跳,呼应 ZK)。
  • 已读回执用位图/区间压缩,别逐条存。

万能兜底话术

没准备过的场景也别慌,套路一样:「先澄清三个问题 → 给一个能跑的最小方案 → 说两个瓶颈(读瓶颈加缓存、写瓶颈加队列/分片)→ 点一个一致性/可靠性取舍」。面试官考的是思维结构,不是标准答案。

高频面试题

  • Q:短链为什么用 301 不用 302? —— 永久重定向浏览器会缓存跳转,省一次回源;302 每次都打到服务端,读放大 | 详见《03-系统设计场景题》
  • Q:布隆过滤器能删元素吗?误判率怎么定? —— 基础版不能(置 0 会误伤);m 与 k 由容量 n 和目标误差 p 推出;「说没有一定准」 | 详见《03-系统设计场景题》
  • Q:订单超时关闭有哪几种实现,怎么选? —— 扫表/DelayQueue/Redis ZSET/MQ 延迟;生产选 MQ 延迟消息或 RocketMQ 定时消息,关单用条件更新防与支付回调打架 | 详见《02-RabbitMQ》
  • Q:排行榜同分怎么处理? —— score 复合(分数 × 基数 + 取反时间戳),一次排序解决「同分先达优先」 | 详见《01-Redis》
  • Q:Feed 流推拉结合怎么划分用户? —— 粉丝量阈值:超阈值大 V 写扩散推,普通用户读扩散拉,兼顾写放大与读延迟 | 详见《03-系统设计场景题》
  • Q:UV 统计为什么用 HyperLogLog? —— 只关心计数不关心成员,12KB 内存 0.81% 误差,比 SET 省几个量级 | 详见《01-Redis》

推荐资料

资料用来干什么链接
JavaGuide 系统设计面试题场景题答题框架的原始出处https://javaguide.cn/system-design/system-design-questions.html
pdai.tech 分布式与架构篇发号器/缓存/消息队列的工程视角https://pdai.tech/md/arch/arch-index.html
廖雪峰 Web 应用开发HTTP 重定向/状态码的底层语境https://www.liaoxuefeng.com/books/java/
码工具 Java API场景实现时查类签名https://www.matools.com/api/java8