系统设计面试:设计一个短链接服务

System Design Interview: Designing a URL Shortener Service

| Alex Chen | 2026-08-12T17:41:00

短链接服务是系统设计面试的经典题目。这篇文章从需求分析到详细设计,给出一个完整的解答思路。

A complete system design interview answer for URL shortener service, from requirements analysis to detailed design.

## 需求分析 面试官:请设计一个类似 bit.ly 的短链接服务。 ### 功能需求 - 给定长 URL,生成短链接 - 访问短链接时重定向到原 URL - 短链接可以设置过期时间 - 统计点击次数 ### 非功能需求 - 日活 1 亿次重定向 - 读写比 100:1 - 短链接尽可能短 - 高可用、低延迟(< 100ms) ### 容量估算 ``` 写入:1 亿 / 100 = 100 万次/天 ≈ 12 次/秒 读取:1 亿次/天 ≈ 1200 次/秒 存储:假设保留 5 年,100 万/天 × 365 × 5 = 18.25 亿条 每条 500B → 18.25 亿 × 500B ≈ 900GB ``` ## 短码生成方案 ### 方案一:哈希取前缀 ``` MD5(longUrl) → 取前 7 位 Base62 编码 ``` 问题:哈希冲突。需要数据库层面做唯一性校验。 ### 方案二:自增 ID + Base62 ``` 数据库自增 ID → Base62 编码 ID 12345 → Base62 → "3d7" ``` 7 位 Base62 可以表示 62^7 ≈ 3.5 万亿个短码,足够用了。 问题:自增 ID 是可预测的,有安全隐患。 ### 方案三:分布式 ID 生成(推荐) 用 Snowflake 或类似算法生成分布式唯一 ID,再 Base62 编码。 ``` Snowflake ID: 1750000000000001 Base62 编码: "2DfG8kL" ``` ## 系统架构 ``` 客户端 → 负载均衡 → API 服务(无状态) ↓ ┌─────────┴──────────┐ ↓ ↓ 写入路径 读取路径 ↓ ↓ ID 生成器 Redis 缓存 ↓ ↓ (miss) MySQL/分库 MySQL/分库 ``` ### 写入流程 1. 接收长 URL 2. ID 生成器生成唯一 ID 3. Base62 编码得到短码 4. 存入数据库 `{short_code, long_url, created_at, expires_at}` 5. 写入 Redis 缓存 6. 返回短链接 ### 读取流程 1. 解析短码 2. 查 Redis 缓存 3. Cache miss 查数据库 4. 返回 301/302 重定向 5. 异步记录点击统计 ### 301 vs 302 ``` 301 Moved Permanently → 浏览器永久缓存,后续不再请求服务器 302 Found → 每次都请求服务器 ``` 如果需要统计点击次数,**必须用 302**。否则浏览器缓存后不会再请求服务器,统计数据不准。 ## 数据库设计 ```sql CREATE TABLE url_mapping ( id BIGINT PRIMARY KEY, short_code VARCHAR(10) NOT NULL, long_url TEXT NOT NULL, created_at DATETIME NOT NULL, expires_at DATETIME, click_count BIGINT DEFAULT 0, UNIQUE KEY uk_short_code (short_code), KEY idx_expires (expires_at) ) ENGINE=InnoDB; ``` ### 分库策略 18 亿条数据需要分库。按 `short_code` 的首字母分 62 个库(Base62 的每个字符一个库)。这样同一个短码永远路由到同一个库。 ## 缓存策略 ``` Cache-Aside 模式: 1. 先查 Redis 2. 命中 → 直接返回 3. 未命中 → 查 DB → 写入 Redis → 返回 缓存过期:24 小时 热 key 保护:热点短链接单独设置更长的过期时间 ``` 读取 QPS 1200,缓存命中率 90% 的话,数据库承受的 QPS 只有 120,完全没问题。 ## 高可用 - API 服务:多实例 + 负载均衡,无状态 - Redis:主从 + Sentinel 或 Redis Cluster - MySQL:主从复制,读操作走从库 - ID 生成器:预分配号段(每次从数据库拿 1000 个 ID 缓存在内存里) ## 点击统计 不要在 url_mapping 表上做 `UPDATE click_count = click_count + 1`,高并发下会有热点行问题。 方案:异步写入 + 批量聚合 ``` 点击事件 → Kafka → 消费者批量聚合 → 写入统计表 ``` ## 加分项 面试时可以额外提到: 1. **防滥用**:限流、黑名单 URL 检测 2. **自定义短码**:允许用户指定短码(需要唯一性校验) 3. **分析报表**:地域分布、时间分布、Referrer 来源 4. **多区域部署**:用 DNS 做地理路由,缩短延迟


Complete system design interview answer for URL shortener: capacity estimation (1B reads/day, 900GB storage over 5 years), short code generation via distributed ID + Base62 encoding, read-heavy architecture with Redis caching (90%+ hit rate), MySQL sharding by short code prefix, 302 redirects for click tracking, async click counting via Kafka, and high availability patterns.

← Back to News