原文地址:Everything I know about good system design
基于DeepSeek总结修改
这篇文章的精髓在于它颠覆了很多人对“好系统”的认知:真正优秀的系统设计往往看起来平淡无奇,甚至让人觉得“比我想的要简单”;而那些看起来很厉害的复杂系统,可能反而掩盖了根本性的设计问题。所有能正常运行的复杂系统,无一不是从能正常运行的简单系统演化而来,从零开始直接构建复杂系统绝对是糟糕的想法。
下面这个表格概括了优秀系统设计与不良系统设计的关键特征对比,能帮助你更直观地理解肖恩·戈德克的核心观点。
特征维度 | 优秀的系统设计 | 不良的系统设计 |
---|---|---|
核心表现 | 长期稳定,很少出错 | 频繁出现问题,维护成本高 |
复杂性处理 | 简单直观,从有效的简单系统演化而来 | 过度复杂,可能从零开始就设计成复杂系统,或滥用“聪明技巧” |
状态管理 | 严格控制状态,尽可能无状态,状态集中化 | 状态分散,多个服务直接操作数据库,难以维护 |
数据库设计 | 表结构清晰易懂,索引策略合理 | 结构复杂(如过度使用JSON或键值表),或缺乏必要索引 |
缓存使用 | 谨慎使用缓存,优先优化底层(如数据库索引) | 过度依赖缓存,试图用缓存掩盖底层性能问题 |
架构选择 | 实用主义,简单API调用往往优于事件驱动 | 炫技倾向,盲目采用事件驱动、CQRS等复杂模式 |
给人的感觉 | “平淡无奇”,甚至让人觉得“就这么简单?” | “令人印象深刻”,看起来“很厉害” |
🧩 系统设计的本质
肖恩开篇明义:“程序设计是组装代码,系统设计是组装服务。” 这意味着系统设计的基本单元不再是变量、函数和类,而是应用服务器、数据库、缓存、队列、事件总线、代理等基础设施组件。
💡 核心原则与实战策略
🤖 状态管理的艺术:肖恩认为状态是系统设计中最困难的部分。他的核心建议是:
- 无状态优先:尽量采用无状态组件(如一个纯粹的PDF转HTML服务)。
- 状态最小化:极力减少系统中有状态组件的数量。
- 状态集中化:让一个服务“拥有”并管理某种状态,其他服务通过API或事件与之交互,而不是多个服务直接读写同一个数据库表。因为有状态服务可能陷入异常状态(如错误数据导致崩溃、存储空间耗尽),往往需要手动干预修复,而无状态服务则可以通过简单重启自动恢复。
💾 数据库设计哲学:数据库是存储状态的核心。
- 表结构清晰:目标是打开表结构就能大致理解存储的内容和原因。应避免过度“灵活”的复杂结构(如滥用JSON列或键值表),这会把复杂性转移给应用代码并引发性能问题。
- 索引策略:为最常用的查询模式创建复合索引,并将高基数字段放在前面。避免盲目添加所有可能字段的索引,因为每个索引都会增加写入开销。
- 读写分离:采用一个写入节点(主库)和多个只读副本(从库) 是常见且有效的策略。尽量将查询导向只读副本,以减轻主库压力。对于“写入后立即读取”的场景,若无法容忍复制延迟,可考虑将更新后的数据暂存于内存,而非立即重新读取数据库。
⚡ 性能优化策略:
- 让数据库做繁重的工作:在绝大多数情况下,使用JOIN等数据库操作比在应用内存中手动拼接数据更高效。尤其要警惕ORM在循环中意外触发N+1查询问题。
- 缓存是双刃剑:一个有趣的观点是,初级工程师总想缓存一切,而高级工程师则希望尽量少用缓存。因为缓存本身就是一种状态源,会带来数据一致性、过期和异常等问题。优先考虑优化底层(如数据库索引),而非首选缓存。对于大规模或昂贵操作,可考虑使用计划任务将结果(如报告)持久化到S3等对象存储中,而非依赖Redis/Memcached。
⏳ 快慢操作分离与后台作业:对于耗时操作(如处理大文件),通用解决方案是拆解出核心部分快速响应,其余部分放入后台作业队列异步处理。一个后台作业系统通常包含:
- 队列服务:如Redis(紧急任务)或数据库(非紧急任务)。
- 作业运行器:从队列中获取并执行任务。
- 对于需要长期调度(如一个月后执行)的任务,肖恩建议使用数据库表而非Redis队列来管理,以保证持久化和便于查询。
🚀 事件驱动架构的陷阱:虽然事件驱动架构(常用Kafka)适用于“发送方不关心消费方如何处理”或事件量很大的场景,但肖恩警告不要过度使用。他指出,很多时候,更简单的做法是让一个服务直接调用另一个服务的API,这样更直接,也更易于调试和追踪。
🛠️ 实战经验与建议
- 热路径优先:识别系统中最关键、处理数据最多的部分(热路径),并给予其最高关注,因为这里的选择更少,出错影响也更严重。
- 日志和监控:积极记录异常路径上的详细信息。监控关键指标(如CPU/内存、队列大小、请求时间),并重点关注P95和P99分位数,而非平均值。
- 容错和降级:采用断路器模式防止级联失败。设计幂等性操作(如使用UUID)以避免重复处理。为功能设计优雅降级策略,决定失败时是开放还是关闭。
💎 总结与启示
肖恩·戈德克的这些观点,对于当前热衷追求“微服务”、“云原生”、“事件驱动”等技术社区来说,是一剂宝贵的清醒剂。他提醒我们回归系统设计的本质:用最简单、最经考验的方式解决实际的问题。
“好的系统设计不是关于聪明的技巧,而是知道如何在正确的地方使用无聊的、经过测试的组件。如果你在做一些太令人兴奋的事情,你可能会弄得一团糟。”