尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从理论到实战:系统设计笔记,覆盖高并发与架构设计全链路

从理论到实战:系统设计笔记,覆盖高并发与架构设计全链路 系统设计这门课我啃了快六年才敢说自己真正入门。期间翻过的资料、记过的笔记、画过的架构图摞起来比工位上的显示器都高。去年我把这些零散内容整理成了system-design-notes这个项目——一份从基础理论到实战案例全覆盖的系统设计笔记目的是让后来者不用再像我当年那样面对浩如烟海的英文博客和技术文档不知道从哪下嘴。这份笔记解决的核心痛点很明确系统设计知识太散、太杂网上资料要么高屋建瓴讲理论要么直接甩一个复杂架构图中间缺少一条从问题到方案的完整推理链。而实际工作中无论是做技术选型、评审架构方案还是准备晋升答辩都需要你具备接到一个模糊需求快速拆解出关键技术点再给出合理设计的能力。所以这套笔记适合所有想系统提升架构设计能力的人——不管是准备面试的候选人、刚带项目的技术负责人还是单纯对大系统是怎么造出来的好奇的后端工程师。1. 内容整体设计与思路拆解1.1 为什么需要一份自己的系统设计笔记先说说我踩过的坑。早期我也和其他人一样收藏了一堆Top 10 System Design Concepts之类的文章刷的时候觉得什么都懂了合上电脑一周后连一致性哈希的虚拟节点是干嘛的都讲不清楚。问题出在哪别人的笔记是别人的思维链不是你的。系统设计本质上是一种约束条件下的决策艺术同样的需求不同人基于自己的经验积累会做出完全不同的取舍。照搬别人的结论等于跳过思考直接记答案遇到变体题目立刻抓瞎。这就是我建立system-design-notes的初衷不是做一个知识搬运仓库而是把每次设计方案时的决策过程和取舍依据记录成结构化笔记。比如记录为什么这个场景选 Redis 而不是 Memcached我会写清楚当时考虑的缓存值大小、是否需要持久化、集群扩容的运维成本对比而不是简单一句话Redis 功能更强。这种笔记方式迫使你把每个技术选型背后的 trade-off 想透时间长了你会发现自己面对新问题时能自然形成一套分析框架。笔记项目的组织方式我参考了国外知名系统设计课程的大纲结构再按自己的理解做了裁剪。整体分四大块基础理论层一致性、可用性、分区容错等抽象概念、核心组件层负载均衡、缓存、消息队列等具体部件、案例实战层短链接、Feed 流、秒杀等完整设计、方法论层如何把模糊需求转化为技术方案。这个分层思路很重要——很多人学系统设计最大的问题是只见树木不见森林单个组件都认识组合起来就不会了。分层笔记能强迫你先建立全局视角再逐步深入细节。1.2 笔记项目的整体架构与组织方式具体到目录结构我的system-design-notes长这样01-basics/理论基础包含 CAP、BASE、一致性哈希、幂等设计等02-components/核心组件负载均衡、缓存、消息队列、分布式 ID 等03-cases/经典案例短链接、聊天系统、推荐系统、秒杀系统等04-methodology/方法论需求澄清、容量估算、架构演进思路05-interview/面试专项高频题目速查、答题模板、模拟演练记录assets/所有架构图的源文件我统一用 Draw.io 画方便随时改这跟传统的收藏夹式笔记有个本质区别每个专题不是一篇孤立的文章而是一条完整的推理链路。比如缓存这个专题我会从为什么需要缓存开始讲清楚缓存能解决什么问题、引入后会带来什么新问题缓存穿透、击穿、雪崩再给出每个问题的对应解法最后附上一个真实业务场景的选型对比。这样读者顺着笔记走一遍等于亲手做过一遍完整的架构推演而不是被动接受结论。说实话最开始整理这套笔记时我也犹豫过网上现成的笔记项目太多了自己再整理一份是不是重复造轮子但用下来的体会是整理笔记本身才是最有价值的环节。当我尝试把如何设计一个高可用系统这件事写成结构化文档时才发现自己对不少概念的理解是模糊的——比如我一直以为读写分离能解决所有数据库压力问题但真去算容量时才发现写多读少的场景下主库才是瓶颈。这个发现直接影响了我后续的技术方案偏好。2. 核心知识模块拆解2.1 从 CAP 到一致性模型先搞清楚你要什么任何一个系统设计绕不开的第一个决策点是在一致性、可用性、分区容错性之间怎么取舍。CAP 定理说分布式系统最多满足两个实际生产环境网络分区不可避免所以 P 是必选项真正纠结的是 C 和 A 怎么平衡。但这里有个新手特别容易误解的地方——CAP 里的一致性指的是线性一致性即所有节点在同一时刻看到完全相同的数据这是一种极端严格的定义。真实的业务系统几乎不会追求这种级别的强一致。比如电商下单后扣减库存允许用户稍后刷新才看到最新库存这就是最终一致性——系统保证在没有新写入的情况下经过一段时间后所有副本会收敛到相同状态。笔记里我专门做了一个一致性模型谱系图强一致、顺序一致、因果一致、最终一致从上往下约束递减、性能通常递增。实际设计时我建议先用一个简单问题判断如果两个用户同时读到不同版本的数据业务上能否接受不能接受就上强一致方案能接受就用最终一致加补偿机制。具体到工程实现一致性模型直接决定了存储选型。需要强一致就老老实实用单机数据库或 Raft 等共识算法支撑的分布式数据库能接受最终一致就可以放心上多副本异步复制甚至用消息队列做异步解耦。我在笔记里反复强调一个观点所谓架构设计本质是在业务容忍度和技术实现成本之间找最小交集。这个交集找得越准方案越简洁后续运维成本越低。2.2 高并发场景的三板斧缓存、异步、削峰高并发是系统设计里最常被提及的话题但很多人一上来就想着堆机器。实际经验告诉我99% 的性能问题都可以用缓存-异步-削峰三个手段解决只有极少数场景才需要真正意义上的分布式改造。笔记里我把这三板斧拆成了独立专题每个专题都配了真实业务场景。先说过载保护最核心的思路是让多余的流量在系统入口就失败而不是拖垮整个系统。比如秒杀场景如果十万人同时抢一百件商品业务上完全可以接受九万九千人看到已售罄那就不该让所有请求都打到数据库。做法通常是三层缓冲CDN 挡静态资源Redis 预扣库存挡动态请求数据库只承担最终的原子扣减。每一层都加上自己的限流阈值超出的请求直接返回失败保护后端的稳定性。异步解耦这块我的切身体会是消息队列最大的价值不是快而是稳。把写操作同步拆出去主流程只需保证消息已投递后续的积分、通知、搜索索引更新全部异步执行。这样即使下游某个服务挂了主流程不受影响消息积压等恢复后慢慢消费。笔记里我记录了某次线上故障的复盘促销活动期间用户通知服务超时因为同步调用链太长导致主链路 RT 飙到 3 秒。改成 MQ 异步之后主流程稳定在 200ms 以内这就是异步的价值。削峰也是类似逻辑。秒杀、抽奖这类瞬间流量极大的场景与其把机器按峰值容量买齐不如在入口加一个漏斗把瞬时流量拉平到一段时间内慢慢处理。最简单的手段就是队列削峰——请求进来先塞进队列后端按自己的处理能力匀速消费。这里有个容易踩的坑队列不是无限的要提前估算积压上限和消费者的最大吞吐量否则流量峰值远超消费能力时队列本身就成了新的瓶颈。2.3 数据层的扩展之路从分库分表到读写分离数据层往往是系统演进到一定阶段后最大的瓶颈。笔记里我记录了数据层扩展的完整路径单库单表 - 读写分离 - 垂直分库 - 水平分表。很多新手容易犯的错是过早做分布式改造实际上大多数业务在单库性能优化到位的前提下撑到几百万用户是没有问题的。我见过一个日活百万级的电商项目核心订单表就是单库靠的是合理的索引、归档和缓存数据库负载常年低于 20%。真正需要分库分表的信号很明确单表数据量过大导致索引失效或者写入并发触及单机瓶颈。这时候要考虑的不只是怎么拆更重要的是按什么维度拆。订单数据适合按用户 ID 取模拆分因为查询总是从我的订单出发消息数据适合按时间维度分表因为冷热数据分明旧数据直接归档。笔记里我特别标注了一个教训拆分维度如果和主要查询维度不一致会导致大量跨库查询性能反而更差。读写分离是另一个性价比很高的手段但实现时有个隐蔽的坑——主从延迟。刚写入的数据马上去从库读经常读不到。解决方案不外乎几种关键读请求强制走主库、写入后短暂缓存最近数据、或者业务上接受这种短暂不一致。笔记里我倾向于第三种方案最多因为大多数业务场景下写完立刻刷新并不是强需求设计时提前和产品对齐预期比在技术上绕来绕去省事得多。3. 实操过程与核心环节实现3.1 拿到一个设计题第一步该做什么不管是面试还是实际工作系统设计的起点永远是同一个动作澄清需求。我见过太多人包括当年的自己拿到题目就开始画架构图结果方向错了后面全白干。澄清需求要问的问题是有套路的笔记里我总结了一个五连问功能范围核心功能有哪些非核心功能可以先不做用户规模预估多少用户多少日活峰值 QPS 是多少数据规模数据总量多大每天新增多少需要存多久读写比例读多写少还是写多读少这决定了缓存和数据分片策略一致性要求业务上能否容忍短暂不一致能不能接受最终一致举个例子拿到设计一个短链接服务这个经典题目先不要想用什么技术栈而是先算数据量。假设日新增 100 万条短链每条记录 100 字节一年下来大约 36GB 数据、3.6 亿条记录。这个量级下单库完全撑得住。那问题核心就不是怎么水平扩展而是短链生成算法怎么保证唯一且不可预测以及如何做到高性能重定向。容量估算这部分很多人觉得难其实核心就是小学算术加上几个经验常数。一台普通的数据库服务器每秒大概能处理几千次简单查询一台应用服务器配合本地缓存单机扛住每秒一两千次请求是常有的事Redis 单实例读写吞吐可以到十万级 QPS。把这些基准值背熟任何题目都能快速算出需要多少台机器这个最关键的数字。笔记里我整理了常见组件的性能基准表实测下来这些数字虽然有波动但作为量级估算完全够用。需求澄清和容量估算做完之后整个设计就有了边界条件。接下来才是画架构图、选组件、定协议每一步都可以对应回前面算出的数字和业务约束。这样产出的方案才是有理有据的而不是网上都是这么设计的我也这么画。3.2 经典案例拆解设计一个短链接服务我用短链接这个案例来展示笔记里完整的推演过程因为它麻雀虽小五脏俱全涉及生成算法、存储设计、重定向性能、数据统计等多个系统设计核心知识点。第一步短链生成算法。这是全网讨论最多的点方案无非两类哈希取余和发号器。MD5 或 MurmurHash 把长 URL 哈希成固定长度再截取前 7 位作为短码。这种方案的问题是哈希碰撞碰撞后要么重新加盐再哈希要么落库时捕获唯一键冲突。发号器方案则用分布式唯一 ID 生成器比如雪花算法或数据库自增得到数字 ID 后再转换为 62 进制字符串7 位 62 进制能表示 62 的 7 次方约 3.5 万亿个组合远超实际需求。两相对比我笔记里的结论是中小规模系统用发号器更省心因为天然无碰撞。第二步存储设计。短码到长 URL 的映射关系用关系型数据库存最稳妥表结构就两列short_code做主键long_url做普通字段。查询场景极端简单——按主键查一行这种访问模式其实用 Redis 做缓存能把性能拉满。笔记里记录了当时的一个取舍因为读多写少读占比 95% 以上缓存命中率会很高于是采用 Cache-Aside 模式先查 Redis没命中再查数据库并回填缓存。这里有个小细节短码要设置合理的过期时间防止脏数据长期占用内存同时加布隆过滤器拦截不存在的短码请求防止恶意攻击穿透到数据库。第三步重定向性能优化。用户访问短链时为了 SEO 和历史兼容通常返回 301 永久重定向但这会让浏览器缓存导致统计数据失真返回 302 临时重定向则每次都会请求服务端便于记录点击数据。业务上更看重点击统计所以倾向 302。这个选择题我特意写进了笔记因为它是一个典型的技术选型依赖业务目标的例子——没有绝对正确只有适不适合。第四步容量规划。回到前面估算的数字几亿条记录、读 QPS 峰值可能上万。数据库主从加 Redis 缓存两台应用服务器加一组哨兵实例完全够用。整套方案下来不需要引入任何复杂的分布式组件却能把每个环节的性能做到极致。这一点我想特别提醒系统设计不是越复杂越好用最朴素的组件解决问题是更高级的能力。3.3 经典案例拆解设计一个消息推送系统第二个案例是消息推送系统因为它更好地展示写多读少场景下异步架构的核心思想。这个案例比短链接复杂一个量级涉及用户连接管理、消息分发策略、推送通道接入等多个模块。先考虑规模假设 1000 万注册用户日活 200 万每个用户平均一天收到 20 条站内推送通知那么每天的消息量是 4000 万条。如果这些消息全部要求实时推送到 APP而且每条消息要针对不同用户定制内容这就不是简单查表能解决的问题了。连接的接入层是关键。移动端和服务端保持长连接服务器需要维护海量连接的会话状态。这个会话层必须设计成无状态的这样任意一台服务器挂了用户重连到任意一台机器都能继续收到消息。实际做法是建立一个连接网关层所有的推送请求先进消息队列由连接网关的消费者把消息推送到对应设备的长连接上。网关层的消费者实例数量可以根据消息积压情况动态扩缩容这就是异步架构带来的弹性。消息内容的个性化处理也值得记一笔。如果 4000 万条消息每条都要实时拼接内容对上游服务的压力会非常大。常见的降级方案是预聚合模板消息提前生成好到了推送时间直接按用户 ID 分组批量发送只有真正需要实时计算的个性化推送才走完整链路。笔记里我画了一张分层架构图把消息生成和消息投递完全解耦成两个独立的服务中间的 MQ 既是缓冲又是天然的重试队列。这样做的好处很明显投递失败的消息可以自动重试完全不影响消息生成服务的稳定运行。这套设计里没有用到任何高深的理论全是基础组件的合理组合MQ 削峰和异步、Redis 存储在线状态、连接网关做水平扩展。但它完整呈现了一个合格系统设计的思考过程从需求澄清到容量估算从模块拆分到选型决策每一步都有依据每一步都能自圆其说。这正是我在笔记里反复强调的目标——不是背下来某个系统的架构图而是掌握拆解任意系统的方法。4. 常见问题与排查技巧实录4.1 学习系统设计时绕不开的四个误区整理笔记的这几年我接触过大量刚开始学系统设计的开发者大家踩的坑出奇地一致这里集中记录四个最典型的。误区一只记结论不记推导过程。看别人的方案觉得有道理但没想过换一个业务背景这个方案还可能成立吗比如看到缓存用 Redis就直接抄却没分析自己的数据量是不是真的需要 Redis 的丰富数据结构也许本地缓存加数据库就够了。破解方法是练习的时候强制自己写下为什么为什么这里要引入缓存引入后多出的缓存一致性成本能否接受写不出来的地方就是还没真正理解的地方。误区二一上来就追求高可用、分布式、微服务。我见过有人设计一个用户量几百人的内部系统硬上了微服务和 Kubernetes结果运维成本比开发成本还高。正确的姿势是从最简单的单体方案开始逐步识别瓶颈在瓶颈处做针对性扩展这和业务增长曲线是匹配的。笔记里我专门有一节叫演进式架构强调设计不是一步到位的而是随着业务发展不断演化的。误区三忽略非功能需求。很多同学设计的系统功能完备但一问监控告警怎么做、数据如何备份恢复、故障时怎么降级就答不上来了。实际上这些看不见的部分才是决定系统稳定性的关键。我参与过的线上事故复盘十次里有八次不是代码逻辑写错了而是没有限流导致连锁故障或者没有降级开关导致故障范围扩大。误区四背诵架构图。这是最隐蔽也最浪费时间的误区。背下来短链接系统架构图并不等于会设计短链接系统因为面试官稍微改一下约束条件比如要求短码不可预测、要求支持自定义alias背诵方案就崩盘了。系统设计能力的唯一来源是大量实战练习——把经典题目自己从头到尾推演一遍再和优秀方案对比找差距如此往复才能形成真正属于自己的设计直觉。4.2 面试与实战中的高频问题速查表这部分笔记我后来整理成了一张速查表方便临场快速回忆。以下是最常被问到也最容易被忽略的几个问题这个方案里数据库和缓存的一致性怎么保证我的标准回答框架先区分是强一致还是最终一致场景再给出缓存策略Cache-Aside、Write-Through 等最后说明极端情况下的补偿方案如延迟双删、MQ 重试。关键是要展现出我知道这里有不一致窗口并且我有应对预案。如果这个服务挂了整个系统会怎样这是考察容灾意识的经典问题。回答时沿着调用链逐层分析入口层挂了怎么办多活、DNS 切换、业务层挂了怎么办优雅降级、服务熔断、数据层挂了怎么办主从切换、数据备份恢复。核心是让面试官看到你的故障思维。用户量再涨十倍哪里会先扛不住这个问题的意图是考察对系统瓶颈的判断力。我通常按入口-应用-缓存-数据库的顺序排查并给出量化的估算当前 QPS 是多少、每层组件上限是多少、差了多远。能清晰说算出这些数字比背诵任何架构模式都加分。为什么不用 XXX这是最考验真实功底的提问。比如为什么用 RabbitMQ 而不是 Kafka我回答时会从多个维度对比消息可靠性要求、吞吐量需求、消息堆积能力、运维复杂度、团队熟悉度。关键是不能只说RabbitMQ 功能更丰富这种空话要结合前面的需求澄清给出适配的理由。我还在速查表里整理了一些答题套路的加分项讲方案时顺手提一句这里我加了一个布隆过滤器拦截非法请求——这种小细节最能体现实战经验主动指出方案的缺点和可能的改进方向——这比全程只说没问题显得成熟得多。4.3 维护这套笔记一年后的心得最后聊聊笔记本身的维护经验这部分可能比任何技术内容都更值得分享。第一架构图一定要保存源文件。我见过太多人笔记里贴了个截图后来想改却发现只能重画。从第一天起我就把所有的 Draw.io 源文件放在assets/目录正文里只引用导出的 PNG。这样系统需求一变改源文件重新导出就行几分钟的事。第二每篇笔记都要标注日期和适用场景。同样的技术2021 年的最佳实践和今天可能完全不同。比如一致性哈希的负载均衡方案在云原生时代已经被服务网格的 LDS/CDS 机制大量替代了。笔记里每个方案我都标注了记录时间和使用背景半年后回看时能清楚地看出技术认知的迭代痕迹。第三定期复盘很重要。我每做完一个真实项目和每完成一次模拟面试都会回到笔记里修订相关内容——补充新的 trade-off、修正过时的结论、删除不再适用的方案。这套笔记之所以能越用越顺手靠的就是这种持续迭代。现在它对我来说已经不只是一份复习资料更像是我个人架构决策经验的外部记忆库。如果你也想建一套自己的系统设计笔记我的建议是从你最近做过的一个系统开始把当时的设计决策完整记录下来再补上如果重做一次哪里会不一样。这比对着教程抄知识点有效得多。等技术积累到一定程度自然会形成一套只属于你自己的设计方法论那才是这个领域最值钱的东西。
返回列表