Bakka's Blog

后端架构演进

2026/6/15

本文目录

上级:后端学习 from zero

架构演进的本质,是用空间换时间、用复杂度换性能。性能问题通常不是凭空出现的,而是用户、请求和并发增加后,原来的资源边界被逐渐触碰。

1. 单机

用户很少时,一台机器可以同时运行应用和数据库。部署简单、调用没有网络开销,机器资源也足够使用。

用户增加后,应用和数据库会竞争 CPU、内存、磁盘 I/O 和网络资源。第一步通常是把应用服务器和数据库服务器拆开,让两类负载使用独立资源。

2. 应用集群和负载均衡

单台应用服务器有负载上限,就部署多台相同代码,通过负载均衡分发请求,这叫水平扩展;多台服务器组成集群。

负载均衡还带来高可用能力:某台机器故障后,流量可以转移到正常实例。此时应用尽量设计为无状态,用户状态放在共享存储中,避免请求必须命中某一台机器。

3. 缓存

数据库使用磁盘保存完整数据,而内存读取快得多。对于高频、可接受短暂延迟的数据,可以引入多级缓存:

浏览器 HTTP 缓存
        -> 应用本地缓存(Caffeine)
        -> 分布式缓存(Redis)
        -> 数据库

缓存能拦截大量热点读,但也会引入缓存穿透、击穿、雪崩、更新时机和一致性问题。它不是免费的数据库替代品,应根据数据的可变性和一致性要求选择缓存策略。

4. 读写分离

数据库的读和写工作负载不同,写操作还会产生锁竞争;现实业务往往读多写少,因此写压力可能拖慢读性能。

可以让主库负责写,从库负责读,形成一主多从:

写请求 -> 主库 -> 复制 -> 从库
读请求 ----------------> 从库

读写分离需要处理复制延迟、读己之写、主库故障切换和读路由,不应只把连接配置改成多个地址就认为完成了分离。

5. 分库分表

主从实例仍可能保存同一份单库单表数据,数据量继续增长后,单表和单库会成为逻辑瓶颈。

  • 垂直分库:按业务域拆分用户库、商品库、订单库。
  • 水平分表:按用户 ID 等分片键把一张大表拆成多张小表。

分片提升存储和并发能力,但跨库事务、跨分片查询、分布式 ID、数据迁移和热点分片都需要额外设计。

6. CDN、网关和中间件

CDN 负责更靠近用户分发静态或可缓存内容;反向代理和网关可以统一处理流量转发、TLS、鉴权、限流和基础安全策略。

业务越来越复杂时,数据库也不应承担所有工作:全文搜索可以交给 Elasticsearch,非结构化或半结构化数据可以使用 NoSQL 或对象存储。

7. 分布式与微服务

单体应用过大后,一个小修改也需要完整部署,团队之间容易互相影响,且无法只给热点业务单独扩容。可以按业务域拆成用户、订单、商品等独立服务,各自部署、发布和扩容。

Client -> Gateway -> User Service
                  -> Order Service -> Product Service
                  -> Notification Service

拆分后服务之间不能再依赖本地方法调用,需要网络通信。常见配套设施包括:

  • RPC:统一服务间调用协议。
  • 服务注册与发现:让服务能找到彼此。
  • 消息队列:把同步调用改成异步事件,削弱时间耦合。
  • 全链路追踪:定位跨服务慢请求。
  • 限流、熔断、降级:避免故障扩散。
  • 日志、指标和告警:提供可观测性。

消息队列的价值不只是“更快”,还包括解耦生产者与消费者、削峰填谷、失败重试和最终一致性。但它会带来重复消费、乱序、堆积和投递失败等新问题。

8. 容器与云

服务数量增加后,手工管理运行环境和机器成本很高。容器封装运行环境,Kubernetes 负责调度、自动重启、滚动发布和扩缩容;云平台则让计算、存储和网络资源按需申请、按量付费。

什么时候不要演进

每一步都是 trade-off。小流量项目可能只需要单体 + 数据库 + 合理索引;只有当监控数据证明瓶颈存在,或业务确实需要隔离发布、独立扩容和高可用时,才引入下一层复杂度。按最复杂的分布式 case 设计,通常是在逃避对业务规模和一致性要求的判断。