系统设计图示例:一个短链接服务,从需求到架构图

以短链接服务为例,完整演示一张系统设计图:需求、粗略估算、分离的读写路径,以及能体现推理过程的布局。

2026/10/22

系统设计图示例:一个短链接服务,从需求到架构图

无论是在系统设计面试中,还是在设计文档里,图都是让思路变得可见的地方。画一个标着“缓存”的方框很容易;让图真正值得一读的,是说明它为什么在那里、能吸收多少流量,以及缓存是冷的时候会发生什么。

本文以一道经典练习——短链接服务——为例,从需求一路做到完整的系统设计图。重点不在短链接服务本身,而在方法:先需求和数字,再 API 和数据,最后才画方框,并把每个决策写在大家都能看到的地方。

第 1 步:需求

功能需求:

  • 为长 URL 创建短链接,可选自定义别名和过期时间。
  • 把短链接重定向到对应的长 URL。
  • 统计每个链接的点击次数。

非功能需求:

  • 重定向必须快速且高可用;创建链接可以容忍更高的延迟。
  • 系统以读为主:链接只创建一次,却会被点击很多次。
  • 短码不应容易被枚举。

第 2 步:粗略估算

假设每月新增 1 亿条链接,每条新建链接带来 100 次重定向:

指标 估算
写入 100M / 每月 2.6M 秒 ≈ 平均每秒 40 次
读取 100 × 40 ≈ 每秒 4,000 次重定向;按 10× 峰值规划,即每秒 40,000 次
5 年后的链接数 100M × 12 × 5 = 60 亿
存储 60 亿 × ~500 字节 ≈ 3 TB,不含副本
短码长度 7 个 base62 字符可得 62⁷ ≈ 3.5 万亿个短码

这些数字已经在塑造设计:读请求占绝大多数,所以读取路径要加缓存;3 TB 不会永远待在一台小数据库上,所以数据模型必须能干净地分片;而 7 个字符留有充足的余量。

第 3 步:API

POST /api/links
{ "url": "https://example.com/a/very/long/path", "alias": "launch", "expiresAt": "2027-01-01" }
→ 201 { "code": "launch", "shortUrl": "https://s.example/launch" }

GET /{code}
→ 302 Location: https://example.com/a/very/long/path

有一个决策值得在图上写一行:301 还是 302? 301 是永久重定向,浏览器会缓存它,重复访问根本到不了你这里。这样能节省流量,却会丢掉点击统计。302 会让每次点击都经过重定向服务,这正是分析所需要的。本设计使用 302。

第 4 步:数据模型与 ID

核心表很简单:code(主键)、long_url、owner_id、created_at、expires_at。每次读取都是按 code 查找,所以键值存储或带主键的关系型数据库都能胜任;按 code 的哈希分片,可以把负载均匀分散。

生成短码才是有意思的部分:

方案 优点 缺点
计数器 + base62 没有冲突 连续的短码容易被枚举;需要协调
随机 7 个字符 难以猜测,无需协调 插入时必须检查冲突
URL 的哈希 相同 URL 得到相同短码 截断后会冲突;别名需要特殊处理

这里,写入 API 从 ID 区间分配器 租用 ID 区间,例如每次 100 万个,并在 base62 编码前用可逆置换把每个 ID 打乱。这样短码无需冲突检查就是唯一的,在外人看来也不连续。

第 5 步:画图

把需求不同的路径分开画。写入路径量小,必须正确;读取路径量大,必须快;分析可以滞后。

短链接服务的系统设计图:写请求发往写入 API,它从分配器租用 ID 并保存链接;重定向先读 Redis,再读链接数据库,并经由 Kafka 把点击事件送往分析。

11 个组件 · 10 个连接

在 Archboard 中打开
图表内容
  • Users
  • Load balancer
  • ID range allocator
  • Write API
  • Redirect service
  • Cache: code → URL
  • Links DB (sharded by code)
  • click events
  • Analytics workers
  • Analytics store
  • HTTPS
  • POST · 40/s
  • GET · 4k/s, peak 40k/s
  • lease ID range
  • INSERT link
  • GET code
  • on cache miss
  • click event
  • consume
  • counts
本文的短链接服务:经过 ID 分配器的写入路径、由 Redis 缓存提供服务的读取路径、异步的点击分析,以及决定这一设计的各项估算。

这张图的读法如下:

  • 写入路径(上方)。 负载均衡器把 POST /api/links 路由到写入 API,写入 API 从已租用的区间中取出一个 ID,编码后把链接写入数据库。
  • 读取路径(中间)。 GET /{code} 到达重定向服务,它先查 Redis。只要热点数据放得进内存,大多数重定向根本不会访问数据库。缓存未命中时,它读取数据库并回填缓存。
  • 分析(下方)。 每次重定向都会向 Kafka 发出一个点击事件,分析 worker 把计数聚合到分析存储中。重定向从不等待分析。

画出能看见思路的图

  • 在连线上标数字。 写入连线上的 40/s 和读取连线上的 4k/s, peak 40k/s,比任何标签都更能说明为什么需要缓存。
  • 把假设写在方框旁边。 “缓存命中率目标 90%”“302 用于分析”“ID 按 1M 为一段租用”。
  • 区分同步与异步。 请求路径用实线,点击事件用虚线。
  • 说明出故障时会怎样。 如果 Redis 宕机,重定向会回退到数据库,延迟随之升高;把这一点写在图上。
  • 把估算留在画布上。 在图旁边放一张小小的数字表,图就成了一份设计文档。

值得提前准备的深入话题

  • 缓存。 热门链接呈长尾分布,所以一个 TTL 适中的 LRU 缓存就能覆盖大部分流量。在删除和过期时让缓存失效。
  • 数据库扩展。 在这里,只读副本的作用不如缓存;按短码分片能让写入和存储均匀增长。
  • 过期。 在读取时检查 expires_at,并在后台清理,而不是跑一个巨大的删除任务。
  • 滥用。 按用户对创建链接限流,并用恶意软件列表检查目标 URL。

在 Archboard 中构建

  1. 使用 Google 或 GitHub 登录,并新建一张图。
  2. 添加网络节点包中的 互联网 节点表示用户,再添加云节点包中的 云负载均衡器 作为负载均衡器。
  3. 把写入 API、重定向服务、ID 分配器和分析 worker 作为 C4 模型节点包中的 容器 节点添加进来,并在“技术”字段中填写编程语言。
  4. 从数据节点包中添加 Redis 作为缓存、Postgres 作为链接数据库、一个 主题 承载点击事件,以及一个 数据仓库 用于分析。
  5. 给连线标注操作和速率,把点击事件设为虚线,并把估算写进基础节点包中的 Markdown 块。

这个示例共用 11 个图表节点(包括写有估算的 Markdown 块),在 Free 套餐的限制之内。适用于任何架构图的通用方法,请参阅如何绘制软件架构图。

常见问题

面试时,系统设计图应该画多细? 能展示读写路径、数据存储、缓存和一个异步组件就够了。只在面试官追问的地方深入。

链接表用 SQL 还是 NoSQL? 按主键查找,两者都可以。根据你打算如何分片、团队擅长运维什么来选,并把理由写在图上。

要画 CDN 吗? 只有在边缘缓存重定向时才需要。用了 302 又要统计点击,边缘缓存的作用有限,所以很多设计都省略它。

为什么不全部用 301? 301 能节省流量,但浏览器不会再来问你,于是你会丢失点击数据,也无法为回访用户更改目标地址。

数字放在哪里? 放在画布上、图的旁边,这样任何评审设计的人都能核对方框背后的假设。

免费开始 — 1 个席位,无需信用卡。

相关文章

← 返回博客

一分钟打开画布

使用 Google 或 GitHub 登录。单席位免费,团队加入后再升级。