目录
正在加载目录…
专栏文章
专栏文章
性能工程专栏
1. 高并发系统设计:架构演进与关键技术选型 2. 稳定性与容灾设计:限流、熔断、降级与隔离 3. 全链路压测:容量验证、流量构造与瓶颈定位 4. 秒杀系统设计:库存、防超卖与高并发治理 5. 性能优化方法论:指标分析与分层调优策略 6. 性能测试体系:压测方法、指标与瓶颈定位 7. 容量规划与弹性:水位线、扩缩容与大促保障

高并发系统设计:架构演进与关键技术选型

发布于 2026-08-19 15:02 · 最后编辑于 2026-08-19 15:03 · 字数 5,148 👁 27 次阅读

本文系统梳理高并发系统设计的核心方法:从 QPS/TPS/响应时间等核心指标出发,讲解水平扩展 vs 垂直扩展的选择依据,深入分析多级缓存、读写分离、分库分表、消息队列削峰、CDN 加速、连接池与线程池、异步化改造等关键技术,并给出系统演进的实践路径。

性能工程系列秒杀系统设计 · 性能优化方法论 · 相关:消息队列核心原理 · 分布式一致性

目录

章节说明
核心指标QPS / TPS / 响应时间 / 并发数
三大通用方法Scale-out / 缓存 / 异步
水平扩展 vs 垂直扩展两种扩容思路的适用场景
缓存层设计多级缓存 / 本地缓存 / 分布式缓存
数据库扩展读写分离 / 分库分表
消息队列削峰填谷异步处理 / 解耦合 / 削峰
CDN 静态资源加速静态资源就近分发
连接池与线程池池化技术的核心设计
系统演进路径从单体到高并发的渐进演进
存储架构的演进(许式伟视角)存储层次、外存管理、对象存储与 CDN
过载保护与容量规划(许式伟视角)雪崩成因、自适应节流、优雅降级
权衡法则:成本、效率、可靠性三角(郭东白视角)性能优化的商业价值量化、不同阶段的权重
分层高并发设计(李智慧视角)前端层/网关层/服务层/数据层各层的高并发策略
高并发三板斧的系统化讲解(李智慧视角)缓存/异步/水平扩展的深度展开与权衡

核心指标

指标定义说明
QPS(Queries Per Second)每秒查询次数衡量读请求处理能力
TPS(Transactions Per Second)每秒事务次数衡量写请求处理能力,通常 TPS < QPS
响应时间(RT)从请求发出到收到响应的时间通常关注 TP99(99% 请求的响应时间)
并发数同时处理的请求数量并发数 = QPS × 平均响应时间
DAU日活跃用户数估算峰值流量的基础数据

峰值流量估算方法

日均请求量 = DAU × 人均请求次数
平均 QPS = 日均请求量 / 86400
峰值 QPS ≈ 平均 QPS × 3(经验倍数)
设计目标 = 峰值 QPS × 4(预留余量)

参考基准:4核8G 云服务器上 MySQL 5.7 约可支撑 500 TPS / 10000 QPS。

三大通用方法

高并发系统设计的三种核心思路,类比治水方案:

graph TD
    A["高并发流量冲击"] --> B["Scale-out<br/>横向扩展<br/>(分流,类比都江堰)"]
    A --> C["缓存<br/>加速访问<br/>(拓宽河道)"]
    A --> D["异步<br/>削峰解耦<br/>(水库蓄洪)"]
    style A fill:#fcc,stroke:#c00
    style B fill:#cfc,stroke:#060
    style C fill:#cfc,stroke:#060
    style D fill:#cfc,stroke:#060

水平扩展 vs 垂直扩展

维度Scale-up(垂直扩展)Scale-out(水平扩展)
方式升级单机硬件(4核→8核,4G→8G)增加机器数量,组成分布式集群
优点简单,无需改造代码突破单机极限,理论上无上限
缺点有物理上限,成本指数增长引入分布式复杂性(一致性、故障处理)
适用阶段系统初期,流量未超单机极限并发超过单机极限后

Scale-out 引入的核心问题

  • 某节点故障如何保证整体可用性?
  • 多节点状态如何保持一致?
  • 如何做到对使用方无感知地增删节点?

缓存层设计

缓存的本质

缓存是空间换时间的性能优化手段。核心原理:

内存寻址:~100ns
磁盘寻道:~10ms
差距:约 10 万倍

多级缓存架构

graph TD
    U["用户请求"] --> CDN["CDN<br/>静态资源缓存"]
    CDN --> NGX["Nginx<br/>静态页面缓存"]
    NGX --> LC["本地缓存<br/>Guava Cache / Caffeine<br/>(热点数据,秒级 TTL)"]
    LC --> DC["分布式缓存<br/>Redis / Memcached<br/>(动态数据,分钟级 TTL)"]
    DC --> DB["数据库<br/>MySQL"]
    style CDN fill:#cfc,stroke:#060
    style LC fill:#cfc,stroke:#060
    style DC fill:#cfc,stroke:#060

原则:请求尽量挡在上层,越往下层,并发承受能力越差。

三种缓存类型对比

类型部署位置适用场景典型实现
静态缓存Nginx / CDN 节点静态内容(文章、图片)Nginx 静态文件、Squid
分布式缓存独立缓存集群动态数据查询Redis、Memcached
本地缓存应用进程内极端热点数据(明星微博、首页推荐)Guava Cache、Caffeine、HashMap

本地缓存示例

// 使用 Guava Cache 缓存首页推荐商品(30s 刷新)
LoadingCache<String, List<Product>> cache = CacheBuilder.newBuilder()
    .maximumSize(1000)
    .refreshAfterWrite(30, TimeUnit.SECONDS)
    .build(new CacheLoader<String, List<Product>>() {
        @Override
        public List<Product> load(String key) throws Exception {
            return productService.loadRecommended(); // 从数据库加载
        }
    });

缓存的不足

问题说明
读多写少限制写多场景命中率低,缓存意义不大
数据不一致风险更新 DB 成功但更新缓存失败 → 脏数据
内存容量限制必须设置 TTL,避免内存溢出
增加运维复杂度多了一个需要排查的组件

缓存命中率是最重要的监控指标,热点数据命中率应 > 95%。

数据库扩展

读写分离

原理:利用主从复制,主库处理写请求,从库处理读请求。

graph LR
    APP["应用层"] -->|"写"| M["主库 Master"]
    APP -->|"读"| S1["从库 Slave 1"]
    APP -->|"读"| S2["从库 Slave 2"]
    M -->|"主从复制"| S1
    M -->|"主从复制"| S2

适用场景:读多写少(大多数 Web 系统),读写比 10:1 以上效果显著。

分库分表

触发条件:单表数据量超过千万级别,索引无法全量缓存,查询性能下降。

两种拆分方式

方式思路解决的问题
垂直拆分(分库)按业务模块拆分到不同数据库故障隔离、减少单库压力
水平拆分(分表)按字段值将数据分散到多库多表突破单表数据量瓶颈

水平拆分的两种规则

1. 哈希拆分(适合实体表)
   分库索引 = hash(userId) % 16
   分表索引 = hash(userId) % 64
   优点:数据分布均匀
   缺点:范围查询困难

2. 区间拆分(适合时序数据)
   按月份建表:order_2024_01、order_2024_02
   优点:范围查询高效
   缺点:存在热点(最新月份访问量最大)

分库分表引入的问题

  • 所有查询必须带分区键,否则需要全库全表扫描
  • 跨库 JOIN 不再可用,需在业务层拼接
  • COUNT 等聚合操作需要额外存储(如 Redis 计数)

实践原则

  1. 性能没有瓶颈就不做
  2. 一次到位(如 16 库 × 64 表),避免多次迁移
  3. 考虑 HBase / MongoDB 等原生支持 auto sharding 的 NoSQL

消息队列削峰填谷

三大核心作用

graph TD
    MQ["消息队列"] --> A["削峰填谷<br/>瞬时高峰写流量缓冲到队列<br/>由有限消费者平稳处理"]
    MQ --> B["异步处理<br/>主流程同步<br/>次要流程异步(发优惠券/积分)"]
    MQ --> C["解耦合<br/>生产者不直接依赖消费者<br/>消费者可独立扩展"]

秒杀场景下的削峰

问题:秒杀开始瞬间,1 万个数据库写请求同时到达,数据库濒临崩溃。

解决方案

用户下单请求 → 写入消息队列 → 返回"排队中"
                    ↓
         有限个消费者线程(如 10 个)
                    ↓
         依次处理:校验库存 → 扣减 → 生成订单
                    ↓
         通知用户秒杀结果

容量规划

商品数量:1000 件
单次处理时间:500ms
部署 10 个消费者 → 总处理时间:50s(用户等待上限)

使用消息队列需考虑的问题

问题处理思路
消息丢失生产者确认 + 消费者 ACK + 持久化
消息重复消费消费端幂等设计(见稳定性篇)
消息延迟过高增加消费者数量;监控队列堆积量
消息顺序性同一业务 key 路由到同一分区

CDN 静态资源加速

原理:将静态资源(图片、JS、CSS、视频)分发到全球各地的 CDN 节点,用户就近访问,减少源站压力和网络延迟。

graph LR
    U1["北京用户"] --> CDN_BJ["北京 CDN 节点"]
    U2["上海用户"] --> CDN_SH["上海 CDN 节点"]
    CDN_BJ -->|"缓存未命中"| ORIGIN["源站服务器"]
    CDN_SH -->|"缓存未命中"| ORIGIN

适用内容

  • 图片、视频、音频等媒体文件
  • JS、CSS 等前端静态资源
  • 下载类文件

不适用内容:动态接口(需要实时计算的 API)

连接池与线程池

池化技术的本质

核心思想:空间换时间。预先创建好对象,避免频繁创建/销毁的开销,同时对对象进行统一管理。

适用条件:对象创建耗时(如 MySQL 连接建立需要 TCP 三次握手 + 认证,约 4ms)且会被频繁使用。

数据库连接池

连接获取流程:
1. 当前连接数 < 最小连接数 → 创建新连接
2. 有空闲连接 → 复用空闲连接
3. 无空闲连接 && 当前连接数 < 最大连接数 → 创建新连接
4. 当前连接数 = 最大连接数 → 等待(checkoutTimeout)
5. 等待超时 → 抛出错误

关键配置(经验值):

  • 最小连接数:10
  • 最大连接数:20~30

连接有效性维护

  • 定期发送 SELECT 1 探活(推荐,C3P0 方式)
  • 获取时校验(testOnBorrow,有额外开销,生产环境慎用)

线程池

JDK ThreadPoolExecutor 的特点:优先入队而非创建线程,适合 CPU 密集型任务。

Web 系统(IO 密集型)的改造思路(参考 Tomcat):

超过 coreThreadCount 时,优先创建线程(而非入队)
直到达到 maxThreadCount,再开始入队
这样可以在 IO 等待时充分利用 CPU

关键注意事项

  • 监控队列堆积量(重要告警指标)
  • 禁止使用无界队列(会导致内存耗尽触发 Full GC)
  • 核心线程数需要预热(系统启动时预先初始化)
// 合理的线程池配置示例
ThreadPoolExecutor executor = new ThreadPoolExecutor(
    10,                                  // corePoolSize
    30,                                  // maximumPoolSize
    60L, TimeUnit.SECONDS,               // keepAliveTime
    new LinkedBlockingQueue<>(1000),     // 有界队列!
    new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用方执行
);

系统演进路径

高并发系统的演进应该是循序渐进,以解决系统中存在的问题为驱动力的。

flowchart TD
    A["单体架构<br/>1台 Web + 1台 DB"] -->|"DB 查询慢"| B["连接池 + 索引优化"]
    B -->|"读流量大"| C["主从读写分离"]
    C -->|"数据量大"| D["分库分表"]
    D -->|"写流量峰值"| E["消息队列削峰"]
    E -->|"静态资源慢"| F["CDN 加速"]
    F -->|"服务耦合严重"| G["服务化拆分<br/>(微服务)"]
    G -->|"服务治理复杂"| H["服务网格<br/>Service Mesh"]
    style A fill:#fcc,stroke:#c00
    style H fill:#cfc,stroke:#060

淘宝演进参考:购买现成系统 → DB 引擎升级 → 分库分表 → 缓存层 → 中间件研发 → "五彩石"服务化 → 支撑亿级 QPS。

核心原则

  • 最简单的设计满足当前需求
  • 发现瓶颈再优化,而不是预先过度设计
  • 引入新组件前评估团队的运维能力

存储架构的演进(许式伟视角)

许式伟从操作系统和基础平台的角度,提供了一套理解存储架构的底层视角。

存储层次与访问特性

寄存器(ns级)→ L1/L2 Cache(ns级)→ 内存(100ns级)
→ SSD(100μs级)→ 机械硬盘(10ms级)→ 网络存储(ms~s级)

每跨一个层次,访问延迟约上升 10-100 倍。架构设计的本质之一,就是把热数据尽量推向更快的存储层次

外存管理的架构含义

文件系统是"自描述"的数据格式——它把存储设备中的数据组织成一棵树,让数据可以被随时查看和访问。这个设计思想直接影响了现代存储系统:

存储类型特点典型场景
关系型数据库B+Tree 索引,支持复杂查询事务性业务数据
时序数据库LSM-Tree,写优化,TTL 自动清理监控指标、日志数据
对象存储扁平命名空间,RESTful API大文件、媒体资源
KV 存储极低延迟,简单数据模型缓存、会话、计数器

对象存储与 CDN 的关系

对象存储(如 S3、七牛云)使用 HTTP RESTful API,天然与 CDN 配合:

用户请求 → CDN 边缘节点(命中缓存,就近返回)
                ↓ 缓存未命中
          回源到对象存储(拉取并缓存)

为什么对象存储用 HTTP:HTTP 协议的开放性(可扩展的 Header、规范的 CURD 语义、基于域名的路由)使它成为最通用的应用层协议,CDN 节点天然支持 HTTP 缓存语义(ETag、Cache-Control)。

过载保护与容量规划(许式伟视角)

过载的成因与雪崩效应

过载的四大成因:

  1. 用户增长超预期:资源规划没跟上
  2. 部分资源下线:双机房容灾需要 2 倍资源储备,否则一机房下线就会过载
  3. 数据库随时间劣化:数据量增大导致延迟上升、并发下降
  4. 重试放大:失败重试 2 次 × 多层调用 = 请求数放大 9~81 倍

雪崩效应的正反馈循环

超出容量 → 请求失败 → 重试放大请求数 → 更多失败 → 互备节点也被压垮 → 服务全挂

过载应对策略

服务端

  • 主动拒绝请求(快速失败,而非让请求排队等死)
  • 基于 CPU 使用率而非 QPS 来判断过载(更稳定可靠)
  • 优雅降级:按请求重要性分级(可丢弃→可延后→重要→非常重要)

客户端

  • 限制重试次数(通常不超过 2 次)
  • 指数退避 + 随机抖动(防止重试风暴)
  • 自适应节流:当 requests > K × accepts 时,客户端开始本地丢弃请求(K=2 时服务端过载仍保持 50% 处理率)

权衡法则:成本、效率、可靠性三角

郭东白在实战中总结了一个反直觉但极其重要的权衡框架:在高速增长期,挣钱比省钱更重要;在成熟期,省钱才值得投入

性能优化的商业价值量化

郭东白在 AliExpress 的性能优化案例展示了如何把技术工作归因到商业价值:

关键洞察:不是优化所有页面,而是找到性能损耗 / 优化成本比值最大的页面,依次优化。

性能损耗(Lpage) = 因加载慢而损失的转化率
预期订单增量 = f(Lpage, 流量, 转化漏斗)
优化 ROI = 预期订单增量 / 研发人日成本

结果:6 人兼职半年,全站订单数提升 10.5%,相关专利 13 项。

通用方法论

  • 把所有技术优化动作归因到同一个业务指标(如订单数)
  • 按 ROI 排序,只做回报最大的优先项
  • 发现性能优化回报不够大时,立即切换赛道(转向监控、分析等)

三角权衡的本质

graph TD
    A["成本<br/>(人力/机器/时间)"] --- B["效率<br/>(开发速度/迭代速度)"]
    B --- C["可靠性<br/>(稳定性/数据一致性)"]
    C --- A
    style A fill:#fcc
    style B fill:#cfc
    style C fill:#ccf

不同阶段的权重

阶段优先级
创业/高增长期效率 > 可靠性 > 成本
成熟期可靠性 > 成本 > 效率
衰退期成本 > 可靠性 > 效率

做架构和做业务一样,都不能靠饱和攻击取胜,而是靠对阶段性精确目标的最大化投入来取得进步。

分层高并发设计(李智慧视角)

李智慧在《高并发架构实战课》中,将高并发系统的设计按技术层次分为四层,每一层都有对应的高并发策略。

分层架构总览

flowchart TD
    A["前端层<br/>(浏览器/客户端)"] --> B["网关层<br/>(API 网关/Nginx)"]
    B --> C["服务层<br/>(微服务集群)"]
    C --> D["数据层<br/>(数据库/缓存/消息队列)"]
    style A fill:#e3f2fd
    style B fill:#e8f5e9
    style C fill:#fff3e0
    style D fill:#fce4ec

高并发系统分层架构

前端层高并发策略

目标:减少请求数量,降低服务端压力。

策略说明效果
静态资源 CDNJS/CSS/图片就近分发消除 90%+ 静态资源请求
资源合并多个 JS/CSS 合并为一个文件减少 TCP 连接数
浏览器缓存设置 Cache-Control / ETag重复访问无需请求服务器
懒加载图片/组件按需加载减少首屏请求数
本地存储localStorage 缓存不变数据减少重复 API 调用

网关层高并发策略

目标:流量过滤与路由,保护后端服务。

策略说明
全局限流令牌桶/漏桶,控制整体 QPS 上限
认证鉴权在网关层统一处理,减少后端服务负担
黑名单过滤拦截已知恶意 IP/用户
负载均衡将流量均匀分发到后端服务实例
协议转换HTTP → gRPC,减少内部通信开销

服务层高并发策略

目标:提升单服务处理能力,实现水平扩展。

策略说明适用场景
无状态化Session 外置到 Redis,服务可随意扩容所有 Web 服务
异步处理非核心逻辑通过 MQ 异步处理发送通知、更新统计
本地缓存热点数据缓存在进程内(Caffeine)极热点数据(首页推荐)
线程池隔离不同业务使用独立线程池,防止相互影响微服务多依赖场景
服务降级非核心依赖故障时返回兜底数据推荐、评论等非核心功能

数据层高并发策略

目标:突破数据库单点瓶颈,支撑高并发读写。

策略解决的问题引入的复杂性
读写分离读多写少场景,从库承担读压力主从延迟导致读到旧数据
分库分表单表数据量超千万,查询性能下降跨库 JOIN 困难,需要分区键
多级缓存数据库并发上限低(千级 TPS)缓存一致性问题
消息队列写入峰值超过数据库处理能力最终一致性,延迟增加
NoSQL关系型数据库无法满足特定场景功能有限,无事务

高并发三板斧的系统化讲解(李智慧视角)

高并发 6 大核心设计模式

三板斧的本质

李智慧将高并发系统设计归纳为三个核心手段:

graph TD
    A["高并发系统"] --> B["缓存<br/>减少计算,提升读速度"]
    A --> C["异步<br/>削峰解耦,提升写吞吐"]
    A --> D["水平扩展<br/>突破单机极限"]
    style B fill:#cfc,stroke:#060
    style C fill:#cfc,stroke:#060
    style D fill:#cfc,stroke:#060

缓存:不只是 Redis

缓存的层次与选型

层次技术延迟容量适用数据
CPU Cache硬件自动1-10nsKB 级热点变量(JIT 自动优化)
进程内缓存Caffeine / Guava< 0.1msMB-GB 级极热点数据,进程内共享
分布式缓存Redis / Memcached1-5msGB-TB 级动态数据,跨实例共享
CDN 缓存云厂商 CDN5-50ms无限静态资源,不变数据

缓存的核心权衡

  • 一致性 vs 性能:缓存 TTL 越短,一致性越好,但缓存效果越差
  • 内存 vs 命中率:缓存越大,命中率越高,但内存成本越高
  • 复杂度 vs 收益:多级缓存命中率高,但维护复杂度高

缓存的适用条件(并非所有数据都适合缓存)

适合缓存:
  ✅ 读多写少(读写比 > 10:1)
  ✅ 数据相对稳定(TTL 内变化少)
  ✅ 计算代价高(复杂查询、聚合计算)
  ✅ 访问有热点(20% 数据承受 80% 访问)

不适合缓存:
  ❌ 写多读少(缓存命中率低,维护成本高)
  ❌ 强一致性要求(如账户余额)
  ❌ 数据量极大且访问均匀(无热点)

异步:不只是消息队列

异步的三种形式

形式原理适用场景
消息队列异步生产者发消息,消费者异步处理削峰、解耦、跨系统
线程池异步主线程提交任务,线程池异步执行进程内异步,减少响应时间
响应式编程非阻塞 IO + 事件驱动高并发 IO 密集型场景

异步的代价

  • 最终一致性:异步处理完成前,数据可能不一致
  • 错误处理复杂:异步失败需要补偿机制(重试/幂等/死信队列)
  • 调试困难:异步链路追踪比同步复杂

水平扩展:不只是加机器

水平扩展的前提:服务必须是无状态的,或状态可以分片。

扩展的三个维度(AKF 扩展立方体):

X 轴扩展:水平复制(加机器)
  → 解决:单机容量不足
  → 局限:所有实例处理相同数据,DB 仍是瓶颈

Y 轴扩展:功能分解(微服务拆分)
  → 解决:单体应用复杂度高,无法独立扩展
  → 局限:引入分布式复杂性

Z 轴扩展:数据分片(分库分表)
  → 解决:数据量超过单机极限
  → 局限:跨分片查询困难

实践建议:优先 Y 轴(服务拆分),再做 X 轴(水平扩容),必要时做 Z 轴(数据分片)。

参考资料

  • 《高并发系统设计 40 问》— 唐扬(极客时间专栏)
  • 《从0开始学架构》— 李运华(极客时间专栏)
  • 《数据密集型应用系统设计》(DDIA)— Martin Kleppmann(O'Reilly)
  • 周志明《凤凰架构》— 极客时间专栏
  • 郭东白《架构课》— 极客时间专栏
  • 许式伟《架构课》— 极客时间专栏
  • 李智慧《高并发架构实战课》— 极客时间专栏
← 返回列表

评论 (0)

暂无评论,来留下第一条吧。
登录注册 后才能发表评论