性能压测问题排查思路

一个接口在做性能压测的时候,100并发的时候CPU,响应时间是正常的,但是到200并发的时候CPU飙升到90%并且响应时间从600ms飙升到3s说一下你的排查思路:

1.数据库方面排斥思路

|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 数据库表没有加索引 |
| #### 1. 核心原因:从索引查找到全表扫描的成本爆炸 * 100并发时:内存缓冲还在起作用 * 当没有索引时,数据库执行查询通常需要全表扫描。 * 在压测初期或低并发时,数据库的Buffer Pool(缓冲区)是相对"干净"的。随着100个并发查询的执行,它们需要将表的数据页从磁盘加载到内存中。 * 随着压测的持续,这张表的大部分数据可能已经被加载到了内存里。 * 关键点: 在100并发时,虽然执行的是全表扫描,但因为数据已经在内存中了(逻辑IO),所以CPU只需要处理内存中的数据。内存的读写速度非常快(纳秒级),CPU可以轻松应对100个并发线程对内存数据的遍历。 * 200并发时:物理IO爆发,CPU等待与上下文切换的恶性循环 * 当并发数翻倍到200时,情况发生了质变。 * 内存不够了: 200个线程同时进行全表扫描,意味着每个线程都要遍历整个表。如果表的大小超过了分配给数据库的Buffer Pool大小,内存就会迅速被占满。 * 激烈的淘汰与载入: 内存被占满后,旧的缓存数据必须被淘汰,才能为新查询让路。这会导致大量的物理磁盘读取(物理IO)。 * CPU的困境: CPU的速度远远快于磁盘IO。当200个线程发起海量的物理读请求时,CPU需要花费大量时间来做两件事: 1. 发起IO请求:告诉磁盘控制器去读数据。 2. 上下文切换:由于磁盘读很慢,操作系统会让发起读请求的线程进入"休眠"等待状态,转而执行其他线程。当磁盘数据读回来后,又会产生中断,把等待的线程唤醒。 * 结果: 系统资源被大量消耗在"发起请求-等待-切换-唤醒"这个循环上,真正用于处理业务的CPU时间反而变少了。这就导致了CPU使用率飙升到90%(主要用于管理IO和线程切换),而查询响应时间也因等待磁盘而急剧延长到3秒。 #### 2. 并发本身加剧了全表扫描的负面影响 没有索引时,数据量对查询成本的影响是线性 的,但并发数对系统总成本的影响是指数级的。 * 假设: 一张表有100万行数据,一次全表扫描需要扫描100万行。 * 100并发: 意味着每秒需要处理 100万行/秒 * 100 = 1亿行 的逻辑判断。 * 200并发: 意味着每秒需要处理 100万行/秒 * 200 = 2亿行 的逻辑判断。 CPU需要处理的逻辑行数直接翻倍。这不仅仅是数字翻倍,当内存无法容纳全部数据时,还会叠加前面提到的物理IO等待成本。这就像一条高速公路,100辆车跑100迈很顺畅,但200辆车同时涌上来,即使每辆车都想跑快,也会因为需要频繁地排队、并线(上下文切换)而导致整体速度(响应时间)急剧下降。 #### 3. 排查路径:如何验证你的猜想 如果你怀疑是索引问题,可以通过以下步骤来验证这个"100并发正常,200并发飙升"的现象: 第一步:查看数据库的关键指标 在200并发压测时,登录数据库,执行以下命令(以MySQL为例): sql -- 查看当前的线程状态,是不是很多都在 'Sending data'(代表正在扫描数据) SHOW PROCESSLIST; -- 查看磁盘读写压力指标 SHOW GLOBAL STATUS LIKE '%innodb_data_reads%'; -- 物理读次数 SHOW GLOBAL STATUS LIKE '%innodb_buffer_pool_read_requests%'; -- 总逻辑读次数 -- 计算逻辑读与物理读的比例,如果物理读比例在200并发时急剧升高,说明内存已经无法覆盖工作集 第二步:执行一次成本分析 对怀疑的查询执行EXPLAIN: * 确认 type 列是否为 ALL(全表扫描)。 * 确认 rows 列扫描的行数是否非常大。 第三步:模拟大压力下的单条查询 如果不想重新压测,可以尝试在数据库空闲时,清空缓存 执行一次查询,对比 有缓存 时执行一次查询的耗时差异。 * SELECT SQL_NO_CACHE * FROM your_table WHERE your_column = '某个不存在的值'; (模拟物理读) * SELECT * FROM your_table WHERE your_column = '某个不存在的值'; (如果刚查过,可能走缓存) 如果物理读比逻辑读慢几十倍甚至上百倍,就印证了高并发下物理IO爆发导致CPU飙升的推测。 #### 总结 "100并发正常,200并发CPU飙升"是因为系统从"内存计算"状态被推入了"磁盘IO等待+频繁上下文切换"的"风暴"状态。 * 100并发 :数据在内存中,CPU在计算,效率高。 * 200并发 :内存不够,数据在磁盘上,CPU忙于调度和等待,效率极低,表现为高CPU占用(系统态开销大)和长响应时间。 解决这个问题的核心,就是通过添加合适的索引,将"大海捞针"式的全表扫描(需要加载整个表),转变为"查目录"式的索引查找(只需加载少量索引页和个别数据页),从而大幅降低对内存和磁盘IO的需求,让系统在200并发时依然能保持稳定。 |

|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 数据库连接池配置不合理 |
| ##### 1. 最大连接数(maximumPoolSize)设置过小------导致"请求堆积"与"线程饥饿" * 场景: 连接池最大连接数设置为50。 * 100并发时: * 100个请求同时进来,但只有50个能拿到连接,立即去数据库执行查询。 * 另外50个请求在连接池的等待队列中排队(例如HikariCP的queue)。 * 此时CPU表现: CPU可能正常或略低。因为只有50个线程在真正干活,另外50个在"阻塞等待"。操作系统处理简单的线程阻塞/唤醒,开销不大。 * 200并发时: * 同样只有50个连接可用。 * 150个线程涌入等待队列。如果等待队列超时时间(connectionTimeout)设置较长(比如30秒),这150个线程并不会立刻失败返回,而是全部在应用服务器内部阻塞。 * 此时CPU飙升的原因: 虽然数据库压力没变,但应用服务器的线程调度压力剧增 。操作系统需要频繁管理这150个等待线程的状态(阻塞->唤醒->再阻塞)。当某个连接被释放时,连接池需要从150个候选中选择一个,这涉及锁竞争(Lock Contention)。 * 结果: 大量应用服务器线程处于TIMED_WAITING状态,CPU时间大量消耗在线程上下文切换和锁竞争上,导致CPU使用率飙升。响应时间自然延长到3秒,因为这3秒大部分时间是在排队等待连接。 ##### 2. 连接泄漏------导致"资源耗尽"与"GC压力" * 场景: 代码中没有正确关闭连接(ResultSetStatementConnection未关闭),或者连接池的泄漏检测未开启。 * 100并发时: * 虽然代码有漏洞,但因为并发低,垃圾回收(GC)还能及时回收那些"被遗忘"的连接对象。系统勉强维持平衡。 * 200并发时: * 并发翻倍,连接泄漏的速度翻倍。连接池中的物理连接很快被"僵尸请求"占满,无法归还到池中。 * 为了维持连接数,连接池可能尝试创建新连接,但这又会消耗数据库资源。同时,堆内存中充斥着大量无法被回收的连接对象。 * 此时CPU飙升的原因: 频繁的Full GC(完全垃圾回收)。内存压力增大,GC线程疯狂运行试图回收内存,占用大量CPU。最终导致"GC overhead limit exceeded"错误或应用卡死。 ##### 3. 连接池过大的"反向优化"------导致数据库垮掉 * 场景: 为了应对高并发,将连接池最大连接数设得非常大(如200)。 * 200并发时: * 应用瞬间创建200个物理连接到数据库。 * 此时CPU飙升的原因: 这200个请求同时涌向数据库 。如果数据库每条查询都很重(哪怕是简单的select * from xxx),数据库的CPU会瞬间被打满,导致查询极慢。 * 连锁反应: 应用在等待数据库返回,连接被长期占用,新的请求继续等待连接。整个系统陷入死锁。 * 这一点其实与"没有索引"的结果类似,但根因是连接数配置过多,把数据库压垮了。 |

相关推荐
Gauss松鼠会2 小时前
openGauss数据库源码解析系列文章--openGauss简介(上)
数据库·性能优化·database·opengauss
V1ncent Chen2 小时前
从零学SQL 05 基础查询
数据库·sql·mysql·数据分析
老迟聊架构2 小时前
完全基于对象存储的数据库引擎:SlateDB
数据库·后端·架构
阿蒙Amon2 小时前
C#常用类库-详解CsvHelper
开发语言·数据库·c#
006_2 小时前
Java8的lambda用法总结
前端·数据库
倔强的石头1062 小时前
KWDB 3.1.0 制造业实战:从 0 到 1 搭建工业设备健康监测系统
数据库·kwdb
qq_4924484462 小时前
Maven直接下载jar包
数据库·maven·jar
开始了码2 小时前
基于 Qt 实现多客户端 TCP 通信聊天室
开发语言·数据库·php