Skip to content

第 1 讲:高并发系统基础认知 -- 指标、瓶颈与架构演进

核心结论(7 条必记)

  1. 高并发核心指标 -- QPS(每秒查询数)、TPS(每秒事务数)、RT(响应时间)、并发数 = QPS x RT
  2. 不要只看平均值要看 P99 -- 平均值被极端值拉高掩盖真实体验,大厂通常要求 P99 < 200ms
  3. 系统瓶颈五大来源 -- CPU、内存、磁盘 IO、网络 IO、数据库,数据库是最常见瓶颈
  4. 架构演进是渐进的 -- 单机 -> 集群 -> 缓存+读写分离 -> 分库分表 -> 微服务 -> 多活
  5. 单机优化优先于分布式 -- 先做好索引优化、SQL 优化、本地缓存,再考虑分布式
  6. 容量评估要考虑峰值和安全系数 -- 按峰值 2-3 倍设计,至少 50% 冗余
  7. 每个技术引入都有时机 -- 缓存解决读瓶颈、MQ 解决写削峰、分库分表解决数据量瓶颈

一、高并发系统的度量指标

1. QPS vs TPS vs RT

指标全称含义典型场景
QPSQueries Per Second每秒查询数,衡量系统吞吐首页加载、搜索请求
TPSTransactions Per Second每秒事务数,一次完整业务流程下单、支付、转账
RTResponse Time响应时间,一次请求从发出到收到响应所有请求

关键区别

  • QPS 是"查询",一次 HTTP 请求就是一个 QPS
  • TPS 是"事务",一次下单可能包含多次请求(查库存 -> 创建订单 -> 扣库存 -> 支付)
  • 一般情况下 QPS >= TPS,一个事务可能包含多个查询

2. 并发数公式

并发数 = QPS x RT

举例:

  • 系统QPS = 1000,平均RT = 0.1s
  • 并发数 = 1000 x 0.1 = 100
  • 意味着任意时刻系统中有约 100 个请求正在处理

这个公式是容量规划的基础,记住它。

3. P95 / P99 的意义

指标含义价值
平均值所有请求的算术平均容易被极端值拉高,参考价值有限
P50(中位数)50% 的请求在此值以下反映大多数用户体验
P9595% 的请求在此值以下反映绝大多数用户体验
P9999% 的请求在此值以下反映几乎全部用户体验

为什么看 P99 不看平均值:

  • 100 个请求,99 个 10ms,1 个 10s -> 平均值 109ms,看起来还行
  • 但有 1% 的用户体验极差,用户直接流失
  • 大厂通常要求 P99 < 200ms,P999 < 1s

4. 三个"并发"的区别

概念含义说明
并发用户数同时在线的用户数1万在线用户不一定都在发请求
并发连接数同时建立的 TCP 连接数一个页面可能开 6 个连接
并发请求数同时正在处理的请求数最有意义的指标,即"并发数"

经验比例

  • 并发请求数 / 并发用户数 约 1:10 ~ 1:100
  • 1 万在线用户,实际并发请求可能只有 100-1000

5. 可用性(几个 9)

可用性年停机时间典型系统
99%3.65 天内部工具
99.9%8.76 小时一般互联网应用
99.99%52.6 分钟核心业务(支付、订单)
99.999%5.26 分钟运营商级别
99.9999%31.5 秒理论值

可用性每提升一个 9,成本指数级增长,业务要根据自身要求选择目标。


二、系统瓶颈定位

1. CPU 瓶颈

表现:load average 持续高于 CPU 核数,us% > 80%

常见原因

  • 计算密集型任务(加密、压缩、排序)
  • 频繁 GC(Full GC 导致 CPU 飙升)
  • 正则匹配、复杂算法
  • 死循环、线程竞争

排查命令

bash
# CPU 使用情况
top -Hp <pid>          # 查看进程内各线程 CPU 占用
vmstat 1 10            # 查看 CPU 上下文切换
mpstat -P ALL 1 5      # 每核 CPU 使用率
pidstat -p <pid> 1 5   # 进程级 CPU 使用

优化方向

  • 算法优化、减少不必要的计算
  • 异步化、并行化
  • JVM 调优减少 GC
  • 增加机器(垂直/水平扩展)

2. 内存瓶颈

表现:OOM、频繁 Full GC、swap 使用率高

常见原因

  • 内存泄漏(集合未清理、ThreadLocal 未 remove)
  • 大对象(一次性加载大量数据)
  • 缓存无限增长(无淘汰策略)
  • 线程池过大(每个线程有栈空间开销)

排查命令

bash
# 内存使用
free -h                        # 系统内存概况
jmap -heap <pid>               # JVM 堆内存分布
jmap -histo <pid> | head -20   # 对象数量排行
jstat -gcutil <pid> 1000 10    # GC 统计

优化方向

  • 分页查询、流式处理,避免一次性加载
  • 缓存设置上限和淘汰策略
  • 修复内存泄漏
  • 合理设置 JVM 参数(-Xms、-Xmx)

3. IO 瓶颈

磁盘 IO

表现:iowait% 高,应用响应慢但 CPU 使用率不高

常见原因

  • 大量随机读写(数据库)
  • 日志写入过多
  • 文件上传下载
  • 数据库 buffer pool 命中率低

排查命令

bash
iostat -x 1 10         # 磁盘 IO 统计
iotop                  # 进程级 IO 排行
df -h                  # 磁盘空间

网络 IO

表现:带宽打满、连接超时、丢包

常见原因

  • 大量小包传输
  • 序列化/反序列化开销
  • 缺少压缩
  • 连接数过多

排查命令

bash
sar -n DEV 1 10        # 网卡流量
netstat -s             # 网络统计
ss -s                  # 连接数汇总
tcpdump                # 抓包分析

优化方向

  • 数据库读写分离减少磁盘压力
  • 批量操作减少 IO 次数
  • 数据压缩减少网络传输
  • 使用更高效的序列化协议

4. 数据库瓶颈(最常见)

表现:慢查询日志暴增、连接池耗尽、锁等待超时

常见原因

  • 慢查询(缺索引、全表扫描、索引失效)
  • 锁等待(行锁、表锁、死锁)
  • 连接数打满
  • 大事务
  • buffer pool 不够

排查命令

sql
-- MySQL 慢查询
SHOW PROCESSLIST;                    -- 当前连接状态
SHOW ENGINE INNODB STATUS;           -- InnoDB 状态(含锁信息)
SELECT * FROM information_schema.INNODB_LOCKS;       -- 当前锁
SELECT * FROM information_schema.INNODB_LOCK_WAITS;  -- 锁等待
bash
# 慢查询日志分析
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
pt-query-digest /var/log/mysql/slow.log

优化方向

  • 索引优化(最优先)
  • SQL 优化(避免 SELECT *、子查询改 JOIN、分页优化)
  • 读写分离
  • 引入缓存
  • 分库分表

5. 应用层瓶颈

常见原因

  • 线程池配置不合理(过小则排队,过大则资源竞争)
  • 同步阻塞调用(远程调用无超时)
  • 连接池耗尽
  • 序列化开销
  • 单点问题

排查方式

bash
# 线程状态
jstack <pid> | grep -A 5 "BLOCKED\|WAITING"  # 查看阻塞线程

优化方向

  • 合理配置线程池(核心线程数、队列、拒绝策略)
  • 所有远程调用设置超时
  • 异步化(CompletableFuture、响应式编程)
  • 连接池参数调优

三、从单机到分布式的架构演进

阶段 1:单机应用(QPS < 1000)

+-------------------+
|   应用 + 数据库    |
|   (一台服务器)     |
+-------------------+

特点

  • 应用和数据库在同一台机器
  • 架构简单,维护成本低
  • 单点故障风险

瓶颈

  • CPU、内存、磁盘 IO 互相争抢
  • 无法水平扩展

演进时机:QPS 接近 1000,或需要高可用

阶段 2:应用集群化(QPS 1000 - 10000)

                    +----------+
               +--->|  应用 A   |
               |    +----------+
+--------+     |    +----------+
| Nginx  |-----+--->|  应用 B   |
+--------+     |    +----------+
               |    +----------+
               +--->|  应用 C   |
                    +----------+
                          |
                    +----------+
                    |  数据库   |
                    +----------+

引入技术

  • Nginx / HAProxy 负载均衡
  • Session 共享(Redis / Token)
  • 应用无状态化

瓶颈

  • 数据库成为单点(连接数、读写压力)
  • 单库数据量增长

演进时机:数据库成为瓶颈,读写比例高

阶段 3:缓存 + 读写分离(QPS 10000 - 50000)

                    +----------+
               +--->|  应用 A   |---+
               |    +----------+   |
+--------+     |    +----------+   |    +---------+
| Nginx  |-----+--->|  应用 B   |---+--->|  Redis  |
+--------+     |    +----------+   |    +---------+
               |    +----------+   |
               +--->|  应用 C   |---+
                    +----------+   |
                                   |    +----------+
                                   +--->|  主库(写)  |
                                        +----------+
                                             |
                                        +----------+
                                        |  从库(读)  |
                                        +----------+

引入技术

  • Redis / Memcached 缓存
  • MySQL 主从复制 + 读写分离
  • CDN(静态资源)

瓶颈

  • 缓存穿透/击穿/雪崩风险
  • 主库写压力、单表数据量过大
  • 从库延迟

演进时机:单表数据量超 2000 万、单库 QPS 超 5000

阶段 4:分库分表 + Redis 集群(QPS 50000 - 100000)

                    +----------+
               +--->|  应用 A   |---+
               |    +----------+   |
+--------+     |    +----------+   |    +------------------+
| Nginx  |-----+--->|  应用 B   |---+--->|  Redis Cluster   |
+--------+     |    +----------+   |    +------------------+
               |    +----------+   |
               +--->|  应用 C   |---+
                    +----------+   |
                                   |    +---------+  +---------+
                                   +--->|  分库1   |  |  分库2   |
                                        +---------+  +---------+
                                        |  分表1   |  |  分表1   |
                                        |  分表2   |  |  分表2   |
                                        +---------+  +---------+

引入技术

  • ShardingSphere / MyCat 分库分表中间件
  • Redis Cluster
  • 数据库中间件(路由、聚合)

瓶颈

  • 分布式事务
  • 跨库查询困难
  • 运维复杂度陡增
  • 单个服务过于庞大

演进时机:代码量 > 10 万行、团队 > 20 人、服务之间耦合严重

阶段 5:微服务 + MQ + 服务治理(QPS 100000+)

+------+   +----------+   +----------+   +----------+
| 网关  |-->|  用户服务  |   |  订单服务  |   |  商品服务  |
+------+   +----------+   +----------+   +----------+
                              |    ^            |
                              v    |            v
                         +---------+     +---------+
                         |   MQ    |     |  缓存    |
                         +---------+     +---------+
                              |                |
                              v                v
                         +----------+    +----------+
                         | 库存服务  |    |  各自DB   |
                         +----------+    +----------+

              +--------------------------------------+
              |       服务治理(注册/配置/限流/熔断)    |
              +--------------------------------------+

引入技术

  • Spring Cloud / Dubbo 微服务框架
  • Kafka / RocketMQ 消息队列
  • 服务注册发现(Nacos / Eureka)
  • 熔断限流(Sentinel / Hystrix)
  • 配置中心、链路追踪、API 网关

瓶颈

  • 服务间调用延迟增加
  • 分布式一致性问题
  • 运维成本极高

演进时机:需要 99.99%+ 可用性、异地容灾

阶段 6:同城双活 / 异地多活

+-- 北京机房 --+          +-- 上海机房 --+
|              |          |              |
|  应用集群 A   |<------->|  应用集群 B   |
|              |  同步复制  |              |
|  数据库 A    |<-------->|  数据库 B    |
|              |          |              |
+--------------+          +--------------+

引入技术

  • 数据同步(Otter / Canal + MQ)
  • 全局唯一 ID(雪花算法)
  • 异地流量调度
  • 单元化架构

什么时候做

  • 业务可用性要求 > 99.99%
  • 核心金融/交易业务
  • 法规合规要求

架构演进不是越先进越好,而是刚好够用就好。过早引入复杂方案是灾难。


四、什么时候引入什么技术

引入缓存的时机

条件说明
读远多于写读写比 > 10:1
数据变化不频繁商品信息、配置信息、字典数据
重复查询多热点数据被大量请求访问
可接受短暂不一致缓存更新有延迟

不适合缓存的场景:实时性要求极高(库存扣减)、写多读少、数据量小且查询简单

引入 MQ 的时机

条件说明
写入峰值明显秒杀、抢购等瞬时高并发写入
系统解耦需求A 系统写完数据,B/C/D 系统都要感知
异步处理发短信、发邮件、日志处理
流量削峰瞬时流量远超系统处理能力

不适合 MQ 的场景:实时性要求高(必须同步返回结果)、系统简单不需要解耦

分库分表的时机

条件具体数值
单表数据量> 2000 万行(或单表文件 > 20GB)
单库 QPS> 5000
单表字段过多> 50 个字段考虑垂直拆分
索引效率下降即使有索引查询也明显变慢

分库分表前先做:索引优化、SQL 优化、读写分离、缓存 -- 这些成本远低于分库分表

微服务的时机

条件具体数值
代码量> 10 万行,单项目编译部署困难
团队规模> 20 人,多团队协作
独立扩展需求不同模块负载差异大(商品浏览 vs 订单创建)
故障隔离需求一个模块挂了不能影响其他

过早微服务的代价:运维复杂度、调试困难、分布式事务、团队沟通成本 -- 小团队不要碰

多活的时机

条件说明
可用性要求> 99.99%(年停机 < 53 分钟)
容灾需求机房级故障不能影响业务
合规要求金融行业监管要求
用户分布全国/全球用户,就近接入

五、容量评估方法

1. 基于 QPS 的机器估算

机器数 = (总 QPS x 安全系数) / 单机 QPS
参数说明
总 QPS系统需要承载的峰值 QPS
安全系数通常取 2-3,保证冗余
单机 QPS单台机器能稳定承载的 QPS

举例:

  • 总 QPS = 10000,安全系数 = 2,单机 QPS = 2000
  • 机器数 = 10000 x 2 / 2000 = 10 台

2. 基于用户数的 QPS 估算

峰值 QPS = (DAU x 人均请求数) / 86400 x 峰值系数
参数说明
DAU日活跃用户数
人均请求数每个活跃用户每天产生的请求数
86400一天的秒数
峰值系数通常取 3-5(早晚高峰集中访问)

举例:

  • DAU = 100 万,人均 50 次请求/天,峰值系数 = 5
  • 峰值 QPS = (1000000 x 50) / 86400 x 5 = 2894 QPS

3. 基于并发数和 RT

QPS = 并发数 / RT
  • 100 并发,RT = 50ms -> QPS = 100 / 0.05 = 2000
  • 100 并发,RT = 200ms -> QPS = 100 / 0.2 = 500

RT 越短,同样并发数下的 QPS 越高,所以优化 RT 是提升吞吐的关键。

4. 单机性能参考数据

组件单机 QPS 参考值说明
MySQL(简单查询)2000-5000有索引的单表查询
MySQL(复杂查询)200-500多表 JOIN、聚合
Redis(GET/SET)50000-100000简单 KV 操作
Redis(复杂操作)10000-30000SORT、LRANGE 等
Kafka(单分区)10000-50000顺序写入
Nginx50000-100000静态资源/反向代理
Tomcat(普通接口)1000-3000业务逻辑 + 数据库访问
Spring Boot(空接口)5000-10000无业务逻辑

以上为经验参考值,实际性能受硬件、数据量、业务逻辑复杂度影响,需压测确认。


六、面试高频题

Q1:你们系统的 QPS 是多少?怎么评估的?

DAU 统计 -> 计算日均请求总量 -> 除以 86400 得平均 QPS -> 乘以峰值系数(3-5)得峰值 QPS -> 再乘以安全系数(2)做容量规划 -> 压测验证单机 QPS -> 算出机器数

Q2:系统性能变慢,你怎么排查?

监控告警确认现象 -> top/vmstat 看 CPU -> free/jstat 看内存 -> iostat 看磁盘 IO -> sar 看网络 -> SHOW PROCESSLIST 看数据库 -> 慢查询日志定位 SQL -> explain 分析执行计划 -> 针对性优化

Q3:为什么看 P99 不看平均值?

平均值被极端值拉高 -> 99个 10ms + 1个 10s = 平均 109ms -> 看起来正常但 1% 用户体验极差 -> P99 反映绝大多数用户的真实体验 -> 大厂要求 P99 < 200ms

Q4:什么时候该做分库分表?

单表 > 2000 万行 -> 索引优化和 SQL 优化已做到极致 -> 读写分离和缓存已经上了 -> 单库 QPS > 5000 或单表文件 > 20GB -> 此时才考虑分库分表 -> 不是上来就分

Q5:如何保证高可用?

消除单点(集群部署) -> 故障自动转移(主从切换) -> 限流降级(保护核心链路) -> 熔断(快速失败不拖垮上游) -> 超时控制(不阻塞线程) -> 监控告警(快速发现) -> 容灾演练(验证预案)

Q6:微服务和单体怎么选?

看团队规模(< 20 人单体优先) -> 看代码量(< 10 万行单体优先) -> 看部署频率(各模块独立发布需求强则微服务) -> 看扩展需求(模块负载差异大则微服务) -> 小团队小项目用单体 -> 逐步拆分不要一步到位


练习题(待完成)

  • [ ] 练习 1:你的系统 DAU = 50 万,人均每天 30 次请求,峰值系数 = 5,安全系数 = 2,单机 QPS = 2000。请计算需要多少台应用服务器?
  • [ ] 练习 2:一个接口 P50 = 20ms,P95 = 50ms,P99 = 2s。请分析问题可能出在哪里,给出排查思路。
  • [ ] 练习 3:电商系统日均订单 100 万,大促期间峰值 QPS 是日常的 10 倍。当前架构是 3 台应用 + 1 主 2 从 MySQL。请给出架构演进方案,说明每个阶段引入什么技术、解决什么问题。

基于 VitePress 构建