1. 这不是笔记是系统设计能力的“肌肉记忆”训练手册“system-design-notes”这个标题乍看平平无奇像极了某次面试前随手建的GitHub仓库名或是技术群里一句“求份系统设计笔记”的潦草求助。但如果你真把它当成一份可复制粘贴的速记清单那大概率会在下一轮面试中卡在“如何设计一个短链接服务”这道题上——不是答不出而是答得散、答得浅、答得没重点。我带过三十多个准备系统设计面试的工程师其中八成人在第一轮模拟面试里栽在同一类问题上他们能背出CAP定理的定义却说不清为什么Twitter早期用MySQL分库分表而不是直接上MongoDB他们记得“缓存穿透”四个字但面对“如何防止恶意ID刷请求击穿缓存”连布隆过滤器该放在哪一层都犹豫三秒。这背后暴露的根本不是知识盲区而是系统设计思维的结构性缺失——缺少对规模、一致性、可用性、成本之间真实张力的体感缺少把抽象原则落地为具体取舍的决策路径。所以“system-design-notes”绝不是知识点罗列它是一套可执行、可验证、可迭代的系统设计能力训练框架。它解决的核心问题是把“知道”变成“会用”当你面对一个从零开始的业务需求比如“支持千万级用户实时点赞的微博Feed流”你能快速拆解出关键约束QPS峰值延迟容忍数据一致性要求能基于这些约束主动排除不合理的方案比如用单机Redis扛写请求能清晰说出每个组件选型背后的代价权衡为什么用Kafka不用RabbitMQ为什么用Cassandra不用Spanner最后还能画出一张让面试官点头的架构图——不是靠死记硬背而是靠一套内化的思考节奏。它适合三类人正在冲刺大厂后端/基础架构岗的应届生或初级工程师想突破技术瓶颈、从功能开发转向架构设计的中级开发者以及需要快速评估第三方系统技术合理性的技术负责人。它不承诺“三天速成”但能确保你每读一页、每练一题都在加固自己脑中的系统设计“神经回路”。2. 为什么这套笔记结构比内容更重要从“抄答案”到“建模型”的底层跃迁市面上不缺系统设计资料有按场景分类的“高频题库”有按组件讲解的“中间件百科”还有按公司命名的“FAANG真题解析”。但它们共同的短板是把系统设计当成了“拼图游戏”——告诉你“短链接服务哈希缓存数据库”却不说清楚为什么必须是这个顺序为什么不能颠倒为什么这个组合在10万QPS和1000万QPS下要彻底重构。这种教法本质上是在训练“条件反射”而非“设计能力”。而“system-design-notes”的核心价值恰恰在于它用结构化框架强行打断你的惯性思维逼你回到设计原点重新推演。这不是玄学而是基于十年一线架构实践沉淀下来的四层推演逻辑2.1 第一层需求解构——先砍掉80%的“伪需求”几乎所有失败的设计都始于对需求的误读。比如题目说“设计一个支持1亿用户的社交App”新手第一反应是“得用分布式数据库得上K8s集群”。但资深架构师会先问三个问题峰值QPS是多少是日均10万还是秒级5万前者可能单机MySQL读写分离就够后者必须考虑分片路由。数据一致性要求是什么点赞数允许1秒延迟更新最终一致性还是转账必须强一致线性一致性前者可用异步消息后者必须引入分布式事务。成本约束在哪里是创业公司烧钱换速度还是成熟产品抠每一分钱前者敢用AWS托管服务后者必须自研压缩算法省带宽。我在某电商做秒杀系统时就吃过亏。最初团队按“支撑百万并发”设计堆了10台Redis集群结果上线后发现95%流量集中在前3秒其余时间闲置。后来重做需求分析把“峰值QPS”细化为“3秒内50万请求”立刻意识到核心瓶颈是瞬时连接数和网络IO而非内存或CPU。最终方案是前端加CDN静态资源缓存JS限流网关层用Nginx做连接池控制Lua脚本预校验Redis只存最简库存Key非JSON对象反而用6台机器扛住了双11。这说明需求解构不是填空而是用工程语言翻译业务语言的过程。笔记里每个案例开头都强制要求填写这三项参数就是逼你养成习惯。2.2 第二层约束映射——把抽象原则变成具体取舍系统设计没有银弹只有取舍。CAP定理不是让你背诵“C、A、P三选二”而是教你在特定场景下哪个字母的牺牲代价最小。比如设计一个物流轨迹查询系统如果用户容忍轨迹延迟5分钟更新如冷链运输那“一致性C”可以妥协用ES做异步索引吞吐量翻倍如果是快递员实时上报位置需秒级可见那“分区容忍性P”就不能牺牲必须放弃强一致用Gossip协议同步节点状态如果是金融级物流如药品溯源那“可用性A”必须让步宁可服务短暂不可用也要保证每次查询结果绝对准确。笔记里所有组件选型表格都标注了“适用约束”栏。比如对比Redis和Memcached特性RedisMemcached适用场景需要复杂数据结构ZSet做排行榜、原子操作INCR、持久化保障纯KV缓存、极致吞吐、内存敏感型应用一致性代价主从复制有毫秒级延迟哨兵模式切换时可能丢数据无持久化故障即丢失但无复制延迟运维成本需监控AOF/RDB、主从同步延迟、内存碎片配置简单重启即清空无状态管理你看选Redis不是因为它“高级”而是因为你需要ZSet且能接受主从延迟选Memcached不是因为它“落后”而是因为你追求吞吐且能容忍缓存击穿后的数据库压力。这种映射关系才是设计决策的真正依据。2.3 第三层渐进演化——拒绝一步到位的“完美架构”很多新人设计稿里动辄出现“全链路灰度发布”、“多活容灾”、“Service Mesh治理”。这很酷但现实是90%的初创系统连数据库读写分离都没做好就去搞微服务拆分纯属给自己挖坑。真正的系统设计是一条从“能跑”到“能扛”再到“能省”的演化路径。以电商订单系统为例V1.0单体阶段所有逻辑在Spring Boot里MySQL单库用Transactional保证本地事务。此时最大瓶颈是数据库连接数解决方案是连接池调优慢SQL治理。V2.0垂直拆分订单、支付、库存拆成独立服务用RocketMQ解耦。此时新瓶颈是跨服务事务解决方案是Saga模式补偿任务而非硬上Seata。V3.0水平扩展订单库按用户ID哈希分片支付服务引入TCC模式。此时新瓶颈是分片键选择解决方案是用“用户ID时间戳”复合键避免热点而非盲目增加分片数。笔记里每个案例都标注了“当前版本瓶颈”和“下一阶段演进触发点”。比如短链接服务当Redis缓存命中率跌破85%就是引入布隆过滤器的信号当MySQL写入延迟超过200ms就是切分link_id生成逻辑的时机。这种设计让你明白架构不是静态图纸而是动态生长的生命体。2.4 第四层成本量化——用数字说话终结“我觉得”工程师最容易陷入的陷阱是用“我觉得”代替数据。比如“加一台Redis服务器就能解决问题”但没人算过这台机器的月成本是多少带来的QPS提升是否覆盖成本如果用本地缓存Caffeine替代节省的带宽费用能否买下整台机器我在做视频平台推荐系统时曾为“是否引入Flink实时计算”争论两周。最后我们拉出三组数据当前离线计算Spark每天处理10TB日志延迟6小时服务器成本$1200/月Flink实时计算延迟1分钟但需常驻8台高配机器成本$4500/月折中方案KafkaStorm延迟3分钟成本$2800/月且能复用现有Kafka集群。最终选了折中方案因为业务方确认“3分钟延迟可接受”而$1700/月的成本差够养一个专职运维。笔记里所有方案对比都强制要求填写“成本影响”列。不是让你成为财务专家而是培养一种本能任何技术选型都要先问“它值多少钱”。3. 核心笔记模块详解从“看到题”到“画出图”的实操闭环“system-design-notes”的骨架由五大模块构成每个模块都对应系统设计面试中一个不可跳过的实战环节。它不追求面面俱到而是聚焦于高频、高区分度、易踩坑的关键动作。下面以“设计一个支持千万级用户的实时聊天系统”为例逐层拆解笔记如何驱动你完成一次完整设计。3.1 模块一需求澄清与指标量化15分钟必做这是决定成败的起点。笔记模板强制要求填写以下字段且每个字段都附带反例警示核心功能支持单聊、群聊、消息已读回执、离线消息推送。提示不要写“支持聊天”必须明确交互细节。“已读回执”意味着需要存储用户阅读状态这直接影响数据库设计。非功能需求QPS峰值5万按1000万用户5%同时在线人均2条/秒估算延迟95%消息端到端延迟500ms可用性99.99%全年宕机52分钟数据持久化消息至少保存7天重要群聊永久保存。注意QPS不能凭空猜测。笔记里提供速算公式峰值QPS (日活用户 × 同时在线率 × 人均操作频次) ÷ (86400秒 × 0.3)0.3代表高峰时段占全天30%。用这个公式算出的数字比拍脑袋靠谱十倍。约束条件现有技术栈Java/Spring Cloud MySQL Kafka团队规模5人后端无专职运维预算限制基础设施月成本$8000。警示忽略“团队规模”是致命错误。一个5人团队硬上Service Mesh等于给自己装枷锁。笔记强调架构复杂度必须匹配团队能力水位。3.2 模块二核心链路与数据流图手绘优先很多候选人一上来就画“用户→API网关→微服务→DB”这太粗了。笔记要求你先画出最简可行链路MVP Flow再逐步叠加。以消息发送为例MVP链路单机版客户端 → Spring Boot Controller → Service层 → MySQL写入 → WebSocket广播此时瓶颈明显MySQL写入和WebSocket广播都是单点无法水平扩展。解耦第一步引入消息队列客户端 → Controller → Service → Kafka写入 → 消费者1MySQL持久化 → 消费者2WebSocket推送此时解决了写入瓶颈但WebSocket推送仍受限于单机连接数。解耦第二步引入长连接网关客户端 → 自研长连接网关Netty → Kafka → 消费者MySQL/ES/推送网关负责维持TCP连接、心跳、消息路由彻底释放业务服务压力。笔记里所有数据流图都用不同颜色标注红色核心瓶颈点如单点MySQL蓝色可水平扩展组件如Kafka消费者组绿色状态存储如Redis存在线用户列表灰色可裁剪组件如ES用于搜索非核心链路。这种视觉编码让你一眼看清系统脆弱点和扩展点。3.3 模块三关键组件选型与参数计算拒绝模糊选型不是查文档而是做实验。笔记提供每个组件的“压测速查表”基于真实生产数据长连接网关选型方案单机连接数内存占用开发成本适用场景Netty自研10万2GB高需处理心跳、断线重连大厂定制化需求EnvoyWebSocket5万1.5GB中需配置xDS快速验证MVP商业网关如腾讯云CLB100万0低托管初创公司保命实操心得我试过用Envoy做网关看似省事但遇到“连接突然中断”问题时排查链路长达4小时Envoy→gRPC→后端服务。自研Netty虽然前期投入大但每个字节都可控。笔记结论如果团队有1个资深网络工程师优先自研否则用商业网关把精力留给业务。消息存储选型MySQL vs Cassandra计算公式单日消息量 日活 × 人均消息数 × 2收发假设1000万用户人均20条则单日4亿条。MySQL单表超5000万行性能骤降必须分库分表Cassandra天然支持海量写入但查询灵活性差不支持JOIN。笔记建议用MySQL存元数据sender_id, receiver_id, msg_id用Cassandra存消息体msg_content, timestamp通过msg_id关联。这样既保证查询效率又扛住写压。3.4 模块四容错与扩展设计面试官最爱深挖点这里暴露的是你对系统“死亡”的理解深度。笔记不讲理论只列真实故障场景和应对场景1Kafka集群脑裂现象Producer持续超时Consumer重复消费。根因ZooKeeper网络分区导致Controller选举失败。笔记方案Producer端启用retriesMAXidempotencetrue幂等性Consumer端用enable.auto.commitfalse手动提交offset监控UnderReplicatedPartitions指标0立即告警。踩坑记录某次线上事故因未开启幂等性导致用户收到100条重复红包消息。从此笔记里所有Kafka配置都强制标红“幂等性开关”。场景2长连接网关OOM现象网关频繁Full GC连接数缓慢下降。根因未及时清理断开连接的Channel内存泄漏。笔记方案Netty ChannelPipeline中加入IdleStateHandler(60, 0, 0)自动关闭空闲连接使用PooledByteBufAllocator替代UnpooledByteBufAllocator减少GC压力JVM参数-XX:UseG1GC -XX:MaxGCPauseMillis200。实测数据开启IdleStateHandler后单机内存占用从4GB降至1.2GB连接数稳定性提升300%。3.5 模块五演进路线图展示长期主义思维面试官想看的不仅是“现在怎么搭”更是“未来怎么长”。笔记用甘特图形式规划三年演进阶段时间目标关键动作成功标志V1.00-3月MVP上线单体MySQLKafkaNetty网关支撑5万QPS99.9%可用V2.04-12月稳定性攻坚引入Sentinel限流、Arthas诊断、ELK日志故障平均恢复时间5分钟V3.013-24月智能化升级接入PrometheusGrafana预测扩容、AI异常检测自动扩容响应时间30秒V4.025-36月生态整合对接公司统一身份认证、计费系统、BI平台跨系统数据打通率100%关键洞察V2.0不提“微服务拆分”因为团队还没掌握分布式链路追踪。笔记强调演进不是按时间表走而是按能力里程碑走。每个阶段的“成功标志”都是可测量的技术指标不是模糊的“用户体验提升”。4. 实操过程手把手带你完成一个完整设计以“短链接服务”为例现在我们用笔记框架完整走一遍“设计一个高可用短链接服务”的实操。这不是演示而是你接下来要做的练习。4.1 第一步需求澄清严格按模板填打开笔记的“需求澄清页”填入核心功能输入长URL返回唯一短码如https://t.co/abc123访问短码302重定向到原始URL支持自定义短码如https://t.co/mylink统计每日点击量UV/PV。非功能需求QPS峰值2万参考Bitly公开数据其峰值QPS约1.5万延迟99%请求100ms重定向是HTTP层必须快可用性99.99%短链接是营销生命线宕机损失客户数据量预计每日生成500万短链接保存1年。约束条件技术栈Go Redis MySQL团队3人1人熟悉Go2人熟悉MySQL预算月成本$3000。实操提示QPS估算很重要。如果写“10万QPS”后续所有设计都会过度复杂。用Bitly数据锚定是专业做法。4.2 第二步MVP链路绘制纸笔优先拿出一张白纸画出最简流程[用户] → [Go API Server] → [MySQL插入长URL] → [生成短码] → [返回短码] [短码访问] → [Go API Server] → [MySQL查短码] → [302重定向]立刻发现问题MySQL是单点且读写都压它肯定扛不住2万QPS。于是升级[用户] → [Go API Server] → [Redis缓存短码] → [MySQL异步写入] [短码访问] → [Go API Server] → [Redis查短码] → [302重定向] → [MySQL异步更新PV]此时Redis是新瓶颈单机Redis QPS上限约10万但内存和网络IO可能先扛不住。笔记提醒缓存不是万能药它只是把瓶颈从DB转移到Cache。4.3 第三步核心组件选型与参数计算短码生成算法Base62a-z,A-Z,0-96位可生成568亿组合足够用雪花算法不行ID太长19位短码要短Hash算法MD5碰撞风险不安全最终方案MySQL自增ID Base62编码。计算SELECT LAST_INSERT_ID()获取ID转Base62。ID从1开始6位Base62最大值为62^6≈568亿按每日500万生成可用30年。Redis集群规划单日500万短链接每条Key约100字节短码长URL元数据日增内存≈500MB一年数据≈180GB按3副本20%预留需总内存≈650GB采用Redis Cluster12个分片shard每分片2主2从共48个实例单实例内存64GBQPS 2万/分片完全够用。注意不要用Redis Sentinel它无法水平扩展。Cluster是唯一选择。MySQL分库分表表结构id(BIGINT), short_code(VARCHAR), long_url(TEXT), created_at(TIMESTAMP)分片键short_code查询都带它分库4库每库128表共512表用crc32(short_code) % 512路由为什么不分id因为查询入口是短码不是ID。4.4 第四步容错设计直击面试官灵魂提问缓存穿透恶意请求不存在的短码如t.co/aaaaaa打穿Redis直击MySQL。笔记方案对空结果也缓存SET t.co/aaaaaa NULL EX 60但需防缓存雪崩更优解布隆过滤器Bloom Filter前置。用Redis Bitmap实现空间效率极高。实测1亿URL的Bloom Filter仅占12MB内存误判率0.1%。缓存雪崩大量短码缓存同时过期请求涌向MySQL。笔记方案缓存过期时间加随机因子EX 3600 rand(0,300)二级缓存本地Caffeine缓存热点短码如TOP 1000过期时间更短。重定向性能302跳转本身有开销如何压到100ms内笔记方案Go HTTP Server启用Keep-Alive复用TCP连接Nginx前置配置proxy_cache_valid 302 1h缓存重定向响应最狠一招DNS CNAME劫持。将t.co域名CNAME到CDNCDN直接返回302绕过所有后端。4.5 第五步演进路线体现架构前瞻性V1.01个月单Go服务Redis ClusterMySQL分库支持自定义短码。V2.03个月接入ClickHouse做实时点击统计替代MySQL聚合查询。V3.06个月引入Link Tracking SDK支持UTM参数自动附加对接GA。V4.012个月开放API支持企业客户批量生成短链接按调用量计费。关键检查点每个阶段是否都解决了上一阶段暴露的瓶颈V1.0解决高并发V2.0解决分析性能V3.0解决营销闭环V4.0解决商业化。逻辑闭环无可挑剔。5. 常见问题与避坑指南那些没人告诉你的“血泪经验”即使你把笔记背得滚瓜烂熟实操中依然会踩坑。这些坑往往不在教科书里而在凌晨三点的线上报警里。以下是我在带教过程中被问得最多、也最痛的五个问题附真实解决方案。5.1 问题1“为什么我的Redis缓存命中率只有60%”表面看是缓存策略问题根因往往是数据访问模式与缓存设计错配。典型场景用Redis缓存用户个人信息user:123但业务代码里频繁用HGETALL user:123而实际每次只读name和avatar两个字段。错误解法加大Redis内存以为“多买点机器就行”。笔记正解先用redis-cli --bigkeys扫描大Key发现user:123平均1MB拆分为细粒度Keyuser:123:name,user:123:avatar,user:123:profile业务代码改用MGET user:123:name user:123:avatar网络IO减少80%加入本地缓存Caffeine设置expireAfterWrite(10m)拦截80%重复请求。实测效果命中率从60%升至92%Redis集群CPU从95%降至35%。记住缓存不是越大越好而是越精准越好。5.2 问题2“Kafka消费者为什么总是重复消费”这是分布式系统的经典难题根源在offset提交时机与业务处理原子性脱钩。错误模式for msg : range consumer.Messages() { process(msg) // 可能失败 consumer.CommitMessages(msg) // 失败后仍提交 }一旦process()失败消息已提交永远丢失。笔记正解三种方案对比方案原理优点缺点适用场景手动同步提交consumer.CommitMessages(msg)在process()成功后调用精确控制性能差吞吐低金融级强一致异步提交重试consumer.CommitAsync() 失败后重试机制吞吐高可能重复日志、监控等容忍重复场景幂等ProducerKafka 0.11支持enable.idempotencetrueBroker端去重无需业务改造仅限Producer端新项目首选我的建议新项目一律用幂等Producer老系统改造优先异步提交业务层去重如用Redis Set记录msg_id。5.3 问题3“MySQL分库分表后跨库JOIN怎么写”这是分库分表的“阿喀琉斯之踵”。笔记给出不依赖中间件的务实解法方案1冗余字段法最常用订单表分库用户表分库但订单表里冗余user_name、user_phone。更新用户信息时用MQ异步同步到订单库。优势查询零成本劣势数据最终一致有延迟。方案2二次查询法先查订单拿到user_id再并行查用户库。Go里用sync.WaitGroup或errgroup并发查询。优势强一致劣势网络IO翻倍延迟增加。方案3ES聚合将订单和用户数据双写到Elasticsearch用nested类型做关联查询。优势灵活支持复杂查询劣势ES不是强一致存储不适合交易场景。经验之谈90%的跨库JOIN需求其实可以用“冗余字段异步同步”完美解决。追求100%技术纯洁性不如先让业务跑起来。5.4 问题4“为什么压测时QPS上不去CPU却只有40%”这暴露了系统瓶颈不在CPU而在I/O或锁竞争。常见原因网络瓶颈单机TCP连接数超65535或网卡带宽打满1G网卡理论极限125MB/s磁盘IO瓶颈MySQL写入时iowait高达80%说明磁盘跟不上锁竞争RedisINCR操作在高并发下排队或MySQL行锁等待。笔记排查三步法top看CPUvmstat 1看si/soswap、waIO等待iftop看网卡流量iostat -x 1看磁盘%util和awaitstrace -p pid抓进程系统调用看卡在哪如epoll_wait说明网络阻塞。真实案例某次压测QPS卡在8000top显示CPU仅30%。iostat发现await达200msiotop定位到MySQL日志刷盘慢。解决方案将innodb_flush_log_at_trx_commit2牺牲一点安全性换性能QPS立刻升至1.5万。5.5 问题5“面试官问我‘如果让你重做一次会改进什么’该怎么答”这是考察你反思能力和成长性的终极问题。千万别答“没缺点”或“都很好”。笔记提供高分话术模板错误回答“我觉得设计很完美没有需要改进的。”显得傲慢且无反思正确回答“如果重做我会在V1.0就引入链路追踪如Jaeger。当时为了赶进度所有日志都是文本格式线上排查一次慢查询要翻3个服务的日志。后来我们花了2周补上现在平均故障定位时间从45分钟降到8分钟。这让我明白可观测性不是锦上添花而是系统设计的第一性原理。”关键点说具体改进点链路追踪不说虚的“加强监控”用数据对比45min→8min证明价值上升到方法论可观测性是第一性原理展现思考深度。6. 最后一点个人体会系统设计是手艺不是魔法写完这篇笔记我翻出自己五年前的第一份系统设计文档上面密密麻麻全是箭头和框图却找不到一行关于“成本”、“团队”、“演进”的文字。那时我以为画出漂亮的架构图就等于掌握了系统设计。后来在无数次线上事故、无数个通宵调优、无数次方案推倒重来之后才懂系统设计最硬核的部分从来不是技术本身而是你如何在一个充满约束的真实世界里做出不完美的、但恰到好处的选择。它更像一门手艺——木匠不会炫耀自己有多懂木材纤维他只会默默选对那块木头用对那把凿子在该省力的地方省力在该较真的地方较真。这份“system-design-notes”就是我这些年打磨出来的那把凿子。它不保证你成为架构大师但它能确保你每一次设计都比上一次更扎实、更清醒、更接近那个“恰到好处”的答案。