System Design Interview: URL Shortener to Feed System

The repeatable method: clarify → capacity estimation → API/DB → scale up → failure modes — worked on the three classics.

AdvancedUpdated 2026-10-2959K views

The method (use it out loud)

  1. Clarify scope: which features, read/write ratio, is the write one-time or continuous
  2. Capacity estimate (back-of-envelope): QPS, storage 5 years, bandwidth, memory
  3. API + data model: endpoints, tables, index direction
  4. Single-machine design first, THEN scale: cache, sharding, async queue, CDN
  5. Failure modes: single points, thundering herd, data loss windows
System design interviews grade the PROCESS as much as the result. Say your estimates out loud, state assumptions, then adjust when the interviewer probes — going mute while 'thinking' is scored as no signal.

Worked example: URL shortener

Requirements: short codes, redirects (read-heavy 100:1), custom aliases, expiry. Assume 100M new URLs/month, 10B reads/month.

容量速算(信封背面)
写:100M / 月 ≈ 40 QPS(月按 30 天 ≈ 2.6M 秒→ ~40/s,峰值 ×3 = 120 QPS)
读:10B / 月 ≈ 4000 QPS,峰值 ×3 ≈ 12k QPS(单 MySQL 抗不住 → 上缓存)
存储:100M × 500B(短码+长URL+元数据) ≈ 50GB/月 → 5 年 ≈ 3TB(单库够)

编码:6 位 Base62(62^6 ≈ 568 亿)绰绰有余

写路径:发号器(Snowflake/Redis incr)→ Base62 编码 → 存 MySQL(short=PK)
读路径:Redis 缓存 short→long(命中率 >95%)→ miss 回库
过期:TTL 或 异步清理任务
SchemeProCon
Hash(MD5) 取后 6 位无状态、可离线算可能冲突、不可预测
自增 ID + Base62短、有序、无冲突发号器单点 / 可被遍历
Snowflake + Base62分布式、可排序码稍长、机器位规划

Worked example: flash sale (秒杀)

Core reality: a flash sale is 99% rejected traffic. Design to reject cheaply before it touches the database.

分层防御(自外向内)
1. 客户端:按钮置灰 + 验证码(挡机器人)
2. CDN/网关:活动开始前静态页 + 限流(令牌桶)
3. Redis:预扣库存 Lua(单线程天然原子)+ 重复购买拦截(setnx uid:goods)
4. MQ:异步下单(削峰,库存成功才落库)
5. DB:最终扣减 + 兜底对账
  • Inventory must be pre-deducted atomically in Redis with Lua: DECR if and only if stock > 0
  • Orders land in a queue, DB consumption pace stays constant — the whole point of 削峰
  • Oversell safety = final reconciliation: queue messages, DB rows, actual goods shipped must match
The oversell incident is always 'we had a cache' but reconciliation was missing. Redis Lua pre-deduction is necessary but not sufficient — a queue/DB reconciliation job is the actual guarantee.

Friends & Partners