☰
system-design-101 实战:从单机单体到支撑千万用户的网站扩展六步曲
2026/10/2 13:43:06 网站建设 项目流程
  • 后端
  • 文档
  • 教程

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

Explain complex systems using visuals and simple terms. Help you prepare for system design interviews.

项目地址:https://gitcode.com/GitHub_Trending/sy/system-design-101
点击查看免费下载

这篇指南以 system-design-101 仓库中的 如何扩展网站以支撑百万级用户 文档为核心,用一个简化的电商网站作为贯穿案例,完整梳理网站从"单台服务器上的单体应用"演进为"面向服务/微服务架构"的六步路线图。读完本文,你将掌握应用与数据库拆分、集群化与负载均衡、读写分离、数据库分区、缓存层以及微服务化这几大核心扩展手段各自的适用场景、实现细节与代价,并能直接用于系统设计面试的口述与架构选型。

背景:一个简化的电商网站

先设定一个具体的案例。假设我们的电商系统包含两个核心业务域:

  • 库存服务(inventory service):负责商品描述与库存管理;
  • 用户服务(user service):负责用户信息、注册、登录等。

扩展的本质,是"系统在负载增长时仍能保持原有性能"。从仓库中的 架构可扩展性速成课 可以看到,扩展通常面临三大瓶颈:

  1. 集中式组件(Centralized components):容易成为单点故障;
  2. 高延迟组件(High latency components):执行耗时操作,拖慢整条链路;
  3. 紧耦合(Tight coupling):组件互相依赖,难以独立扩展。

因此,可扩展系统的设计原则可以概括为:无状态(statelessness)、松耦合(loose coupling)、异步处理(asynchronous processing)。下面六步正是沿着这条主线展开的。

Step 1:把应用服务器与数据库服务器拆开

随着用户量增长,第一道坎是"一台服务器扛不住全部负载"。此时最基础的动作是把应用服务器与数据库服务器分别部署到两台独立机器上:

  • 应用服务器专职处理请求逻辑、渲染与业务计算;
  • 数据库服务器专职存储与查询,独立分配磁盘 I/O 与内存资源。

这一步消除了"应用计算与数据库存取争抢同一台机器资源"的互相干扰,为后续各自独立扩展(垂直扩容或水平扩容)创造了前提。它是所有后续步骤的地基:先分离,再扩展。

Step 2:部署应用服务器集群

业务继续增长,单台应用服务器再次不够用。此时不再无限堆高单机配置(垂直扩展有硬件天花板),而是部署一组应用服务器集群,让负载在多台实例之间分担——这就是水平扩展(horizontal scaling)。

8 个必知扩展策略 中专门强调了水平扩展的两条配套原则:

  • 无状态服务(stateless services):服务不依赖服务器本地保存的会话或业务数据,任何一台实例都能处理任意请求,这样新增实例即可线性分摊压力;
  • 水平扩展(add more servers):通过增加服务器数量共享工作负载,而非单纯升级单机。

集群出现后,下一个问题立刻浮现:请求来了该交给哪台服务器?

Step 3:引入负载均衡,让流量均匀分发

有了多台应用服务器,就必须有一个组件把进来的请求均匀路由到它们身上,避免某台实例过载、其他实例闲置——这个组件就是负载均衡器(load balancer)。

仓库中的 什么是负载均衡器 给出了完整的能力清单与分类:

负载均衡器做什么

  • 分发流量(Distributes traffic);
  • 保证可用性与可靠性(Ensures availability and reliability)——配合健康检查,自动摘除故障实例;
  • 提升性能(Improves performance);
  • 让应用得以扩展(Scales applications)。

类型与层次

  • 硬件负载均衡:专用物理设备分发流量;
  • 软件负载均衡:部署在标准硬件或虚拟机上的应用程序(如 Nginx 等);
  • 云负载均衡:云厂商提供的集成式方案,例如 AWS Elastic Load Balancer、Google Cloud Load Balancing、Azure Load Balancer;
  • L4 负载均衡(传输层):基于 IP 地址与 TCP/UDP 端口做转发决策,性能高、对内容无感知;
  • L7 负载均衡(应用层):工作在 OSI 应用层,可按 URL、请求头、Cookie 等内容做更精细的路由;
  • GSLB(全局服务器负载均衡):跨地域分发流量,用于全球范围的高可用与就近访问。

结合 架构可扩展性速成课 的定义:负载均衡把请求分散到多台服务器上,防止单台服务器成为瓶颈。注意,负载均衡同时也消除了"单台应用服务器"这一集中式单点,呼应了前面提到的第一类瓶颈。

Step 4:读写分离,用读副本扛住读流量

业务持续增长,数据库开始成为新瓶颈。大多数业务是读多写少的(商品浏览、订单查询远多于下单),因此一个高性价比的做法是读写分离:把频繁的读查询分流到**读副本(read replicas)**上,主库专注写入,从而大幅提升数据库整体吞吐。

仓库中的 读副本模式 给出了这个模式的完整定义与工作流程:

所有数据修改命令(insert、delete、update)都发往主库(primary DB),读请求则发往读副本。

以电商下单为例的典型流程:

  1. 用户下单,请求到达订单服务(Order Service);
  2. 订单服务在主库写入订单记录,数据复制到两个副本;
  3. 用户查看订单详情,读请求由副本响应;
  4. 用户查看最近订单历史,仍由副本响应。

这个模式最大的坑是复制延迟(replication lag)。在网络延迟、服务器过载等情况下,副本中的数据可能落后主库几秒甚至几分钟。如果用户刚下完单立刻去查订单状态、而查询恰好被路由到落后的副本,就可能"看不到刚下的单",体验非常困惑。这需要"写后读一致性"(read-after-write consistency)。文档给出的三种缓解方案:

  • 对延迟敏感的读请求,直接发往主库;
  • 紧跟写操作之后的读请求,路由到主库;
  • 关系型数据库通常提供"副本是否追平主库"的检测能力:副本数据已最新则查副本,否则读主库或让该次读请求失败重试。

实现方式:应用层路由 vs 数据库中间件

如何实现读副本模式 介绍了两种落地方式:

  1. 把路由逻辑内嵌到应用代码中(应用自己判断读请求走主库还是副本);
  2. 使用数据库中间件——中间件在应用与数据库之间提供透明的路由,可基于用户、schema、SQL 语句等规则定制路由逻辑。它通常走标准 MySQL 网络协议,因此任何兼容 MySQL 的客户端都能接入,迁移成本低。

两种方式各有取舍:应用内嵌路由实现简单但应用要感知数据库拓扑;中间件让应用代码保持简洁、兼容性好,但引入了额外的复杂层与网络延迟,且由于所有查询都经过它,必须做高可用部署以避免单点故障。

Step 5:单个数据库扛不住时,垂直分区 / 水平分区 / 缓存三选一

假设业务继续增长,单个数据库已经无法同时承受 inventory 表和 user 表的负载。此时原文档给出三个选项:

选项 A:垂直分区(Vertical Partitioning)

给数据库服务器加更多算力(CPU、内存等)。它的优点是无侵入、见效快,但存在硬性上限——单机硬件总有天花板,且成本随配置非线性上涨。原文档明确标注"it has a hard limit",因此它适合作为过渡手段,而不是终极方案。

选项 B:水平分区(Horizontal Partitioning / Sharding)

增加更多数据库服务器,把数据分散到多台机器上。垂直分区与水平分区 给出了两种策略的精确定义:

  • 垂直分区:把某些列移到新表,每张表行数相同但列更少;
  • 水平分区(常称分片 sharding):把一张表切成多张更小的表,每张表列数相同但行更少,每张表是独立的数据存储。

水平分区应用最广,关键在于**路由算法(routing algorithm)**决定数据落在哪个分片:

  • 基于范围的分片(range-based sharding):用有序列(整数、long、时间戳等)切分。例如按 User ID 范围:ID 1、2 进 shard 1,ID 3、4 进 shard 2;
  • 基于哈希的分片(hash-based sharding):对一列或多列做哈希决定归属。例如以User ID mod 2作为哈希函数:ID 1、3 进 shard 1,ID 2、4 进 shard 2。

收益:便于水平扩展(加机器即扩容量)、缩短响应时间(查询只需扫更少的行)。代价:跨分片的 ORDER BY 排序变复杂(通常要取回各分片数据在应用层排序)、可能出现数据倾斜(某些分片数据远多于其他分片,即热点 hotspot)。这与 8 个必知扩展策略 中"分片可同时扩展读写"的定位一致。

选项 C:加缓存层,卸载读压力

增加缓存层把读请求从数据库卸载出去,让高频重复查询直接在内存中命中。仓库中的 5 大缓存策略 列出了引入缓存后与数据库保持同步的常用策略:

读策略

  • Cache Aside:应用先查缓存,未命中再查数据库并回填缓存;
  • Read Through:缓存层自己负责从数据库加载缺失数据。

写策略

  • Write Around:直接写数据库,缓存靠读时回填更新;
  • Write Back:先写缓存,后台异步刷入数据库,吞吐高但有丢数据风险;
  • Write Through:写数据库的同时同步更新缓存,一致性最好但写路径更慢。

这些策略常组合使用,例如Write Around 常与 Cache Aside 搭配,保证缓存不会长期脏数据。

一个值得注意的定位区别:分区(B)解决的是"数据太多单机装不下/写不动的容量问题";缓存(C)解决的是"读请求太频繁的性能问题"。两者并不互斥,在实践中往往叠加使用——缓存顶住读热点,分片摊开写与容量。

Step 6:模块化拆分,走向面向服务 / 微服务架构

最后一步,把单体应用内部的函数按业务域模块化拆分成不同的服务,架构由此演进为面向服务(service-oriented)或微服务(microservice)。回到开头的案例,即库存服务与用户服务成为独立部署、独立扩展的单元。

这一步的价值与配套原则,可以结合仓库相关文档理解:

  • 每个服务拥有独立的生命周期与扩容策略——比如用户量暴涨时只需单独扩 user service,而不是整体扩容;
  • 服务间通过 API 通信,实现松耦合,可独立演进与故障隔离;
  • 从 8 个必知扩展策略 看,微服务化之后应继续贯彻异步处理(async processing):把耗时、资源密集的任务(如发邮件、生成报表)挪到后台 worker 异步执行,让新请求能快速得到响应,避免被长任务阻塞。

需要注意的是,微服务并非银弹:它引入了服务发现、API 网关、分布式事务、可观测性等一系列新的复杂度。它应被看作扩展演进的阶段结果,而不是起点——先有清晰的业务边界与稳定的接口契约,再谈拆分。

总结:六步扩展路线图

把整条演进路线浓缩成一张速查表,也是系统设计面试时最常被要求的"从 0 到 1 扩展系统"的标准答题骨架:

步骤触发条件核心动作解决的核心问题参考文档
Step 1单机负载过高拆分应用服务器与数据库服务器资源互相争抢扩展网站
Step 2单应用服务器不足部署应用服务器集群吞吐容量不足8 个必知扩展策略
Step 3多实例流量分配引入负载均衡器流量不均、单点故障什么是负载均衡器
Step 4数据库成瓶颈主从复制 + 读写分离读吞吐不足读副本模式、如何实现读副本模式
Step 5单库容量/写压力见顶垂直分区、水平分区、加缓存层容量与读压力垂直分区与水平分区、5 大缓存策略
Step 6单体难以独立演进模块化拆分为面向服务/微服务独立扩展与故障隔离扩展网站

面试要点提示:回答"如何扩展到百万级用户"这类问题时,按 Step 1 → Step 6 的顺序逐步演进,每步都说明"为什么当前架构撑不住了 + 这步做了什么 + 引入了什么新问题",比直接抛出微服务方案更能体现工程判断力。同时记住贯穿全程的三条原则:服务无状态、组件松耦合、耗时任务异步化,它们是任何一层扩展方案背后的通用逻辑。

如果想继续深入某一环,仓库中还有 数据库扩展的 7 大必知策略、一致哈希、缓存系统如何用 与 消息队列类型 等专题文档可以衔接阅读。

  • 后端
  • 文档
  • 教程

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

Explain complex systems using visuals and simple terms. Help you prepare for system design interviews.

项目地址:https://gitcode.com/GitHub_Trending/sy/system-design-101
点击查看免费下载
上一篇:xberg C FFI 实战:用 `xberg_extract_batch` 一次批量提取多个内存字节文档
下一篇:Kimi Code CLI 环境变量完全指南:数据路径、模型切换、遥测控制与代理配置

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询