ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用system-design-primer建立系统设计思维:从CRUD到高并发架构

用system-design-primer建立系统设计思维:从CRUD到高并发架构 很多后端工程师都有过这种体验写业务代码时思路清晰一旦面试官抛出“请你设计一个类似 Twitter 的系统”大脑就会瞬间空白。不是不知道 API 怎么写也不是不会建表而是不知道从哪一步开始思考先算 QPS 还是先画架构图消息队列要不要上缓存穿透怎么解决分库分表用什么键在 GitHub 上system-design-primer 这个项目已经积累了极高的口碑。它不靠一两个“满分答案”教你背题而是用一套可重复的思考框架把系统设计这个开放性问题变成一个有步骤、有取舍、有输出的工程任务。我的判断是如果你想在系统设计面试中稳定发挥或者想从“会写接口”走向“会设计系统”这个开源仓库是最不应该绕过的学习材料之一。这篇文章不打算逐章翻译项目内容而是做三件事第一拆解 system-design-primer 的核心模块和正确打开方式第二把系统设计题的通用解题流程提炼成五个可复用步骤第三用一个短网址服务案例从需求、估算、建表到接口实现完整走一遍。读完以后你会有一份立刻可以上手的学习地图。1. 这篇文章真正要解决的问题先讲一个常见困境。很多后端工程师日常工作并不轻松写接口、改需求、查慢 SQL、优化页面性能。这些工作有价值但它和“系统设计”之间隔着一道认知鸿沟。日常任务通常已经把架构选型、组件选型都打包好了你只需要在应用层写业务逻辑而系统设计题要求你从零开始自己决定用不用缓存、要不要消息队列、数据库怎么分片、一致性怎么保证。system-design-primer 解决的正是这道鸿沟。它不是让你背一个高并发系统的标准答案而是帮你建立一套可以复用的系统设计思维框架。你会看到类似这样的训练内容给定一个业务场景先收集需求再估算流量然后设计数据模型接着选择架构组件最后回到约束条件检查方案是否合理。这套流程一旦熟练面对大多数开放设计题你就不会手足无措。这个项目最适合哪几类读者我的建议是准备中高级后端岗位系统设计面试的人工作中开始涉及模块拆分、服务治理、性能优化想系统补架构知识的人带团队做技术方案评审希望判断一个设计是好是坏的工程师对分布式系统、缓存、消息队列、数据库分片只有零散理解想把这些点串成体系的人。不太适合谁如果只是想找一份答案背下来应付面试那收获会非常有限。系统设计是技能不是知识。技能需要反复练习看答案只会带来“我懂了”的错觉。这一点在后面的实践章节里会反复验证。2. system-design-primer 是什么核心模块与使用价值system-design-primer 是一个 GitHub 开源仓库作者是 donnemartin。它的定位不是软件源码而是一套系统设计学习资源集合。最开始主要是为了帮助工程师准备系统设计面试后来因为内容系统、案例丰富也被很多团队用作内部架构培训材料。打开仓库后你会看到几大核心模块。下面用表格做一个快速总览模块内容适合用途Learning Objectives系统设计基础概念、指标、一致性模型入门第一周建立概念宽度System Design Questions多个经典面试题及分步解答刷题、建立答题框架Object-Oriented Design Questions类设计、对象模型题目补充面向对象设计能力Anki Flashcards可导入 Anki 的记忆卡片碎片化记忆术语与概念Appendix架构组件、公司架构、扩展阅读深入单一方向时检索这个项目最值得称道的是每道系统设计题都保持了统一结构先列需求再做规模估算然后从数据模型、高层设计、组件细节一层层展开最后讨论扩展性。这种结构上的统一性本身就是很好的训练素材因为真正的系统设计面试也强调结构化表达。如果能把这种表达内化成习惯哪怕遇到没见过的题目也能顺着骨架慢慢展开。当然它也有局限性。项目资料为英文部分术语直接翻译过来容易产生偏差另外题解更偏向经典方案对服务网格、Serverless 等较新的云原生内容覆盖较少。所以它更适合作为学习的骨架而不是唯一知识来源。看完题解以后还需要结合具体业务和新技术做二次加工。3. 为什么要学系统设计从 CRUD 到架构思维要真正用好这个项目得先理解“系统设计”和日常开发的差异。日常业务开发中你通常在一个边界清晰的系统里工作数据库已经建好、缓存集群已经搭好、消息队列也已经接入你只需要关注业务逻辑和 API 层。这时候你思考的单位是方法和数据表。系统设计题不一样。它的思考单位是“组件”和“链路”用户从浏览器发起请求经过 DNS、CDN、负载均衡、应用服务、缓存、数据库、日志监控每一环都可能成为瓶颈也都存在替代方案。你需要判断哪一层放缓存收益最大写入要强一致还是最终一致数据库不满足性能时是加索引、读写分离还是分库分表这里可以打个比方。写单个接口就像装修一间卧室墙面颜色、床的位置、插座数量都很明确而设计一个系统就像设计一栋楼要考虑承重结构、消防通道、水电容量、电梯数量还要预留未来加层的可能性。后者几乎没有唯一答案只能在成本、效率、可靠性之间做取舍。在 system-design-primer 里你会高频遇到几组核心概念可用性与可靠性一个强调服务能持续响应一个强调服务不宕机、数据不丢失延迟与吞吐量延迟是单个请求的快慢吞吐量是系统整体的处理能力一致性与分区容错分布式系统很难同时做到强一致和高可用必须做取舍可扩展性是垂直扩展加大机器配置还是水平扩展增加机器数量。这些术语单独看都不难难的是在具体场景中知道取舍依据。system-design-primer 的价值在于它用大量题解告诉你什么时候应该上缓存什么时候引入消息队列什么时候必须做数据库分片。系统设计的本质不是画一张漂亮的架构图而是做 trade-off。4. 环境准备与资料获取这个项目不需要配置复杂的开发环境因为它不是一个可运行的软件服务。你需要的只是一台能访问 GitHub 的电脑、一个能看 Markdown 的工具以及一个愿意做笔记的习惯。4.1 获取仓库最推荐的方式是直接使用 git clone 把仓库拉到本地git clone https://github.com/donnemartin/system-design-primer.git cd system-design-primer如果当前网络访问 GitHub 不稳定也可以在仓库页面直接点击 Download ZIP下载后离线阅读。仓库的 README 和主体内容都是英文建议不要只依赖翻译插件关键术语还是要理解英文原文。拉取下来以后先用ls -la看一下目录结构找到solutions/system_design和flashcards这两个目录它们是你接下来学习的主战场。4.2 阅读工具建议VS Code安装 Markdown Preview Enhanced 或 Markdown All in One 插件左侧目录导航方便Typora适合沉浸式阅读和写学习笔记Anki项目提供了 Anki flashcard导入 Anki 后可以碎片化记忆术语。不推荐用浏览器直接在 GitHub 上长时间阅读因为缺少本地搜索和收藏剪藏不太方便。本地克隆后你可以用 IDE 的全局搜索快速找到某个概念在哪道题里出现过这种检索能力在学习后期会非常有价值。4.3 建议的学习路径不要试图从头到尾把仓库读完那样大概率在“基础概念”部分就会放弃。推荐这样安排先读 README理解项目的学习目标和章节结构读基础概念部分掌握可用性、扩展性、一致性、CAP 定理等选 2 到 3 道常见系统设计题例如短网址服务、新闻流、聊天系统边读边用自己的话写一版方案再对比官方题解最后用 Anki 做术语复习让概念进入长期记忆。这样安排一到两周就能建立整体框架之后再根据兴趣或面试需求深入特定题目。5. 系统设计核心流程从需求到架构的五个步骤在 system-design-primer 的题解里几乎所有系统设计题都会在几十页材料中重复同一个骨架。把这个骨架提取出来就等于拿到了一个通用模板。我将它归纳为五个步骤。5.1 第一步明确需求与约束拿到题目后不要急着画架构图。先和面试官或产品经理把问题边界对齐。功能需求是“用户上传图片”还是“用户分享图片”非功能需求要求 99.9% 可用性还是允许短时降级目标用户是百万级还是亿级只有先把这些问题列清楚后面的设计才有依据。这一阶段的核心产出是一份需求清单。面试时你可以直接说“我先把需求明确一下避免设计方案偏离实际。”这段话本身就会给面试官留下结构感。容易踩的坑是在没有确认规模的情况下直接设计结果后面发现数据量和预想差一个数量级整张架构图都要推翻重来。5.2 第二步规模估算规模估算不需要做到精确但要有量级概念。一个常见的估算思路是先假设日活跃用户数再估计每个用户每天产生多少次操作最后除以 86400 秒得到平均 QPS再乘一个峰值系数得到峰值 QPS。举个例子假设日活 100 万用户平均每人每天写入 1 条数据那么写入 QPS 100万 / 86400约等于 12 次/秒峰值按 3 倍算约 36 次/秒。如果读写比是 100:1读 QPS 就是 1200峰值约 3000。有了这些数字你才能判断“单表能否扛住”“是否需要缓存”“要不要上消息队列”。面试官并不指望你算到小数点后两位但你要证明自己有容量规划意识。5.3 第三步设计数据模型数据模型是系统设计的地基。你需要明确核心实体和关系选择合适的存储介质关系型数据库适合事务和复杂查询KV 存储适合简单读取列式存储适合分析型场景对象存储适合图片视频等文件。数据模型阶段还要考虑数据增长速度。如果一年后单表数据量达到上亿行是不是一开始就要考虑分片如果对一致性要求很高能不能接受从库同步延迟这些问题越早回答后面的架构设计越平稳。5.4 第四步高层架构设计从客户端到服务端的完整链路是系统设计的灵魂。典型链路是客户端 - DNS/CDN - 负载均衡 - 应用服务 - 缓存 - 数据库 - 异步任务或外部服务。画架构图时不需要追求完美关键是说清每个组件的职责负载均衡负责流量分发应用服务负责业务逻辑缓存负责减少数据库压力数据库负责持久化。画完图以后可以用一段话描述请求在链路中的完整流转这一步能立刻暴露你没有想清楚的地方。5.5 第五步深入组件与扩展高层架构跑通后再回到约束条件做局部优化。热点数据要不要加缓存缓存过期策略怎么设计写多读少要不要通过消息队列削峰数据量大了以后分片键怎么选多机房容灾怎么落地这一步往往是面试官深挖的地方。建议每个组件选择都准备一个“为什么不用其他方案”的解释。比如用 Redis 而不是本地缓存是因为多实例需要共享引入消息队列是因为下游可以容忍异步。这样的回答会明显强于“这个场景大家都这么用”。为了方便记忆可以用下面这张表概括五步流程步骤核心问题输出物常见错误1 需求要解决什么问题需求列表没确认规模就画图2 估算流量和存储多大QPS、存储量凭感觉报数字3 数据如何存储和索引表结构、存储方案不考虑数据增长4 架构请求如何流转高层架构图堆组件但说不清理由5 优化瓶颈如何解决组件取舍清单只扩容不做 trade-off6. 实战演示设计一个短网址服务接下来用一个经典系统设计题把上面的流程走一遍。短网址服务在 system-design-primer 中也有类似方案这里做最小化演示重点展示每一步的输出。6.1 需求与约束假设我们要做一个短网址服务。功能需求包括用户提交一段长 URL得到一段短 URL用户访问短 URL 时服务返回 301 或 302 跳转到原始 URL短 URL 可以设置过期时间。非功能需求包括系统读多写少读 QPS 远大于写 QPS跳转延迟要低最好在几十毫秒内服务需要高可用不能因为单点故障导致短链不可用。下面用这些假设作为设计约束。6.2 规模估算假设每天新增 100 万条短链那么平均写 QPS 100万 / 86400约 12峰值写 QPS 约 30。如果读写比按 100:1 估算峰值读 QPS 约 3000。每条记录按 512 字节估算一年新增的数据量约为 100万 * 365 * 512B约等于 187GB。从量级看单机 MySQL 在数据量尚可时能够支撑存储但读 QPS 到 3000 时单库直连会有明显压力。因此必须在数据库前面加一层缓存至少覆盖热点短链的读取请求。6.3 数据模型与存储选型核心表结构可以这样设计CREATE TABLE url_map ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, short_code VARCHAR(16) NOT NULL UNIQUE, original_url VARCHAR(2048) NOT NULL, user_id BIGINT UNSIGNED NULL, expires_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有几个设计要点short_code是短链的唯一标识必须加唯一索引original_url使用VARCHAR(2048)足够覆盖绝大多数 URLexpires_at用来支持过期短链如果短码由发号器生成id也可以改成雪花算法生成的分布式 ID。对于读多写少场景可以在 MySQL 前面加 Redis 缓存。Redis 以short_code为 key存储原始 URL过期时间可以配置为短链的有效期。这样大部分读取请求都不需要落到数据库。6.4 短码生成算法短链的核心是把一个唯一 ID 转成短码。常见方案有两种。第一种是全局自增 ID 加 Base62 编码发号器生成唯一 ID再把 ID 转成 62 进制短码。好处是短码无重复、长度短缺点是 ID 连续容易被遍历可以在编码前加随机混淆。第二种是对原始 URL 做哈希用 SHA-256 或 MD5 生成摘要后取前几位但需要处理哈希冲突。下面用 Python 给出 Base62 编码的示例import string BASE62 string.digits string.ascii_letters def encode_base62(num: int) - str: if num 0: return 0 code while num 0: num, rem divmod(num, 62) code BASE62[rem] code return code def generate_short_code(unique_id: int) - str: # 对 ID 做一次轻量混淆避免短码按序排列 obfuscated_id (unique_id * 37 11) % (62 ** 7) return encode_base62(obfuscated_id).zfill(7)这段代码可以放在发号器下游作为编码函数。真实生产环境中unique_id需要保证全局唯一通常由数据库自增主键或分布式发号器提供。这里用了一个简单的线性混淆让短码看起来不是连续递增的降低被遍历猜测的风险。6.5 API 设计与整体架构短网址服务的写接口可以这样设计POST /api/v1/shorten Host: api.example.com Content-Type: application/json { url: https://www.example.com/very/long/url, expires_in_days: 30 }预期响应{ short_url: https://short.example/xK9f2a }访问短链时客户端请求GET https://short.example/xK9f2a服务端先查 Redis未命中则查 MySQL再写回缓存最后返回 301 跳转。整体链路可以这样描述用户请求到达 DNS 后由负载均衡分发到短链服务短链服务先读 Redis 缓存缓存未命中再读 MySQL写接口则先做限流再生成短码写入数据库并更新缓存。监控和日志通过异步方式收集不影响主链路。还可以在入口层做限流避免短链接口被脚本刷爆。下面是 Nginx 层限流的示例配置limit_req_zone $binary_remote_addr zoneshorten_limit:10m rate10r/s; server { listen 443 ssl; server_name api.example.com; location /api/v1/shorten { limit_req zoneshorten_limit burst20 nodelay; proxy_pass http://backend; } }这里限制每个 IP 每秒最多写入 10 次突发允许到 20 次。既能防止恶意刷接口又不至于影响正常用户。6.6 运行与验证在本地测试写接口可以用 curl 模拟curl -X POST https://api.example.com/api/v1/shorten \ -H Content-Type: application/json \ -d {url: https://www.example.com/very/long/url, expires_in_days: 30}预期返回一个包含short_url的 JSON。接着访问短链curl -I https://short.example/xK9f2a预期看到HTTP/1.1 301 Moved Permanently并且响应头中的Location指向原始 URL。如果第一步失败先排查 DNS 解析、证书和后端服务存活状态如果后端返回 500再查看数据库连接池和 Redis 连接配置是否正常。这个案例虽然不是一个完整生产实现但覆盖了系统设计面试的主要环节需求、估算、存储、算法、接口、缓存、限流。你可以用同样的流程去套其他题目。7. 如何用 system-design-primer 验证学习效果读完项目、刷完几道题以后你还需要一套自检方法确认自己是真的掌握了而不是“眼睛会了”。7.1 Anki 记忆卡检验项目提供了 Anki flashcards 目录导入 Anki 后每天花 15 分钟复习术语。如果能在不看原文的情况下说出 CAP、最终一致性、读写分离、缓存穿透这些概念说明概念层基本过关。如果看到卡片还要想很久说明基础概念还没有真正进入长期记忆需要继续刷。7.2 白纸复盘法学完一道题后不要马上去看官方题解。拿出一张白纸或一个空白画布限制 30 分钟尝试画出完整架构图写下规模估算过程和数据模型。画完以后再对照 system-design-primer 中的 solution 看差异。重点不是追求完全一致而是观察自己在哪一步卡住了是需求没想清楚还是组件选型没有依据。卡住的位置就是你下一步要补的内容。7.3 模拟面试打分给自己准备三道经典题短网址服务、新闻流、聊天系统。对每道题都按五步法表达并用手机录音。回听时重点检查是否在两分钟以内说清需求是否能给出量级正确的估算架构图是否无清晰链路能否解释每个组件存在的理由如果 10 分钟内讲完需求、估算和高层架构说明框架已经比较稳定。如果讲不完就把五步法做成小卡片面试前反复过。7.4 自测问题清单自测项合格标准不达标时的动作能区分可用性、一致性能结合 CAP 解释重读基础概念 Anki能快速估算 QPS 和存储算得单位正确练 3 道题的规模估算能画出系统架构图链路清晰白纸复盘法能解释一个取舍说明为什么选缓存对照题解找“原因”段落验证学习效果本质上不是“读完”而是“能在限定时间内重新输出”。输出比输入重要得多。8. 学习过程中的常见问题与排查思路很多人学习 system-design-primer 时会遇到类似的问题下面整理成一个排查表格遇到哪一种就对照处理。问题现象可能原因排查方式解决方案仓库内容太多不知道从哪开始没有明确学习路径先读 README 和目录采用五步学习路径先看 3 道经典题英文阅读速度慢术语陌生阅读量大先看基础概念章节中英对照学习先理解术语再读题解看得懂题解但自己写不出被动阅读缺少输出用白纸复盘法重画每天限时重画一道题再对比规模估算没有头绪缺少数量级感觉从日活和读写比入手记住公式DAU/86400 得到平均 QPS不知道何时用缓存、队列只背组件不记适用条件问自己“不选会怎样”每个选择写一个 trade-off 表格面试时忘记流程框架没有内化模拟面试录音把五步法做成卡片面试前复习其中最常见的就是“看得懂但写不出”。这个问题的根源通常是阅读占用了太多时间而输出练习太少。解决办法很简单每读一道题必须强制自己在不看答案的前提下重新输出一版设计。哪怕很粗糙也要先写出来。9. 最佳实践与工程建议系统设计能力不能靠一个月突击但如果方法对成长速度会很快。这里分享几组工程实践经验。9.1 每周一题写设计文档每周选一道 system-design-primer 中的题目按照需求、估算、数据模型、架构、权衡五个部分写一份简短设计文档。不要只写
返回列表