1系统设计面试:内幕指南(第2版)

系统设计面试步骤

面试方法

需求澄清高层设计详细设计

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第1章:系统设计面试步骤(Steps for System Design Interview)

一、导读

1.1 本章学习目标

系统设计面试没有标准答案,但有标准流程。第1章建立了一套结构化的面试方法论,分为四个步骤。Alex Xu 通过大量实例展示了如何在45分钟内有条理地完成一个完整的系统设计。

本章的核心学习目标包括:

  • 掌握四步面试框架:需求澄清 → 高层设计 → 详细设计 → 总结扩展。
  • 学会区分功能需求和非功能需求:前者定义"系统做什么",后者定义"系统做得多好"。
  • 理解数据规模估算的重要性:QPS、存储、带宽的估算决定架构选型。
  • 掌握高层设计的画图技巧:如何用最少的组件表达核心架构。
  • 学会详细设计的深入方向:数据库 Schema、API 设计、关键算法。
  • 了解常见面试错误:如何避免"过度设计"和"忽视非功能需求"。

1.2 为什么本章如此重要

Alex Xu 在开篇就指出:系统设计面试考的不是你能设计多复杂的系统,而是你能否在有限时间内展示结构化的思考能力。很多候选人技术能力很强,但因为缺乏面试方法论,在45分钟内无法展示完整的思考过程,最终遗憾落选。

本章的四步框架是全书的方法论基础,后续每一章的案例分析都严格遵循这个框架。掌握这个框架,你就拥有了解决任何系统设计题目的通用工具。

1.3 核心问题引导

系统设计面试的四个步骤分别应该花多少时间?

功能需求和非功能需求的区别是什么?如何向面试官澄清?

数据规模估算(Back-of-the-envelope calculation)为什么重要?

高层设计应该包含哪些核心组件?

详细设计应该深入到什么程度?

面试官最看重候选人的哪些能力?


二、核心概念详解

2.1 第一步:需求澄清(5分钟)

2.1.1 功能需求

功能需求定义"系统能做什么"——用户可以使用哪些功能。

// 功能需求澄清模板:
// 面试官:"请设计一个 URL 短链服务"
//
// 候选人应该问的问题:
// 1. 短链服务的基本功能是什么?
//    → 用户输入长 URL,生成短 URL
//    → 访问短 URL,重定向到长 URL
//
// 2. 用户是否可以自定义短链接?
//    → 是/否(影响设计复杂度)
//
// 3. 是否需要用户认证?
//    → 匿名用户也可以使用?
//
// 4. 是否需要统计功能?
//    → 点击次数、访问来源、访问时间
//
// 5. 短链接是否有过期时间?
//    → 永久有效 or 可设置过期
//
// 6. 是否支持 URL 删除?

2.1.2 非功能需求

非功能需求定义"系统做得多好"——性能、可用性、一致性等。

// 非功能需求澄清模板:
//
// 1. 流量规模:
//    → DAU(日活用户)?
//    → 每日 URL 缩短次数?
//    → 每日重定向次数?
//
// 2. 性能要求:
//    → 短链生成的响应时间?(< 200ms)
//    → 重定向的响应时间?(< 100ms)
//
// 3. 可用性要求:
//    → 99.9% 还是 99.99%?
//
// 4. 一致性要求:
//    → 短链生成后立即可用?(强一致)
//    → 还是允许短暂延迟?(最终一致)
//
// 5. 特殊需求:
//    → 高并发场景?(如社交媒体分享链接)
//    → 地理分布?(全球用户 vs 单一地区)

2.1.3 数据规模估算

// Back-of-the-envelope 计算:
//
// 假设:
// - 每月新增短链:1亿
// - 每个短链数据:长URL(500B) + 短码(7B) + 创建时间(8B) + 过期时间(8B) ≈ 523B
// - 读写比:10:1(重定向次数远大于创建次数)
//
// 存储估算:
// - 每月存储增量:1亿 × 523B ≈ 50GB
// - 5年存储总量:50GB × 60个月 = 3TB
//
// QPS 估算:
// - 每月新增 QPS:1亿 / (30 × 24 × 3600) ≈ 40 QPS
// - 峰值 QPS:40 × 3 = 120 QPS(假设峰值是均值的3倍)
// - 重定向 QPS:120 × 10 = 1200 QPS
//
// 带宽估算:
// - 写入带宽:120 × 523B ≈ 60KB/s
// - 读取带宽:1200 × 523B ≈ 600KB/s
//
// 结论:
// - 存储需求不大(3TB/5年)
// - 读 QPS 较高(1200),需要考虑缓存
// - 写 QPS 不高(120),单数据库可以承受

2.2 第二步:高层设计(15分钟)

2.2.1 核心组件识别

// URL 短链服务的高层设计:
//
// [Client] → [Load Balancer] → [API Server]
//                                    ↓
//                            [URL Service]
//                              ↓         ↓
//                         [Database]  [Cache]
//
// 核心组件:
// 1. Load Balancer:分发请求到多个 API Server
// 2. API Server:接收请求,调用 URL Service
// 3. URL Service:核心业务逻辑(生成短码、查找长URL)
// 4. Database:存储长短URL映射
// 5. Cache:缓存热门短链,减少数据库压力

// 数据流:
// 短链生成:
// Client → LB → API Server → URL Service → Database (存储映射)
//
// 重定向:
// Client → LB → API Server → URL Service → Cache/Database (查找长URL)
//                                              ↓
//                                        301/302 Redirect

2.2.2 画图技巧

// 高层设计图的关键原则:
// 1. 只画核心组件(5-8个),不要画太多细节
// 2. 用箭头表示数据流方向
// 3. 标注关键组件的职责
// 4. 标注读写比例和 QPS

// 好的高层设计图:
// [Client] → [LB] → [Web Server × N]
//                        ↓
//                   [URL Service]
//                     ↓       ↓
//                [MySQL]   [Redis]
//               (持久化)   (缓存)
//
// 标注:
// - 读QPS: 1200, 写QPS: 120
// - 读写比: 10:1
// - 缓存命中率目标: 80%

// 不好的高层设计图:
// - 画了太多组件(20+个)
// - 没有标注数据流方向
// - 没有标注 QPS 和读写比
// - 过度深入细节(如具体的表结构)

2.3 第三步:详细设计(20分钟)

2.3.1 数据库 Schema 设计

sql
-- URL 映射表
CREATE TABLE url_mapping (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    short_code VARCHAR(7) UNIQUE NOT NULL,
    long_url VARCHAR(2048) NOT NULL,
    user_id BIGINT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expiration_at TIMESTAMP NULL,
    INDEX idx_short_code (short_code),
    INDEX idx_user_id (user_id),
    INDEX idx_created_at (created_at)
);

-- 设计决策:
-- 1. short_code 使用 VARCHAR(7):62^7 ≈ 3.5万亿种组合,足够使用
-- 2. long_url 使用 VARCHAR(2048):HTTP URL 最大长度
-- 3. user_id 可为空:支持匿名用户
-- 4. expiration_at 可为空:支持永久链接

2.3.2 短码生成算法

// 方案1:哈希 + 碰撞处理
// 1. 对长URL计算 MD5/SHA256
// 2. 取前7个字符作为短码
// 3. 如果冲突,追加随机字符重新哈希
//
// 优点:简单
// 缺点:无法预知短码,碰撞时需要重试

// 方案2:Base62 编码(推荐)
// 1. 使用自增ID作为唯一标识
// 2. 将ID转换为Base62编码(a-z, A-Z, 0-9)
// 3. ID=1000 → 短码="gk"
//
// Base62 编码:
// 字符集:0-9 (10) + a-z (26) + A-Z (26) = 62个字符
// 7位短码可以表示:62^7 ≈ 3.5万亿个URL
//
// 优点:短码唯一,无碰撞
// 缺点:短码可预测(安全性低)

// 方案3:预生成短码池
// 1. 预先批量生成随机短码,存入数据库
// 2. 每次请求从池中取出一个使用
// 3. 池低于阈值时补充
//
// 优点:生成速度快,短码不可预测
// 缺点:需要管理短码池

// 推荐方案2(Base62),简单高效

2.3.3 API 设计

// RESTful API 设计:

// 1. 创建短链
POST /api/v1/shorten
Content-Type: application/json
{
    "long_url": "https://www.example.com/very/long/url",
    "custom_alias": "mylink",    // 可选
    "expiration": "2025-12-31"   // 可选
}

Response 201:
{
    "short_url": "https://short.ly/abc123",
    "long_url": "https://www.example.com/very/long/url",
    "created_at": "2024-01-15T10:30:00Z"
}

// 2. 重定向(核心接口)
GET /abc123
Response: 301 Moved Permanently
Location: https://www.example.com/very/long/url

// 3. 查询短链信息
GET /api/v1/shorten/abc123
Response 200:
{
    "short_code": "abc123",
    "long_url": "https://www.example.com/very/long/url",
    "created_at": "2024-01-15T10:30:00Z",
    "click_count": 1523
}

2.4 第四步:总结与扩展(5分钟)

2.4.1 瓶颈分析

// 系统瓶颈分析:
//
// 1. 数据库瓶颈:
//    - 单MySQL写QPS约1000,当前写QPS=120,暂时够用
//    - 读QPS=1200,通过Redis缓存(命中率80%)减少到240
//    - 如果流量增长10倍,需要数据库分片
//
// 2. 缓存瓶颈:
//    - Redis单实例QPS约10万,当前读QPS=1200,完全够用
//    - 需要Redis集群保证高可用
//
// 3. 单点故障:
//    - API Server:多实例 + 负载均衡
//    - 数据库:主从复制 + 自动故障转移
//    - Redis:Redis Sentinel 或 Cluster

2.4.2 扩展方向

// 可能的扩展方向:
// 1. 统计分析:记录每次点击,生成访问报告
// 2. CDN 集成:热门短链的重定向通过 CDN 处理
// 3. 地理分布:多区域部署,就近访问
// 4. 限流和防滥用:防止恶意生成大量短链
// 5. A/B 测试:同一短链根据条件重定向到不同目标

三、实战案例

3.1 案例一:完整面试模拟——设计 Pastebin

// 第一步:需求澄清(5分钟)
// 功能需求:
// 1. 用户可以粘贴文本,生成分享链接
// 2. 访问链接可以看到原始文本
// 3. 文本支持设置过期时间
// 非功能需求:
// 1. 高可用:99.99%
// 2. 低延迟:< 200ms
// 3. DAU:1000万
// 4. 每日新增 Paste:100万

// 第二步:高层设计(15分钟)
// [Client] → [CDN] → [LB] → [API Server × N]
//                                    ↓
//                            [Paste Service]
//                              ↓         ↓
//                         [PostgreSQL] [Redis]

// 第三步:详细设计(20分钟)
// 数据库:
// CREATE TABLE pastes (
//     paste_id VARCHAR(8) PRIMARY KEY,
//     content TEXT NOT NULL,
//     user_id BIGINT,
//     created_at TIMESTAMP,
//     expiration_at TIMESTAMP NULL
// );
//
// 短ID生成:Base62编码自增ID
// 缓存策略:LRU,缓存最近访问的 Paste

// 第四步:总结(5分钟)
// 瓶颈:大文本存储、CDN缓存策略
// 扩展:语法高亮、密码保护、文件上传

3.2 案例二:面试评分标准

// 面试官评估维度:

// 1. 需求澄清(20%)
//    - 是否主动澄清功能和非功能需求?
//    - 是否进行了合理的数据规模估算?

// 2. 高层设计(30%)
//    - 是否识别了核心组件?
//    - 数据流是否清晰?
//    - 是否考虑了负载均衡和缓存?

// 3. 详细设计(30%)
//    - 数据库 Schema 是否合理?
//    - API 设计是否 RESTful?
//    - 关键算法是否正确?

// 4. 沟通与总结(20%)
//    - 是否主动与面试官沟通?
//    - 是否分析了瓶颈和扩展方向?
//    - 时间管理是否合理?

四、本章小结

本章建立了系统设计面试的标准化方法论:

核心知识点:

  • 四步框架:需求澄清(5min)→ 高层设计(15min)→ 详细设计(20min)→ 总结扩展(5min)。
  • 功能需求定义"做什么",非功能需求定义"做得多好"。
  • 数据规模估算(QPS、存储、带宽)决定架构选型。
  • 高层设计只画核心组件(5-8个),标注数据流和 QPS。
  • 详细设计深入到数据库 Schema、API 设计和关键算法。
  • 面试评估维度:需求澄清20%、高层设计30%、详细设计30%、沟通总结20%。

五、思考与练习

思考题

如果面试官在需求澄清阶段就给出了所有需求,你应该怎么做?

数据规模估算的误差范围是多少?如何验证估算的合理性?

高层设计和详细设计之间的边界在哪里?

练习题

使用四步框架,完成"设计一个 URL 短链服务"的完整练习(严格计时45分钟)。

使用四步框架,完成"设计 Pastebin"的完整练习。

对比你的设计和参考答案,找出差距和改进方向。

1/6系统设计
0%