1. 厘清需求(Requirements)
功能性需求:
- 给一个长 URL,返回一个唯一的短 URL。
- 访问短 URL 时,重定向到原始长 URL。
- (可选)自定义短码、链接过期时间、访问统计。
非功能性需求:
- 高可用(服务挂了用户就打不开链接)。
- 重定向低延迟。
- 短码不可预测(出于安全)。
- 系统读多写少。
2. 容量估算(Capacity Estimation) —— 这一步很多人会漏,但评分里通常占分
假设每月新增 1 亿条短链,读写比约 100:1。
- 写:100M / month ≈ 40 次/秒。
- 读:约 4000 次/秒。
- 存储:每条约 500 字节,5 年 ≈ 60 亿条 ≈ 3 TB。
- 短码长度:用 62 进制(a-z, A-Z, 0-9),7 位 = 62⁷ ≈ 3.5 万亿,足够用很多年。
3. API 设计
POST /shorten body: { longUrl, customAlias?, expireAt? } -> { shortUrl }
GET /{shortCode} -> HTTP 301/302 重定向到 longUrl
重定向用 302(临时) 还是 301(永久):301 浏览器会缓存、减轻服务器压力,但你就拿不到点击统计;302 每次都回服务器,便于统计。面试里能说出这个权衡是加分点。
4. 数据模型
一张核心表即可:
url_mapping( short_code PK, long_url, created_at, expire_at, user_id )
因为是 key-value 式的查询(用 short_code 查 long_url),用 NoSQL(如 DynamoDB/Cassandra)或带缓存的关系库都行。
5. 核心:短码怎么生成? —— 这是这道题真正的考点
- 预生成 + 发号服务(推荐):用一个独立的 ID 生成服务(如 Snowflake,或 Zookeeper/Redis 维护号段),各应用服务器批量领取一段 ID,再 Base62 编码。既避免单点瓶颈,又无冲突。
6. 整体架构 & 扩展性
读路径(重定向)是性能关键:
- 前面放 缓存(Redis),缓存热门短码 → 长 URL 的映射,大部分读请求不落库。
- 数据库做分片(按 short_code 哈希),配只读副本扛读流量。
- 前置 CDN / 负载均衡,服务无状态、可水平扩展。
- 发号服务用号段预分配,避免成为写瓶颈。
7. 收尾:可补充的点
限流防滥用、过期链接的清理(定时任务或惰性删除)、点击分析走异步消息队列(Kafka)避免拖慢重定向、监控告警。
API Design
POST /shorten body: { longUrl, customAlias?, expireAt? } -> { shortUrl }
GET /{shortCode} -> HTTP 301/302 重定向到 longUrl
重定向用 302(临时) 还是 301(永久):301 浏览器会缓存、减轻服务器压力,但你就拿不到点击统计;302 每次都回服务器,便于统计。面试里能说出这个权衡是加分点。
读路径(重定向)是性能关键:
- 前面放 缓存(Redis),缓存热门短码 → 长 URL 的映射,大部分读请求不落库。
- 数据库做分片(按 short_code 哈希),配只读副本扛读流量。
- 前置 CDN / 负载均衡,服务无状态、可水平扩展。
- 发号服务用号段预分配,避免成为写瓶颈。
4. 数据模型
一张核心表即可:
url_mapping( short_code PK, long_url, created_at, expire_at, user_id )
Detailed Component Design
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.