系统设计URL短链TinyURLsystemdesign面试

系统设计 Deep Dive:设计 URL 短链服务(TinyURL)

系统设计面试深度解析:基于真实候选人面经整理,涵盖SQL、Go、算法、系统设计等核心技术。还原系统设计题目、架构决策与面试官追问,附准备策略助你高效备战技术面试。

Sam · · 10 分钟阅读

面试官真实提问

“请设计一个类似 TinyURL 的 URL 短链服务。用户输入一个长 URL,你返回一个短 URL。当用户访问短 URL 时,重定向到原始长 URL。”

这道题出现在 Google、Meta、Amazon 的系统设计面试中,通常是第一道系统设计题,用来考察候选人是否掌握基础的系统设计方法论。


需求澄清清单

功能需求

Must-have:

  • 将长 URL 缩短为短 URL
  • 短 URL 访问时 301/302 重定向到原始 URL
  • 支持自定义短码(可选)

Nice-to-have:

  • URL 过期设置
  • 点击统计与分析
  • QR 码生成
  • URL 预览功能

规模估算

指标估算值
DAU1000 万
URL 创建每天 1 亿次(写)
URL 访问每天 100 亿次(读)
读写比100:1(读远多于写)
存储每条 200 字节,1 亿条约 20GB/天,一年约 7.3TB

非功能需求

  • 可用性: 99.99%(URL 访问是核心功能)
  • 延迟: P99 < 100ms(重定向要快)
  • 一致性: 最终一致性可接受

第 1 步:高层设计

┌─────────┐
│ 客户端   │  Web / App
└────┬────┘

┌────▼────┐
│ LB      │  Nginx / 负载均衡
└────┬────┘

   ┌─┴─┐
┌──▼──┐ ┌──▼──┐
│ API │ │ API │  Node A / Node B
│ A   │ │ B   │
└──┬──┘ └──┬──┘
     └──┬──┘
     ┌──▼──────┐
     │ Redis   │  缓存
     │ Cluster │
     └──┬──────┘

      ┌─┴─┐
  ┌───▼┐ ┌───▼──┐
  │MySQL│ │MySQL │  Shard A / B
  │ A   │ │  B   │
  └─────┘ └──────┘

核心组件:

  • 客户端:Web/App,发起创建短链和访问短链请求
  • 负载均衡器:分发请求到 API 服务器
  • API Server:处理业务逻辑,短码生成和重定向
  • Redis Cluster:缓存热点短 URL,减少数据库压力
  • MySQL(分片):持久化存储 URL 映射关系

第 2 步:核心组件设计

API 设计

# 创建短链
POST /api/v1/shorten
{
  "long_url": "https://example.com/very/long/url/..."
}
Response:
{
  "short_url": "https://tiny.url/abc123",
  "short_code": "abc123"
}

# 重定向(301/302)
GET /abc123
→ 302 Found → Location: https://example.com/very/long/url/...

# 获取 URL 信息
GET /api/v1/info/abc123
{
  "short_url": "https://tiny.url/abc123",
  "long_url": "https://example.com/very/long/url/...",
  "created_at": "2026-05-06T10:00:00Z",
  "click_count": 1234
}

数据模型

CREATE TABLE urls (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  short_code VARCHAR(8) UNIQUE NOT NULL,
  long_url TEXT NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  expires_at TIMESTAMP NULL,
  click_count INT DEFAULT 0,
  user_id BIGINT NULL,
  INDEX idx_short_code (short_code),
  INDEX idx_created_at (created_at)
);

短码生成算法(核心考点)

这是这道题最重要的部分。面试官主要考察你对唯一性、可扩展性和安全性的理解。以下是三种主流方案:

方案 1:数据库自增 ID + Base62 编码

原理: 利用数据库自增 ID(Auto Increment),将其转换为 62 进制字符串(0-9, a-z, A-Z)。

  • ID=1"1"
  • ID=62"z"
  • ID=63"10"
优点缺点
简单高效:实现最容易,短码最短可预测性(安全漏洞):攻击者可以通过遍历 ID 获取其他用户 URL
无冲突:数据库保证 ID 唯一单点瓶颈:并发写入压力大
有序性:短码包含时间顺序信息暴露业务量:ID 直接反映系统 URL 总量

方案 2:哈希算法(MD5/SHA)

原理: 对原始长 URL 进行哈希计算(如 MD5),截取前 8 位作为短码。

优点缺点
天然分布式:不需要数据库协调冲突风险:必须查库校验,冲突则重试
不可预测:短码随机,无法遍历短码较长:通常需要 8 位(Base62^8 ≈ 21 万亿)
确定性:相同 URL 生成相同短码无法统计:无法区分不同的分享事件

方案 3:分布式 ID(Snowflake)+ Base62

原理: 使用 Snowflake 算法生成唯一 ID(时间戳 + 机器 ID + 序列号),然后转为 Base62。

优点缺点
高吞吐:单机每秒可生成数十万 ID依赖时钟:时钟回拨可能导致 ID 冲突
无冲突:算法保证全局唯一
不可预测:包含随机因素,无法遍历
趋势递增:写入数据库时索引局部性好

方案对比总结

特性自增 ID + Base62哈希算法 (MD5)Snowflake + Base62
唯一性保证需处理冲突保证
安全性差 (可遍历)
扩展性差 (DB 瓶颈)极好
短码长度最短较长中等
实现复杂度

面试推荐方案:Snowflake + Base62

话术: “考虑到系统需要高并发写入(扩展性)且不希望用户遍历 URL(安全性),我推荐使用 Snowflake 生成唯一 ID 并转为 Base62 的方案。如果规模较小,初期也可以使用 Redis INCR 模拟自增 ID。“


详细请求流程

创建短链流程:

  1. 客户端 POST /api/v1/shorten,传入 long_url
  2. API Server 用 Snowflake 生成唯一 ID
  3. ID → Base62 编码short_code
  4. 写入 MySQL(short_code, long_url)
  5. 写入 Redis 缓存(short_code → long_url,TTL 24h)
  6. 返回 short_url 给客户端

访问短链流程:

  1. 客户端 GET /abc123
  2. API Server 查 Redis 缓存
  3. 缓存命中 → 302 重定向 + 异步更新点击数
  4. 缓存未命中 → 查 MySQL → 写入 Redis → 302 重定向

第 3 步:扩展性与优化

瓶颈分析

瓶颈 1:数据库写入压力

  • 每天 1 亿次创建,QPS ≈ 1157
  • 单台 MySQL 处理不了 → 分片(Sharding)
  • short_code 哈希分片到多个数据库

瓶颈 2:热点 URL 读取

  • 热门 URL(如营销链接)可能被数百万次访问
  • 多级缓存策略:
    • L1:API Server 本地缓存(Guava/Caffeine,TTL 1 分钟)
    • L2:Redis Cluster(TTL 24 小时)
    • L3:CDN 缓存 302 重定向响应

瓶颈 3:自增 ID 冲突

  • 多服务器同时生成 ID 会冲突
  • 解决: 用 Snowflake 算法,每台服务器分配不同 worker_id

容量估算

指标计算结果
存储200 字节 × 1 亿/天 × 365 天7.3TB
数据库每 shard 500GB15 个 shard
Redis缓存 10% 热点数据200GB
带宽100 亿次/天 × 500 字节/次115MB/s 平均

面试官常问 Trade-offs 与实战问答

Q1:你选择 301 还是 302 重定向?为什么?

候选人回答:

“我选择 302 临时重定向。因为 301 是永久重定向,浏览器会缓存这个映射,后续请求不会再经过我们的服务器,这样我们就无法统计点击数据了。虽然 302 每次都要请求服务器,但我们可以通过 CDN 缓存 302 响应 来优化性能,同时保留统计能力。”

面试官追问:

“如果 CDN 缓存了 302 响应,用户修改了长 URL 怎么办?”

候选人回答:

“设置合理的 TTL(比如 5 分钟),并在用户更新 URL 时主动清除 CDN 缓存。对于需要实时更新的场景,可以在 URL 中添加版本参数绕过 CDN 缓存。“


Q2:你为什么要用 MySQL 而不是 NoSQL?

候选人回答:

“URL 短链的数据模型非常简单,就是 key-value 映射。MySQL 的查询性能完全够用,而且支持复杂查询(比如按时间范围统计、用户维度分析)。如果未来写入量极大(每秒数十万),我会考虑迁移到 Cassandra 这样的宽表数据库。”

面试官追问:

“Cassandra 和 MySQL 在写入性能上有什么区别?”

候选人回答:

“Cassandra 使用 LSM-Tree 存储引擎,写入是追加操作,性能极高。MySQL 使用 B+ 树,写入需要维护索引,性能相对较低。但 Cassandra 的最终一致性模型对于 URL 短链来说是可以接受的。“


Q3:短码长度选多少位合适?

候选人回答:

6 位 Base62 可以提供 568 亿 个唯一短码,对于大多数场景足够了。如果预期规模更大,我会选择 7 位(约 3500 亿)。同时会预留一些短码给自定义短码功能。”

面试官追问:

“如果用户想要自定义短码(比如 my-brand),怎么处理冲突?”

候选人回答:

“先检查自定义短码是否已被占用。如果冲突,提示用户选择其他短码。同时需要防止用户占用系统保留的短码(比如 admin、login 等)。“


Q4:Redis 缓存策略是怎样的?

候选人回答:

“我采用多级缓存策略。第一级是 API Server 的本地缓存(Guava/Caffeine),TTL 1 分钟,处理超热点 URL。第二级是 Redis Cluster,TTL 24 小时,处理大部分读取。第三级是 CDN 缓存 302 响应,进一步减少回源请求。”

面试官追问:

“Redis 宕机了怎么办?”

候选人回答:

降级为直接查询 MySQL。虽然延迟会增加,但服务仍然可用。同时会触发告警,运维团队紧急修复 Redis。本地缓存仍然可以处理部分热点请求。“


Q5:怎么处理 DDoS 攻击?

候选人回答:

多层防护策略。第一层在 CDN/WAF 层,过滤已知的恶意 IP 和异常流量模式。第二层在负载均衡器,设置 rate limiting。第三层在 API Server,对单个 IP 做限流。同时会监控流量异常,自动触发防护规则。”

面试官追问:

“如果攻击流量来自正常用户(比如病毒式传播)?”

候选人回答:

“这种情况下不能简单封禁。需要扩容(自动扩缩容),并优化缓存策略。同时监控业务指标,区分正常流量和恶意流量。“


进阶扩展方向

  • 自定义短码: 用户输入 my-brand,需处理冲突
  • URL 过期: 定时任务清理过期记录,Redis TTL 自动过期
  • 统计分析: 独立 PV/UV、地域分布、设备类型、referrer
  • 安全防护: 恶意 URL 检测、黑名单、病毒扫描

常见踩坑点

#踩坑点解决方案
1短码冲突:哈希方案必须处理碰撞查数据库 → 加随机后缀重试
2热点 Key:大 V 发的链接可能占 50% 流量本地缓存 + CDN 缓存
3URL 长度限制:长 URL 可能超过 2048 字符输入验证 + 截断处理
4恶意 URL:短链可能被用于钓鱼安全审核 + 黑名单
5统计准确性:异步更新点击数可能丢失接受最终一致性

总结

URL 短链服务看似简单,但考察了系统设计的核心能力:

能力考察点
容量估算从业务场景推导系统规模
算法选择短码生成的多种方案及权衡
缓存策略多级缓存应对读多写少的场景
扩展性分片、负载均衡应对高并发

面试提示: 不要一上来就画图。先花 3-5 分钟澄清需求,明确规模,再开始设计。面试官更看重你的思考过程,而不是最终答案。


推荐阅读


💡 需要面试辅导?

如果你对准备技术面试感到迷茫,或者想要个性化的面试指导和简历优化,欢迎联系 Interview Coach Pro 获取一对一辅导服务。

👉 联系我们 获取专属面试准备方案

准备好拿下下一次面试了吗?

获取针对你的目标岗位和公司的个性化辅导方案。

联系我们