性能测试中,TPS上不去一般意味着系统的某一部分达到了处理能力的上限,形成了短板。我们可以从你提到的四个方面,以及一个容易被忽视的客户端方面来分析。
一般建议按照从硬件资源到软件配置,再到代码思路的顺序进行排查。以下是详细的短板清单及排查思路:
1. 硬件和系统资源短板
一般意味着服务器的计算或存储能力已达上限。
CPU 达到饱和:
现象: 不管是应用服务器还是数据库服务器,CPU 使用率不断 95% 以上。
原因: 代码中存在死循环、大量的计算任务、线程上下文切换过于频繁,或者服务器核数确实不够。
排查: 使用 top (Linux) 或任务管理器 (Windows) 查看 CPU 占用。如果是多核 CPU,个别重要满载也可能会成为短板。
内存短板:
现象: TPS 上不去,同时系统大量使用交换分区(Swap)或虚拟内存,导致读写磁盘频繁。
原因: 物理内存不足,导致操作系统频繁进行内存和磁盘的交换(缺页中断),这会极大地拖慢处理速度。也可能存在内存泄漏,导致 Full GC 频繁。
磁盘 I/O 短板:
现象: 磁盘读写等待时间(await)很长,CPU 经常处于等待 I/O 的状态。
原因: 数据库大量写入日志、开启慢查询日志、或系统使用机械硬盘处理随机读写。
排查: 使用 iostat 命令查看 %util 和 await 标准。如果数值过高,可以考虑升级为 SSD 来解决。
2. 数据库短板
数据库一般是TPS上不去最常见的原因。
慢 SQL:
原因: 没有为常用的查询条件建立索引,或者 SQL 语句编写不当(如在索引列上使用了函数),导致查询变成了全表扫描。当数据量大时,全表扫描会严重拖慢性能。
数据库连接池满:
现象: 应用日志中出现 Connection Timeout 或 连接池已满 的报错。
原因: 应用的数据库连接池(如 HikariCP、Druid)最大连接数设置得太小,或者有些连接被占用后没有及时释放(死锁或慢 SQL 导致事务长时间不提交),新请求拿不到连接,只能等待,导致 TPS 无法提升。
锁竞争:
原因: 高并发下,多个事务同时试图修改同一条数据(行锁),或者修改同一张表的结构(表锁),导致大量的请求处于锁等待状态。这会使得 TPS 曲线看起来很平,但数据库负载很高。
数据库服务器自身短板:
原因: 数据库服务器的 CPU、内存或磁盘 I/O 达到了极限,无法更快地处理 SQL 请求。
3. 中间件/容器短板
常见的中间件包括 Nginx、Tomcat、Gateway 等。
Web 容器线程池已满:
现象: TPS 达到某个值后无法增长,响应时间(RT)开始急剧上升。客户端往往收到 503 或 504 错误。
原因: Tomcat 或 Jetty 的 max-threads 设置得太小。当所有线程都在处理慢请求时,新请求只能排队等待。
连接数限制:
现象: Nginx 或 Gateway 日志中出现 connection refused。
原因: Nginx 的 worker_connections 或操作系统的 ulimit -n 打开文件数限制太小,导致无法建立新的 TCP 连接。
超时配置不当:
原因: 中间件和后端服务之间的连接超时或读取超时设置得太短,导致请求被频繁中断重试,反而增加了系统负载,降低了有效 TPS。
4. 代码/应用思路短板
这是最需要结合业务进行分析的方面。
同步调用/串行思路:
原因: 业务思路中存在耗时的同步操作,且没有使用多线程异步处理。如,一个请求需要循环调用 3 次外部 API,且是串行执行,那么这 3 次网络耗时累加起来,会直接成为 TPS 的计算分母。
内存泄漏和 Full GC:
现象: TPS 波动非常大,忽高忽低,配合监控可以看到 GC(垃圾回收)时间很长。
原因: 代码中创建的对象无法被回收,堆内存不断增长。当内存占满时,JVM 会触发 Stop-The-World 的 Full GC 来清理内存,此时所有应用线程都会暂停,导致 TPS 瞬间归零。
超大对象/数据传输:
原因: 一次查询返回几千条数据,导致网络传输(带宽)成为短板,同时也增加了序列化和反序列化的 CPU 开销。
算法效率低:
原因: 某段重点代码的时间复杂度是 O(n²),当数据量变大时,处理时间呈指数级增长,TPS 自然上不去。
5. 网络短板
虽然现在内网带宽一般较高,但网络问题依然存在。
带宽跑满:
现象: 网卡流量达到上限。
原因: 传输的数据包过大,或者下载文件类应用占满带宽。可以使用 iftop 或 nload 命令查看实时流量。
TCP 参数和连接数耗尽:
现象: 客户端端口分配完,出现 Cannot assign requested address 错误。
原因: 高并发压测时,客户端机器在短时间内建立大量连接,由于 TIME_WAIT 状态的存在,导致临时端口被耗尽。
丢包和重传:
原因: 网络设备问题或链路拥塞导致丢包,触发 TCP 重传。重传机制会增加实际传输时间,拉低有效 TPS。可以使用 netstat -s 查看重传率。
6. 资源竞争和锁(拓展)
静态资源冲突:
原因: 多个线程同时争抢写同一个文件,或者使用 synchronized 锁住了一个本应无锁的大范围代码块,导致并发退化成串行。
排查思路
当遇到 TPS 上不去时,可以参考以下顺序来排查:
服务端资源: CPU 是不是已打满?内存是不是够用?磁盘 I/O 是不是繁忙?
中间件监控: Tomcat 线程是不是已满?数据库连接池是不是耗尽?
分析数据库: 打开慢查询日志,看是不是有大量慢 SQL。
深入代码: 结合 Profiler(性能分析工具)找到最耗时的函数调用链。
TPS 上不去,往往不是因为系统不够努力,是因为某个步骤的处理能力达到了上限。