在技术面试中,系统设计问题是考察候选人架构思维的关键环节。Tech Interview Handbook(https://github.com/yangshun/tech-interview-handbook)作为备受欢迎的面试准备资源,虽然系统设计部分仍在完善中,但其推荐的外部资料如 Grokking the System Design Interview 和 ByteByteGo 课程提供了宝贵蓝图。本文聚焦分布式系统经典案例:URL 短链服务、速率限制器(Rate Limiter)和键值存储(Key-Value Store),分析其设计要点、可扩展性权衡,并给出工程化参数清单。最后,结合 LeetCode JS 挑战,提供可落地编码实践。
URL 短链服务设计:从单机到分布式扩展
URL 短链服务(如 TinyURL)核心功能包括生成短链(长 URL → 短码)和重定向(短码 → 长 URL)。假设日活跃用户 1 亿,读写比 10:1,每日生成 1 亿短链,则峰值 QPS 约 200 写 / 2000 读,10 年存储需求超 36 TB。
高层架构:
- API 层:POST /shorten (输入长 URL,返回短链);GET /{shortCode} (HTTP 301/302 重定向)。
- 应用服务:负载均衡(Nginx/LB)后多节点无状态服务。
- 存储:Redis 缓存(短码 → 长 URL) + MySQL/PostgreSQL(持久化)。使用一致性哈希分片。
- 扩展:CDN 加速重定向,Kafka 异步日志分析点击。
核心算法选择与权衡:
- 哈希 + 冲突解决:MD5/SHA1 长 URL,取前 7 位 Base62 编码(62^7 ≈ 3.5 万亿容量)。冲突用布隆过滤器(Bloom Filter)预查,概率 <0.01%。优点:均匀分布;缺点:潜在冲突需额外查询。
- 计数器 + Base62:全局唯一 ID(雪花算法或 DB 自增)转 Base62。优点:无冲突,易排序;缺点:ID 暴露安全隐患,可用时间戳 + 随机掩码。
落地参数:
- 短码长度:6-8 位(Base62)。
- 缓存 TTL:7 天(读热数据)。
- DB 分片键:shortCode Hash % 1024 分 1024 库。
- 监控:QPS >80% 阈值扩容;冲突率 >0.1% 调哈希盐值。
扩展 tradeoffs:强一致性(ACID DB)牺牲可用性 → 最终一致性(Redis + 异步同步),CAP 中选 AP。单机 MySQL 扛 2k QPS,分布式需分片 + 读从库。
速率限制器:防护 DDoS 与资源滥用
Rate Limiter 防止 API 滥用,如每 IP / 用户 5 req/s。常见于短链生成接口。
算法实现:
- 令牌桶(Token Bucket):固定速率放令牌(e.g., 每秒 5 个),请求取令牌。Redis 原子操作:INCR + EXPIRE。
- JS 示例(LeetCode 相关):
// 简化令牌桶 const rateLimit = (key, limit = 5, window = 1) => { const now = Date.now(); const redisKey = `rate:${key}:${Math.floor(now / (window * 1000))}`; const tokens = await redis.get(redisKey) || limit; if (tokens > 0) { await redis.decr(redisKey); return true; } return false; };
- JS 示例(LeetCode 相关):
- 滑动窗口(Sliding Window):记录时间戳计数,过期滑动。Redis ZSET 存储。
集成与 scaling:
- 网关层(API Gateway)预过滤。
- 分布式:Redis Cluster,一致性哈希分桶。
- 参数:桶大小 10, refill_rate 5/s;告警阈值 90%。
Tradeoffs:固定窗口简单但突发峰值不准 → 滑动窗口精确但内存高。选 CP(令牌桶)防护关键路径。
键值存储:高性能读写与一致性平衡
KV Store 如 Redis/DynamoDB,支持海量简单查询。设计目标:10w+ QPS,低延迟。
架构蓝图:
- 读路径:客户端 → LB → Cache(Memcached/Redis) → Master/Slave → Persistent(RocksDB)。
- 写路径:异步复制(Raft/Paxos),多副本(3 副本)。
- 分片:一致性哈希环(虚拟节点 100 / 物理节点),动态扩容最小重哈希。
- 持久化:AOF + RDB,WAL 日志。
扩展 tradeoffs:
| 维度 | 方案 | 优点 | 缺点 |
|---|---|---|---|
| 一致性 | 强(2PC) | ACID | 低可用,高延迟 |
| 最终(Gossip) | 高可用 | 短暂不一致 | |
| 扩展 | 分片 | 线性 scale | 热点问题 |
| 复制 | 读扩展 | 写放大 |
参数清单:
- 副本数:3,RTO <5s,RPO <1s。
- 哈希槽:16384(Redis)。
- 淘汰:LRU,maxmemory 80%。
- 监控:Slow log >100ms,OOM 率 0。
LeetCode JS 挑战实践:编码验证设计
Handbook 强调 JS 算法(如 Grind 75)。相关挑战:
- 146. LRU Cache(限流缓存用):双链表 + Map,O (1) get/put。
-
- MedianFinder(计数器):双堆维护中位。
- 面试 JS:实现简易 KV(Map + TTL):
class SimpleKV { constructor(ttl = 3600) { this.store = new Map(); this.ttl = ttl * 1000; } set(k, v) { this.store.set(k, { v, exp: Date.now() + this.ttl }); } get(k) { const item = this.store.get(k); if (item && Date.now() < item.exp) return item.v; this.store.delete(k); return null; } }
这些设计非孤立:短链用 KV + 限流,整体监控 Prometheus + Grafana。回滚策略:蓝绿部署,特征开关。
资料来源:
- Tech Interview Handbook GitHub。
- Grokking System Design Interview 案例。
- LeetCode 设计题 JS 解。
掌握这些蓝图,面试中清晰阐述 tradeoffs,即可脱颖而出。实践参数调优,确保生产级可靠性。(字数:1256)