性能 - 性能调优的常见手段
学习完整课程请移步 互联网 Java 全栈工程师
常见的互联网架构中,一般都能看到 Spring + MyBatis + MySQL + Redis 搭配的身影。一般来说,应用内部的接口都是直接调用的,所谓的面向接口编程,应用间的调用直接调或者通过类似 Dubbo 之类的服务框架来执行,数据格式往往采用 JSON,即统一也方便各数据间做转换和取值,缓存一般使用 Redis 或 Memcached,存储一些对象或 JSON 格式的字符串。对外提供的接口,一般都需要进行压力测试,以便估算其性能,并为后续的调优提供指导方向,以下接口便是在压测过程中出现的各种“奇怪现象”,所谓奇怪,指的是从表象上看与我们正常的逻辑思路不符,但其本质还是我们对压力下程序的表现出来的特征不熟悉,用惯用的知识结构试图去解释,这根本是行不通的。下文是我在一次全面压测过程后对数据进行的分析汇总,其中的现象是很多压测常见的,里面的分析过程及改进措施我认为有很大的参考意义。具体内容如下:(部分接口为了安全我省略了其名称,但不影响我们的分析,另外形如 1N3T
之类的表示的是 1 台 Nginx,3 台 Tomcat,具体的 TPS 数值只是为了说明优化前后的比照,没有实际意义)
名词解释
TPS
每秒钟处理完的事务次数,一般 TPS 是对整个系统来讲的。一个应用系统 1s 能完成多少事务处理,一个事务在分布式处理中,可能会对应多个请求,对于衡量单个接口服务的处理能力,用 QPS 比较多。
QPS
每秒钟处理完请求的次数;注意这里是处理完。具体是指发出请求到服务器处理完成功返回结果。可以理解在 Server 中有个 Counter,每处理一个请求加 1,1 秒后 Counter = QPS。
RT
响应时间,处理一次请求所需要的平均处理时间
并发量
系统能同时处理的请求数
真实调优案例
接口:获取列表
压测现象:单台 TPS 700 多,应用 CPU 高负载
问题分析
旧框架,平均响应时间长,应用 CPU 高,程序内部有大量的 BEAN 到 MAP 到 JSON 之间的转换,修改数据库连接数后,TPS 没有提升。
改进措施
重构系统,用 MyBatis 替代之前的 DAO 操作,减少 BEAN - MAP - JSON 之间的内部数据转换,减少程序内部的无用操作。
改进效果
TPS 改进后能到 3000 左右,有较大提升,但压测时应用 CPU 几乎跑满,还有改善空间。
压测现象:数据库资源利用率高
问题分析
单台应用,数据库资源 CPU 都能到 50%,10 台 TOMCAT 在 1万 并发下数据库 CPU 跑满,LOAD 值 700 多,但 DB 的 QPS 也不过 11554,并不算多,因此怀疑是 SQL 执行耗费了 CPU,可能是某条 SQL 没有按索引查找或者索引失效。
改进措施
查看 SQL 文件发现如下 sql:select count(1) from orders where order_status_id !=40
,将其改为 select order_id from orders
然后通过程序把 order_status_id != 40
的过滤掉。通过 list.size() 来获取。order_status_id 即使加了索引,由于是 !=
比较,所以不会去按索引查找,导致 CPU 高
改进效果
相同环境下(1台 Nginx,10 台 Tomcat,1000 并发),TPS 由 3000 变成 3727,略有增长,但是 DB 的 CPU 明显下降,变为 30%,效果明显
压测现象:1N15T,TPS 4552;10N15T,TPS 9608
问题分析
后端都是 15 台 Tomcat,前端 1 台 Nginx 时 TPS 为 4552,通过 LVS 挂 10 台 Nginx 时为 9608,增长不明显,其 Nginx 和 Tomcat 都压力不大,集群结果不合理,怀疑是 Nginx 转发配置的问题;
改进措施
未进一步改进:可能是需要调整 Nginx 的参数,之前发现过 Nginx 不同的配置对后端集群环境的 TPS 影响很大
改进效果
无
接口:信息查询
压测现象:单台 TPS 2000 多,应用 CPU 高,DB 的 QPS 15000 左右
问题分析
旧框架,程序内部有很多 Bean - Map - Json 相互的转换
改进措施
删除冗余代码、更换连接池包,使用 MyBatis
改进效果
TPS 由 2000 多增长为 8000 多,DB 的 QPS 为 9000 左右,优化后压测应用的 CPU 占用高,几乎跑满。
压测现象:数据库无压力,应用增加多台后 TPS 不变
问题分析
1N1T 和 1N10T 的 TPS 一样,都为 5000,增大并发时错误数增多,应用 CPU 耗费 70%,DB 无压力,Nginx 单台通过 ss –s
发现端口占满,即 Nginx 到 Tomcat 之间转发的连接端口 time-wait
状态 6 万多。Nginx 存在瓶颈。
改进措施
调优 Nginx 参数,将短连接改为长连接
改进效果
1N3T 的 TPS 能到 17376,Tomat 的 CPU 压力 84%,DB 的 QPS 18000,CPU 69%,应用的资源基本使用到量。
接口:获取详情
压测现象:单台应用 TPS 2600,10 台 Tomcat 才 3700
问题分析
增加应用服务器,TPS 增长不明显,且 Nginx、Tomcat、DB 的负载都不高,说明服务器本身不是瓶颈,考虑是不是网络的问题,通过监控网卡包流量发现网络数据跑满,因为此接口会有大量数据的输出,因此瓶颈在网络上。另外,测试过程中发现 Redis 有报错,Redis 服务器是虚机,可能服务能力有限。
改进措施
开启 Tomcat 的 gzip 压缩。
改进效果
同等并发下(1 台 Nginx,10 台 Tomcat,1000 并发),TPS 由 3727 增长到 10022,增长了近 3 倍,效果显著。
压测现象:1N10T 集群下 Nginx 参数调优对 TPS 提升效果明显
问题分析
经过 Tomcat 的启用 gzip 后,1N10T 下 TPS 为 10022,需进一步提升。
改进措施
优化 Nginx:
- Nginx 日志关闭
- Nginx 进程数量 worker,由
24
改为16
- nginx keepalive 数量由
256
改为2048
改进效果
TPS 由 10022 提升为 13270。
压测现象:1N5T 和 1N10T 的 TPS 相差不大
问题分析
1N10T 的 TPS 为 1万3千多,1N5T 的 TPS 为 1万2千多,相差不大,应用的 Tomcat 资源利用没满,CPU 为 65%,DB 的 QPS 已经到 2万多了,单台服务器 DB 基本上到量了,因此再增加应用也没效果,只会导致响应的时间变长。
改进措施
单台 DB 已经无法改进了,要不提升服务器硬件,要不读写分离。
改进效果
无
接口:促销
压测现象:通过 Redis 存取数据,TPS 才 1000 多,CPU 有压力
问题分析
此接口通过 Redis 取数据,TPS 不高才 1000 多,但 CPU 占用了 80%,说明程序内部有大量序列化反序列化的操作,可能是 JSON 序列化的问题。
改进措施
将 net.sf.json
改成 alibaba
的 fastjson
改进效果
同等并发条件下 TPS 由 1000 多提升为 5000 多,提高了近5倍。
压测现象:参数多时 TPS 下降明显
问题分析
此接口根据参数从 Redis 中获取数据,每个参数与 Redis 交互一次,当一组参数时 TPS 5133,五组参数时 TPS 1169,多次交互影响了处理性能。
改进措施
将从 Redis 获取数据的 get 改为 mget,减少交互次数。
改进效果
五组参数时 1N3T 压测 TPS 9707,据此估算即使是单台 Tomcat,TPS 也能有三四千,性能比单次 get 的调用方式提升了 3,4 倍。
压测现象:1N3T TPS 1万多,在增大 Tomcat 可能 TPS 增长不会明显
问题分析
此处说的是可能,因为 Nginx 服务器的 CPU 虽然不高,但 QPS 已经 8000 多,此时应该是 Nginx 的服务器网络流量成为了瓶颈。(只是猜测)
改进措施
可以增加多台 Nginx 负载,前端加 LVS
接口:追踪接口
压测现象:1N10T 的 TPS 低于 1N3T 的 TPS
问题分析
1N3T 在 2000 并发下 TPS 为 9849,此时 DB 的 QPS 为 90000,CPU 80%,将 Tomcat 增到 10 台,5000 并发下,TPS 为 7813,DB 的 QPS 为 19000,CPU 75%,load 1,说明压力增大情况下 DB 的压力反而下来了,注意到 Nginx 服务器的网卡流量达到 885M,说明是压力过大情况下,网络满了,发生丢包,导致 DB 端压力反而下来了。
改进措施
注意压测情况下部分接口由于数据量传输较大,会出现网络瓶颈。
接口:回填接口
压测现象:TPS 不到 500,DB 的 QPS 3500
问题分析
虽然缺少应用的 CPU 及 DB 的 CPU 利用率数据,较低的 TPS 应该是应用的瓶颈,且需要关注是不是 DB 在处理查询的时候缓慢。
改进措施
- 连接池由
DBCP
改为HIKAR
- 减少了日志打印输出
- SQL 优化,将部分条件过滤改为在 Java 代码中执行
改进效果
TPS 由不到 500 增长为 1300 多
接口:券查询
压测现象:集群结果与单台应用结果相比不合理
问题分析
查看是否存在瓶颈资源,可以看到 5 台 Tomcat 集群下,TPS 为 9952,但 DB 的 QPS 为 5-6 万,CPU 利用率为 37%,说明对数据库进行了大量的主键或索引查询,一般单台 DB 的 QPS 也就 4万左右,再增加应用的集群数量,对 TPS 也不会有太大影响。
改进措施
可以考虑分库
接口:推荐
压测现象:Nginx 长短连接差异
问题分析
18 台 Nginx,2 Tomcat 时 TPS 8100,此时应用服务器的端口数满,一般来说,Nginx 短连接在高并发下容易导致端口占满的问题。
改进措施
将 Nginx 改为长连接
改进效果
TPS 增长为 10733,TPS 稳定,起伏减少,但是 CPU 耗尽。说明 CPU 打满了,此时如果还要优化就的进行代码调优了。
接口:查询
压测现象:18N20T 的 TPS 才 6842
问题分析
18 台 Nginx,20 台 Tomcat,TPS 才 6842,此时应用 CPU 利用率 85%,说明 CPU 存在瓶颈,但检查此接口并未做大计算量的工作,有可能是日志的级别问题,CPU 在频繁的打日志。
改进措施
将日志级别由 DEBUG 级改为 INFO 级
改进效果
同等环境 TPS 由 6842 增长为 23592