




























业务一涨,单机迟早到顶——CPU 跑满、内存吃光、连接数撑爆。这时候有两条路:要么把这台机器换成更猛的,要么再搬几台一样的机器上来一起扛。前者总有物理上限,后者理论上可以一直加下去。
好架构的标志之一,就是"加机器就能线性扛更多"——这就是可扩展性(Scalability)。这篇就拆开看:为什么"加机器"才是长久之计、加机器的前提条件是什么、请求怎么分到多台机器上、以及最难啃的数据层怎么扩。这一篇对应《架构演进》开篇地图里"用户变多 → 应用集群 + 负载均衡"那一格,把它讲透。
先上一张全景——这一篇要搭的,就是下面这套"加机器就能扛更多"的架构:用户请求先过负载均衡,分给一堆无状态应用实例,后面接共享缓存和读写分离的数据层。后面几节就顺着这张图逐层拆开。

是什么。 面对"单机扛不住",业界有两种最基本的扩展方向,名字很对仗:
| 方向 | 英文 | 做法 | 俗称 |
|---|---|---|---|
| 垂直扩展 | Scale Up | 给同一台机器升配:加 CPU、加内存、换更快的磁盘 | 把机器"养肥" |
| 水平扩展 | Scale Out | 加更多同样规格的机器,一起分担 | 把机器"养多" |
一句话点破:垂直是让一台更强,水平是让多台一起上。
为什么水平扩展才是长久之计。 垂直扩展上手最简单——不用改代码,云控制台上把规格往上调一档就行,所以早期顶一阵很划算。但它有两个躲不开的天花板:一是物理上限,单台机器的 CPU 核数、内存条数总有极限,再有钱也买不到"无限强"的一台;二是成本曲线陡,配置越往高端走,价格越不是线性涨,顶配机器往往贵得离谱,性价比急剧下滑。更要命的是,单台机器再强,它挂了就是全挂——垂直扩展天然和高可用是矛盾的。
水平扩展则相反:理论上你想扛多少流量,就加多少台普通机器,容量随机器数量近似线性增长;而且多台之间天然互为备份,挂一台还有别的顶着。代价是复杂度——多台机器之间怎么协调、状态放哪、请求怎么分,都是新问题,后面几节就是在解决这些。
实际业务场景。 一个典型的成长路径:产品早期流量小,单台 8 核 16G 够用;流量涨了,先升到 16 核 32G(垂直,省事);再涨,发现单台已经到了云厂商能给的较高规格、再升一档价格翻倍还不止——这时候就该转向水平,把应用部署成多台一样的机器组成的集群,前面架个负载均衡分流。绝大多数互联网系统最终都走上了水平扩展这条路,垂直扩展更多是"早期过渡"和"临时救急"的手段。
注意事项。 水平扩展不是"想加就能加"的——它有一个硬前提:系统得拆得开、加进来的机器能真正分担压力。如果你的服务把用户状态存在了本地内存里(比如登录态就记在这台机器上),那加机器根本没用:请求落到新机器上,它不认识这个用户。所以水平扩展的第一道门槛,是下一节要讲的无状态设计。
是什么。 无状态(Stateless)指的是:一台服务实例不在本地保存"跨请求要用的状态"——它处理完一个请求,不在自己内存或本地磁盘上留下"下次请求还得靠它"的东西。同样一个请求,发给集群里任意一台机器,得到的结果都一样。
反过来,有状态(Stateful)就是把状态记在了本地:这台机器内存里存着张三的登录态、购物车、上传到一半的文件……于是张三的后续请求必须回到这台机器才认得。
为什么无状态是水平扩展的前提。 关键就一句话:有状态,机器就没法随便加和减。
而无状态让每台机器都是"平等且可替换"的——请求爱落哪台落哪台,挂一台、加一台、减一台,对用户都无感。这才是"加机器就能线性扛更多"能成立的根基。
实际业务场景。 最经典的就是登录态(session)。早期单机时代,session 默认就存在应用进程的本地内存里——单机没问题。可一旦做成多台集群,问题立刻暴露:张三在 A 机器登录,session 记在 A 上;下一个请求被负载均衡分到了 B 机器,B 不认识他,直接把他当成未登录踢去登录页。
业界的标准解法是把状态从本地"外置"出去,挪到一个所有机器都能访问的共享存储——session 最常见的去处就是 Redis。每台应用机器都不再自己存 session,而是统一去 Redis 读写。这样应用层就彻底无状态了,加减机器随意。这一点正好接上《「缓存」》篇讲的 Redis"短命数据的主存储"角色:session 本就短命、超高频读、不需要数据库副本,天生适合放 Redis(带 TTL,过期自动清)。
注意事项。 无状态最大的陷阱是那些藏起来的本地状态,你以为无状态了,其实没有:
判断标准很简单:把任意一台机器随时拔掉,用户会不会受影响? 会,就说明还有状态藏在本地没清干净。
是什么。 应用做成无状态、部署成多台之后,马上有个现实问题:用户的请求,到底发给哪一台? 总不能让用户自己挑。负载均衡(Load Balancing)就是干这事的——在用户和后端机器集群之间架一个"分发器(负载均衡器)",它对外是一个统一入口,对内把涌进来的请求按某种策略分摊到后端的多台机器上,让大家压力尽量均匀。
为什么(它在水平扩展里的位置)。 没有负载均衡,水平扩展就是一句空话:机器加了一堆,但没有一个东西负责把流量分下去,等于白加。负载均衡器是整个"加机器扛更多"链条里承上启下的那一环——上承用户的统一入口,下接可随意增减的无状态机器集群。
业界怎么做。 这块开源和云方案都非常成熟,选型时大致认这几类:
| 类别 | 代表 | 特点 |
|---|---|---|
| 软件负载均衡 | Nginx、HAProxy | 应用层(七层)主力,功能丰富、配置灵活,最常用 |
| 更底层的软件 | LVS | 工作在网络层(四层),性能极高,常扛在 Nginx 前面做第一道分流 |
| 云厂商托管 | AWS 的 ELB / ALB / NLB、阿里云 SLB | 开箱即用、自带高可用和弹性,云上业务首选,免去自己运维 |
这里有两个绕不开的基础概念:
① 四层 vs 七层。 这是按它"看得懂请求的哪一层"来分的:
/api/ 走一组、按 /static/ 走另一组"这样精细分发,还能顺手做 SSL 卸载、改写请求。功能强但开销略大。代表:Nginx、ALB。一句话:四层快而粗,七层细而全;实际架构里两者常常叠着用(LVS 在前做四层分流,Nginx 在后做七层路由)。
② 分发策略(把请求分给谁)。 常见几种,各有适用场景:
| 策略 | 怎么分 | 适合 |
|---|---|---|
| 轮询(Round Robin) | 一台一个,轮着来 | 后端机器配置一致时,最简单公平 |
| 加权轮询 | 按权重分,配置高的机器多分 | 机器配置不均时 |
| 最少连接(Least Connections) | 谁手上连接少就给谁 | 请求处理耗时差异大时,更能避免某台过载 |
| 一致性哈希 | 按某个 key(如用户 ID)哈希到固定机器 | 需要"同一来源尽量落同一台"时(如就近利用缓存) |
注意事项。
是什么。 前面三节把应用层扩展讲明白了:无状态 + 负载均衡,应用机器随便加。但有个东西天生加不动——数据库。数据库就是用来存状态的,它是整个系统里最有状态、也最难水平扩展的一层。应用层加机器是"复制一份一样的",数据库加机器却面临一个本质难题:数据怎么在多台之间切分、又不互相冲突。
为什么数据层更难。 应用机器是无状态的、彼此等价,复制 N 份毫无负担;但数据是唯一且要保证一致的——你不能简单地把数据库复制十份了事,那十份之间怎么保证一致?所以数据层的扩展,得读和写分开对付,因为它俩的瓶颈和解法完全不同。
实际业务场景:读怎么扩。 绝大多数业务都是读远多于写(刷商品、看订单的人,比下单的人多得多)。所以读的扩展相对好办,两板斧:
实际业务场景:写怎么扩。 写只能落主库,单个主库总有上限;数据量大到单表/单库装不下时,读写分离也救不了。这时候要上分库分表 / 分片(Sharding):把一张大表的数据,按某个规则水平切成多份,分散到多个库/多张表里,每个分片只存一部分数据、只扛一部分写。常见切分规则:
一致性哈希:解决"加减节点时数据怎么少搬"。 普通哈希分片有个致命问题:用 hash(key) % N(N 是机器数)来定位数据,一旦 N 变了(加一台或挂一台),几乎所有 key 的归属都会变,意味着要把海量数据在机器间大搬家,代价巨大、期间还可能不可用。一致性哈希(Consistent Hashing)就是来治这个的:它把哈希空间想象成一个环,机器和数据都映射到环上,数据归给"顺时针方向遇到的第一台机器"。这样加减一台机器时,只影响环上相邻的一小段数据,绝大部分 key 的归属不变,需要搬的数据量从"几乎全部"降到"一小部分"。所以一致性哈希在分布式缓存(如 Redis 集群)、分片存储里被广泛采用,本质是为水平扩展(增减节点)而生的。
注意事项。
JOIN、一个 COUNT 都可能要查所有分片再合并)。所以业界共识是:先用缓存 + 读写分离顶,真到单库扛不住了再分片,别提前给自己上难度。可扩展性这套(无状态 + 负载均衡 + 水平扩)放到 AI 推理服务上,大框架完全通用,但有几处不一样值得拎出来:
把全文收进一张表——分应用层和数据层两条线,各自的手段、方案和要当心的代价:
| 层次 | 扩展手段 | 主流方案 | 注意事项 / 代价 |
|---|---|---|---|
| 思路 | 优先水平扩展(Scale Out) | 加同规格机器组集群 | 垂直扩展有物理上限、成本陡、单点;水平是长久之计 |
| 应用层 | 无状态设计 | session 等状态外置到 Redis | 警惕本地文件 / 本地内存缓存 / 定时任务等隐藏状态 |
| 应用层 | 负载均衡 | Nginx、LVS、HAProxy、云 SLB/ALB | 健康检查必做;会话保持是退路,优先靠无状态 |
| 数据层(读) | 缓存 | Redis、Memcached | 一致性问题(见《缓存》篇) |
| 数据层(读) | 读写分离(一主多从) | 主库写、从库读,加从库扩读 | 主从同步延迟,写后立刻读要走主库 |
| 数据层(写) | 分库分表 / 分片 | 按范围 / 哈希切分 | 跨分片查询/事务难;能晚分就晚分;分片键要选好 |
| 数据层(扩缩容) | 一致性哈希 | Redis 集群、分片存储 | 加减节点只搬一小部分数据,而非全量重排 |
一条主线串起来:水平扩展是方向 → 无状态让应用层加得动机器 → 负载均衡把流量分下去 → 数据层才是真正的硬骨头,读靠缓存 + 读写分离、写靠分片、增减节点靠一致性哈希。应用层的"加机器就能扛"相对容易,数据层的扩展才是架构的深水区。
本文属《研发都要懂的事》·服务端架构设计系列,对应《「架构设计到底在设计什么」》开篇地图里"用户变多 → 应用集群 + 负载均衡"那一格的展开。涉及的缓存、读写分离细节见《「缓存」》篇;主从延迟、负载均衡器高可用等留给后面的高可用篇深入。
完整代码与系列在 GitHub · backend-notes。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。