实习生视角的系统设计:画框图谁都会,难的是理解数据流向

📅 2026/7/19 16:09:52
实习生视角的系统设计:画框图谁都会,难的是理解数据流向
实习生视角的系统设计画框图谁都会难的是理解数据流向一、第一次参加系统设计评审画的图被 mentor 圈了五个问题实习生被安排参与一个新功能的系统设计。花了一个下午画了服务架构图——用户请求 → API 网关 → 业务服务 → 数据库每个节点标了技术选型画布上整整齐齐。审图时mentor 提了三个问题从用户请求进来到结果返回中间经过了哪些缓存层每一步失败时的降级路径是什么数据在同步链路和异步链路中的一致性怎么保证这三个问题让我意识到画几个框连几条线不是系统设计。系统设计的核心不是有哪些组件而是数据在这些组件之间怎样流动。用户发请求过来我们查数据库返回——这句话的每一个字都包含着大量的隐含假设。请求经过负载均衡了吗数据库查询走了缓存吗如果查不到数据怎么办如果数据库连接池满了怎么办把这些隐含的假设变成显式的设计才是系统设计的真正工作量。具体而言我们需要将系统拆解为三个具体的维度进行描述。在数据流层面明确请求从网关鉴权进入业务服务后如何判断缓存命中未命中时如何查询数据库并回写缓存在控制流层面定义配置中心如何下发参数供业务服务读取以及监控告警如何驱动运维决策并反馈至配置中心在异常流层面预设缓存故障时直接查库、数据库主库故障时切换从库以及全链路故障时降级返回兜底数据的具体路径。二、系统设计的三层视角系统设计文档中至少应该包含三层视角每层回答不同的问题。数据流视角数据在系统内部是怎样流转的一个用户请求从进来到响应返回经过了哪些节点在每个节点上数据被做了什么处理经过了哪些存储层Redis、MySQL、ES、Kafka缓存穿透、击穿、雪崩在每个数据流节点上是否有防护控制流视角系统的行为是由什么来决定的哪些参数是可配置的降级开关、限流阈值、黑白名单配置的变更通过什么机制生效定时轮询、配置中心推送配置变更的生效时间是多久异常流视角系统的每一层如果出问题会怎样下游服务超时时上游怎么处理数据库连接池耗尽时请求是排队还是快速失败异步消息队列消费者积压时会不会触发报警好的系统设计往往能把异常路径想得比正常路径还清楚。三、小规模系统的设计实践不要一上来就想我们要拆多少微服务。刚起步的系统一个单体应用 合理的模块划分比一套复杂的微服务体系更实际。拆微服务的理由应该是业务耦合度降低或团队规模扩大导致的协作冲突不是为了拆而拆。/** * 一个判题服务的渐进式架构演进 * * 阶段 1单体低于 1000 QPS * - Spring Boot 单体应用 * - MySQL 单库 * - 同步调用模型服务 * * 阶段 2读写分离 缓存1000~5000 QPS * - 查询走 Redis 缓存 * - 数据库读写分离 * - 热门题目数据预热 * * 阶段 3异步化 服务拆分5000 QPS * - 判题请求异步化提交后返回任务 ID轮询获取结果 * - 模型服务独立部署 * - 消息队列削峰 * * 设计原则架构复杂度应该和业务规模同步演进 * 不要为 100 QPS 的系统设计 10000 QPS 的架构 */ public class ArchitectureEvolution { // 阶段 1最简单的单体 public JudgeResult judgeV1(SubmitRequest request) { // 直接同步调用模型等 5~10 秒 return modelService.judge(request.getCode()); } // 阶段 2加缓存仅缓存热点题目的判题结果 public JudgeResult judgeV2(SubmitRequest request) { String cacheKey judge: request.getProblemId() : hash(request.getCode()); JudgeResult cached redis.get(cacheKey); if (cached ! null) { return cached; } JudgeResult result modelService.judge(request.getCode()); // 热点题目缓存 1 小时 if (isHotProblem(request.getProblemId())) { redis.setex(cacheKey, 3600, result); } return result; } // 阶段 3异步化用户提交后拿到 taskId前端轮询获取结果 public String judgeV3(SubmitRequest request) { String taskId generateTaskId(); // 将判题任务放入消息队列 mqProducer.send(JUDGE_TASK, new JudgeTask(taskId, request)); // 立即返回 taskId不阻塞用户 return taskId; } }渐进式架构的核心思想是架构随业务复杂度同步成长。一个日活 100 人的内部工具不需要 Kubernetes 集群、微服务体系、全链路追踪。不要被大厂架构的标准框住了——大厂架构之所以那样设计是因为他们的业务体量和团队规模决定了那些复杂度是必要的。小规模的系统追求简单和可维护性把复杂度留给未来的增长。四、写系统设计文档的实用建议先画数据流再画架构图。数据流是需求架构图是方案。如果连数据从哪里来、到哪里去、中间需要被怎样处理都没想清楚画出来的架构图一定是脱离实际的。每一段数据流转都应该能回答为什么数据要走这条路有没有更短的路这条路断了怎么办给每一个异步附上理由。异步不是银弹。把一个同步操作变成异步表面上降低了接口延迟但这并不意味着工作量消失了——它只是把工作量转移到了队列消费者那里还增加了状态管理、重试、超时处理的复杂性。如果不是响应延迟不可接受不要轻易引入异步。设计文档中必须包含如果这部分出问题会怎样。每一个组件、每一条链路都附上一句当这个组件不可用时的思考。这不是过度焦虑而是设计的基本素养。五、总结用实习生的视角去看系统设计最大的收获不是学会了某个框架或中间件的用法而是学会了追问。画几个框追问数据怎么流定一个方案追问出了问题怎么办选一个中间件追问没有它行不行。系统设计不是考画图而是在有限的资源和技术约束下做出最优的权衡决策。这种权衡决策的能力不是看书能学会的而是在一次次被 mentor 追问然后呢、如果这里挂了怎么办中逼出来的。