云计算百科
云计算领域专业知识百科平台

system-design-notes 第一章解析:从单台服务器到百万用户——系统架构的逐级扩展(Scaling)全景指南

  • 文档
  • 教程
  • 后端

【免费下载链接】system-design-notes

Notes of the book System Desgin Interview – An Insider's Guide

项目地址:
https://gitcode.com/GitHub_Trending/sy/system-design-notes

点击查看 免费下载

在 system-design-notes(《System Design Interview – An Insider's Guide》读书笔记仓库)中,第一章 01. Scaling/Readme.md 回答了一个架构设计的起点问题:如何从一个只有单台服务器的初始系统,一步步演进到能够支撑百万级用户的分布式架构。本文完整继承该章节的 12 个演进阶段——单服务器、数据库分离、垂直/水平扩展、负载均衡、数据库主从复制、缓存、CDN、无状态 Web 层、多数据中心、消息队列、日志监控自动化、数据库分片——并结合仓库中后续章节(一致性哈希、分布式消息队列、指标监控)的对应笔记做纵深解读,帮助读者掌握一套可复用的“从零到百万用户”的架构升级方法论。

![单服务器部署架构:Web 应用、数据库与缓存运行在同一台服务器上](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/01. Scaling/images/single-server.png?utm_source=gitcode_repo_files)

一、为什么架构扩展是一个渐进过程

原文的开篇定位非常明确:将系统扩展到支撑百万用户,是一段复杂且反复迭代的旅程(a complex, iterative journey requiring refinement and optimization)。它不是一次性设计出来的,而是随着用户增长逐步演进的。本章的价值在于给出了这条演进路径的完整骨架:每一级扩展都对应一个具体的瓶颈,以及一个针对性的架构手段。

后续各节将按照原文 Section 1 到 Section 12 的顺序展开,每个小节都聚焦一个具体问题和对应的解法。

二、初始形态:单服务器部署(Single Server Setup)

请求流转过程

系统初期,Web 应用、数据库、缓存全部跑在同一台服务器上。用户访问的完整链路如下:

  • 用户通过域名(例如 api.mysite.com)访问应用,域名经 DNS 解析为 IP 地址;
  • Web 服务器的 IP 地址被返回给浏览器或移动端 App;
  • 浏览器向 Web 服务器发起 HTTP 请求,服务器返回 HTML 或 JSON 响应。
  • 两类流量来源

    原文区分了两类典型的流量入口,这对后续 Web 层的设计有直接影响:

  • Web 应用:业务逻辑用服务端语言(如 Python、Java)实现,展示层用客户端语言(如 JavaScript、HTML)实现;
  • 移动应用:通过 HTTP + JSON 与 Web 服务器通信,以轻量化的方式交换数据。
  • 这一阶段的局限显而易见:Web 层、缓存、数据库共享同一台机器的 CPU、内存与磁盘 I/O,任何一层成为瓶颈都会拖垮整体,且没有任何冗余。这是后两级扩展(数据库分离、Web 层水平扩展)要解决的问题。

    三、第一步演进:把数据库拆到独立服务器(Database Separation)

    随着用户增长,第一个要做的分离是把数据库迁到专用服务器上。这样 Web 层与数据库层可以独立扩容——数据库慢了不会直接耗尽 Web 服务器的资源,反之亦然。

    数据库选型:SQL 还是 NoSQL?

    原文给出了两大类选择:

  • 关系型数据库(SQL):结构化数据以表格形式存储,典型代表是 MySQL、PostgreSQL;
  • 非关系型数据库(NoSQL):适合非结构化数据或极低延迟的场景,按数据模型可分为四个类别:
    • Key-Value 存储(键值存储);
    • 图数据库(Graph Databases);
    • 列式存储(Column Stores);
    • 文档存储(Document Stores)。
  • 什么时候该选 NoSQL?

    原文列出了四个判断条件,当满足以下任一情况时,非关系型数据库可能是更合适的选择:

    • 应用需要超低延迟;
    • 数据是非结构化的,或者根本不存在关系型数据;
    • 只需要对数据做序列化与反序列化(JSON、XML、YAML 等);
    • 需要存储海量数据。

    这个判断框架在面试中非常实用:不要为了“技术先进”而选 NoSQL,而是从延迟要求、数据形态、访问模式三个维度去论证。仓库中后续的第 6 章 06. Key-Value Store/Readme.md 正是基于 Key-Value 存储模型展开的完整设计,可以作为本节选型决策的延伸阅读。

    四、垂直扩展与水平扩展的取舍

    垂直扩展(Vertical Scaling)

    给现有服务器加资源:升级 CPU、加内存、换更快的磁盘。

    • 优点:改动小,应用代码基本不用动;
    • 缺点:受硬件规格上限约束,且单台机器越强大,单点故障的爆炸半径越大,缺乏冗余。

    水平扩展(Horizontal Scaling)

    向服务器池中添加更多机器,由负载均衡器负责在多台服务器之间分发请求。这种方式更适合大规模系统,是本章后续所有演进(多 Web 服务器、主从复制、分片)的基础。

    五、负载均衡器(Load Balancer):水平扩展的前提

    当 Web 层变成多台服务器后,需要一个负载均衡器(Load Balancer) 来在它们之间分发流量。原文强调它带来两个核心收益:

  • 冗余性(Redundancy):如果某台服务器宕机,负载均衡器会把流量重新路由到健康的服务器。原文举例:如果 server 1 离线,所有流量都会被路由到 server 2;
  • 可扩展性(Scalability):流量激增时可以平滑地新增服务器承接增量。原文举例:如果网站流量快速增长,可以追加服务器处理新增流量。
  • 从源码仓库的章节组织看,负载均衡器是“水平扩展”与“数据库分片”之间的桥梁:没有它,多加的 Web 服务器无法被客户端感知;而到了数据层,类似的角色则换成了分片路由与一致性哈希(见本文第十二节)。

    六、数据库主从复制(Database Replication)

    当单台数据库服务器的读压力达到极限,且读多写少的特征明显时,下一步是引入主从复制。

    ![主从复制架构:Master 处理写操作,多个 Slave 分担读操作](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/01. Scaling/images/database-replication.png?utm_source=gitcode_repo_files)

    Master-Slave 模型

    • Master 数据库:负责所有写操作。所有数据修改命令(INSERT、UPDATE、DELETE)都必须发送到 master;
    • Slave 数据库:负责读操作,提升性能与可靠性。由于大多数应用的读写比是读远大于写,系统里 slave 的数量通常多于 master(master 一般只有一个)。

    两大收益

  • 性能:读操作可以并行地分发到多个 slave,摊薄单节点读压力;
  • 高可用与数据可靠性:通过数据冗余,任何单个 slave 故障不影响整体读服务。
  • 故障处理路径

    原文对三种故障场景给出了明确的处理策略,这部分是面试中经常被追问的细节:

  • 单个 slave 故障:如果系统里只有一个 slave 且它宕机,读操作会临时回落到 master 上执行;
  • 多个 slave 之一故障:读操作被重定向到其余健康的 slave,同时用一台新服务器替换掉故障节点;
  • master 故障:将一个 slave 提升(promote) 为新的 master;
  • 提升后的数据一致性:原文特别提醒,生产环境中被选中的 slave 可能尚未同步到最新数据,因此需要运行数据恢复脚本(data recovery scripts) 来补数据。原文还指出,multi-master(多主)与 circular replication(环形复制)等方案可以在一定程度上缓解这类问题。
  • 值得注意的推论:主从复制解决的是“读扩展”和“部分冗余”,但 master 的写入能力仍然是天花板——这正是第十二节水平分片要解决的问题。

    七、缓存层(Caching):把热点数据搬进内存

    缓存(cache) 将高频访问的数据保存在内存中,减少对数据库的访问压力。缓存层是一个临时数据存储层,速度远快于数据库。

    五个关键设计考量

    原文给出了缓存设计的五要素,每一条都对应一类真实故障或成本问题:

  • 适用场景(Use case):当数据读得频繁但很少被修改时,才适合引入缓存;写多读少或更新极频繁的数据放进缓存会得不偿失;
  • 过期策略(Expiration Policies):缓存数据过期后即从缓存中移除。如果没有配置过期策略,缓存数据将永久驻留内存,占用空间直至被淘汰;
  • 一致性(Consistency):指保持数据存储与缓存的同步。由于对数据库和缓存的数据修改操作不在同一个事务里,不一致是必然会发生的问题,必须显式处理(如写后失效、延迟双删等策略);
  • 故障缓解(Mitigating failures):单台缓存服务器本身就是一个潜在的单点故障,原文建议在不同数据中心部署多台缓存服务器来避免 SPOF(Single Point of Failure);
  • 淘汰策略(Eviction Policies):缓存写满后必须淘汰条目释放内存,原文指出 LRU(最近最少使用)是最流行的缓存淘汰策略。
  • 从仓库的章节脉络看,Key-Value 存储与缓存在思想上同源:第 6 章 06. Key-Value Store/Readme.md 中讨论的副本、quorum 与向量时钟等内容,可以视为“缓存层如何做高可用与一致性”的深化版本。

    八、CDN(内容分发网络):静态资源的地域化分发

    CDN 通过在地理上分布的服务器上缓存静态内容(图片、CSS、JavaScript)来改善页面加载速度。

    工作流程

  • 用户从离自己最近的 CDN 节点请求内容;
  • 如果节点上没有该内容(缓存未命中),则回源到源站(origin server) 拉取,并在 CDN 节点上缓存下来,供后续请求直接使用。
  • 四个落地考量

  • 成本(Cost):CDN 由第三方提供商运营,会对进出 CDN 的数据传输量计费,静态资源上 CDN 之前要核算流量成本;
  • 缓存过期时间(Cache Expiry):过期时间不宜过长(更新不及时)也不宜过短(回源率过高),需要按资源类型分别调参;
  • CDN 降级(CDN fallback):CDN 出现临时性故障时,客户端应当能检测到问题,并回退到直接从源站请求资源;
  • 文件失效(Invalidating files):文件更新后,必须主动使 CDN 缓存失效,让客户端拿到新版本文件。
  • 九、无状态 Web 层(Stateless Web Tier)

    前面几节已经让 Web 层变成了多台服务器,但如果会话数据存在某台具体的服务器内存里,请求被负载均衡到“另一台”服务器时就会丢失上下文。解法是:把会话数据(session data)搬到共享数据存储中,让每台 Web 服务器本身不再保存状态。

    无状态化带来两个直接收益:

  • 更容易水平扩展:任意一台 Web 服务器都可以处理任意请求,加机器、缩机器都不需要迁移状态;
  • 可以按流量自动扩缩容(auto-scaling):既然服务器之间可互换,就可以根据实时流量弹性伸缩,而不必为会话粘滞保留冗余容量。
  • 这一节是全文的关键分水岭:从这步开始,Web 层才真正具备了“无限加减机器”的能力。

    十、多数据中心部署(Multi-Data Center Setup)

    将服务部署到多个数据中心,可以同时改善可用性(单个机房整体故障不影响全局)和延迟(用户连接到更近的机房)。

    原文给出的两大策略与三个关键考量:

    两大策略

  • GeoDNS 路由:根据用户地理位置,将 DNS 解析指向最近的数据中心;
  • 数据复制:在各数据中心之间同步数据,避免读到不一致的副本。
  • 三个关键考量

    • 流量重定向(Traffic redirection):需要有效的工具(通常是 GeoDNS)把流量导向正确的数据中心;
    • 数据同步(Data synchronization):跨数据中心复制数据是常用策略,但要处理延迟与冲突;
    • 测试与部署(Test and deployment):自动化的部署工具至关重要,用于保证所有数据中心上的服务版本与配置保持一致——多机房最怕的不是单点故障,而是“两个机房跑着不同版本”这种不一致。

    十一、消息队列(Message Queue):用异步解耦同步调用

    消息队列是一个持久化的组件,支持异步通信,在系统中充当缓冲区(buffer),用于分发异步请求。

    基本角色

    • 生产者/发布者(Producers/Publishers):上游服务创建消息并发布到消息队列;
    • 消费者/订阅者(Consumers/Subscribers):其他服务连接队列,执行消息所定义的动作。

    为什么值得引入?

    仓库第 19 章 19. Distributed Message Queue/README.md 的开头对消息队列的收益给出了更完整的表述,恰好可以为本节的简短介绍做补充:

    • 解耦(Decoupling):消除组件之间的紧耦合,允许它们独立更新;
    • 更好的可扩展性(Improved scalability):生产者和消费者可以根据流量独立扩缩容;
    • 更高的可用性(Increased availability):系统的某一部分宕机时,其余部分仍可以继续与队列交互;
    • 更好的性能(Better performance):生产者发出消息后无需等待消费者的确认。

    也就是说,消息队列把“调用方必须同步等结果”的强依赖,转化为“发消息 + 异步消费”的弱依赖,是后续大规模系统中通知、日志采集、数据同步等场景的通用基础设施。第 19 章还进一步讨论 Kafka、RabbitMQ 等实现的分区、消费者组与交付语义等细节,可作为本章“消息队列”一节的深入阅读。

    十二、日志、指标与自动化(Logging, Metrics, and Automation)

    架构层级越复杂,可观测性就越关键。原文把这一层归纳为三件事:

  • 日志(Logging):跟踪错误与系统健康状况;
  • 指标(Metrics):提供关于性能与用户活动的洞察(如请求成功率、延迟分布);
  • 自动化(Automation):让测试、部署、扩缩容流程自动化,减少人工操作在多机、多机房环境下的不一致风险。
  • 仓库的第 20 章 20. Metrics Monitoring and Alerting System/README.md 正是围绕“指标监控与告警”展开的完整设计案例,覆盖了采集、传输管道、时序数据存储与告警渠道等细节,可以作为本节“Metrics”一项的完整展开。

    十三、数据库水平扩展:分片(Sharding)

    当主从复制也无法满足写入吞吐或存储容量时,就需要对数据库本身做水平扩展。

    垂直扩展(回顾与局限)

    给数据库服务器加硬件资源,但存在物理与成本上的硬上限。原文还补充了两点常被忽视的缺点:

    • 单点故障风险更高——数据库是全部数据的唯一副本所在,这台机器故障影响面最大;
    • 垂直扩展的总体成本很高——高端硬件的边际价格曲线非常陡峭。

    水平扩展:Sharding

    ![数据库水平分片架构:数据按分片键分布到多个 Shard](https://raw.gitcode.com/GitHub_Trending/sy/system-design-notes/raw/9d8388721e7231442763ad37398b8d82224aa68f/01. Scaling/images/horizontal-scaling.png?utm_source=gitcode_repo_files)

    • 用分片键(例如 user_id)把数据切分到多个 shard 上;
    • Sharding 将大型数据库拆分为更小的、更易管理的部分,每个 shard 即一个这样的部分;
    • 每个 shard 共享相同的 schema,但各 shard 上实际存放的数据彼此独立;
    • 分片键的选择至关重要:要选一个能让数据均匀分布的键,否则会出现严重的倾斜。

    三大挑战及原文给出的解法

  • 再分片(Resharding data):在以下情况需要重新切分数据:
    • 某个 shard 因为数据快速增长而装不下更多数据;
    • 由于数据分布不均,某些 shard 比其他 shard 更早耗尽(shard exhaustion)。
    • 原文指出,一致性哈希(Consistent Hashing) 用来解决这些问题——它能让增减节点时只有少量 key 需要迁移。仓库第 5 章 05. Consistent Hashing/Readme.md 给出了完整推导:传统 serverIndex = hash(key) % N 在节点数变化时会导致大部分 key 重新映射(引发缓存雪崩式失效),而一致性哈希通过“哈希环 + 顺时针查找”把受影响范围压缩到相邻区间,并可用虚拟节点进一步均衡负载;
  • 名人问题(Celebrity problem):对某个特定 shard 的过度访问(比如某个明星用户的资料被高频读取)会造成该 shard 服务器过载。原文给出的解法是:可以为每个“名人”单独分配一个 shard,把热点隔离出去;
  • 跨 shard 的 JOIN 与反规范化(Join and de-normalization):数据库分片到多台服务器后,跨 shard 的 join 操作变得困难。原文给出的常见折中是**反规范化(de-normalize)**数据库,让查询可以在单表内完成,以冗余换查询性能。
  • 十四、五条核心经验(Key Takeaways)

    原文的结论部分给出了贯穿全章的五条设计准则,这也是对整条演进路径的浓缩:

  • 保持 Web 层无状态(Keep the web tier stateless);
  • 在每一层都构建冗余(Build redundancy at every tier);
  • 用缓存和 CDN 优化性能(Use caching and CDNs to optimize performance);
  • 用分片扩展数据层(Scale the data tier with sharding);
  • 解耦组件以获得灵活性(Decouple components for flexibility)。
  • 把这条主线串起来,就是本章给出的完整演进序列:

    阶段解决的瓶颈核心手段
    单服务器 起点 Web + DB + Cache 同机
    数据库分离 存储与 Web 争抢资源 DB 独立部署,独立扩缩
    垂直扩展 单机容量不足 升级 CPU/内存(有上限)
    水平扩展 + 负载均衡 单机 Web 容量不足 多服务器 + 负载均衡分发
    主从复制 读吞吐不足 Master 写、多 Slave 读
    缓存 热点数据反复读库 内存缓存 + 过期/淘汰策略
    CDN 静态资源传输延迟 边缘节点缓存 + 回源
    无状态 Web 层 会话粘滞阻碍扩缩容 会话外置到共享存储
    多数据中心 单机房故障 / 地域延迟 GeoDNS + 跨中心数据复制
    消息队列 组件强耦合、同步等待 生产/消费异步解耦
    日志指标自动化 规模上去后不可观测 Logging + Metrics + 自动化
    分片 单库写入/容量上限 按分片键水平切分 + 一致性哈希

    十五、延伸阅读(仓库内其他章节)

    本仓库 Readme.md 中,与第一章直接衔接的后续笔记包括:

    • 05. Consistent Hashing/Readme.md:分片章节提到的 Resharding 难题的完整解法,含哈希环与虚拟节点;
    • 06. Key-Value Store/Readme.md:缓存与 NoSQL 选型讨论的工程化落地,含复制、quorum、向量时钟;
    • 19. Distributed Message Queue/README.md:消息队列一节的深化,含分区、消费者组、交付语义;
    • 20. Metrics Monitoring and Alerting System/README.md:日志与指标一节的深化,覆盖监控系统的完整设计流程。

    需要注意的是,本仓库自我标注为“work in progress”的读书笔记,且基于《System Design Interview》书籍整理,因此文中内容以方法论与架构模式讲解为主,不涉及特定框架版本或具体产品的运行配置;实际落地时应结合所选技术栈(如具体数据库、Kafka 版本等)的官方文档做二次验证。

    赞

    分享

    • 文档
    • 教程
    • 后端

    【免费下载链接】system-design-notes

    Notes of the book System Desgin Interview – An Insider's Guide

    项目地址:
    https://gitcode.com/GitHub_Trending/sy/system-design-notes

    点击查看 免费下载

    上一篇:
    ChatDev 终极指南:多智能体协作开发完整教程

    下一篇:
    3个技术突破彻底解锁跨平台流媒体下载

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » system-design-notes 第一章解析:从单台服务器到百万用户——系统架构的逐级扩展(Scaling)全景指南
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!