吐槽
Java程序压测报告反馈吞吐量过低的时候,这个时候,很多小伙伴摸不着从何处去进行优化,优化的点如下:
索引
索引是最常见的,也是很多通病,大部分情况下索引创建能提升很大一部分性能。
常见的检测方法:
explain select * from xxx;
查看是否使用索引,以及索引是否生效,有的时候索引会失效,比如说复合索引某些字段为空,这种经常会遇到
mysql连接数
数据库连接本身就配置了一定的连接数, 并发上来了,大家都要连,就这么多,僧多肉少,咋办呢,排队等还是干一架呢,然后就
慢了,很好理解,数据库连不上,程序卡在那了。
show variables like '%max_connections%'; 查看最大连接数
set global max_connections=1000;重新设置
参考文献:https://blog.youkuaiyun.com/qq_24531461/article/details/80607890
tomcat连接数
数据库连接池
java代码内部配置调整
开启数据压缩
如果一个查询返回的数据量过大,而且接口并发量也挺大的时候,这个时候就需要对Http返回的数据进行压缩,因为数据量
一旦过大,本身传输耗时也会很大
数据参考:一次请求数据量是38kb,压缩后只有2.8kb,数据量降低了十倍
友情提醒:开启压缩会消耗CPU性能,如果并发量不是很大的时候,开启压缩本身不会得到明显的提升。
springboot开启压缩的方式(压缩的是传输数据,这个只能在浏览器中查看,PostMan中看到的是解压缩后的数据量)
#开启压缩
server.compression.enabled=${SERVER_COMPRESSION_ENABLED:false}
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain
#默认就是2048 byte
server.compression.min-response-size=${SERVER_COMPRESSION_MIN_RESPONSE_SIZE:2048}
移除无效数据
很多同学代码返回的基本Bean里可能会有很多无效的数据,比如null,基本类型的默认值,这些字段其实本身都属于废弃的字段
这种字段如果数据量少,并发低的时候无伤大雅,但是数据量大并发也上来的时候将会是一个性能上的掣肘,因此最好还是过滤
有一些工具能帮助我们自动过滤掉
@JsonInclude(JsonInclude.Include.NON_NULL)
在基础的Bean上面使用这个标签,数据传输的时候只会传输非空的字段
Include.Include.ALWAYS 默认
Include.NON_DEFAULT 属性为默认值不序列化
Include.NON_EMPTY 属性为 空字符串或者为 NULL 都不序列化
Include.NON_NULL 属性为NULL 不序列化
Jvm性能调优
-e JAVA_OPTS='-Xmx4g -Xms4g -XX:+UseG1GC'
GC次数以及GC停顿时间,找出最佳的内存分配,GC回收方式调整为G1收集器
参考文献:https://blog.youkuaiyun.com/lovexiaotaozi/article/details/82963472
jvm性能优化还可以监听CPU 内存,监听线程数,耗时等等。有些问题监控的时候能明显看出来,比如高CPU,高内存,也有一些
需要我们自己去分析,比如线程堆栈情况,这些稍微复杂点,后续补充
代码层面
代码层面涉及的就比较多了,首先是SQL索引是否生效;其次观察CPU 内存是否达到瓶颈;如果内存不足会产生FullGC极度消耗性能;考虑进行优化内存设计,扩大内存配置,设置GC回收方式;CPU问题优化算法等方式;观察线程是否产生死锁;是否有线程阻塞;比如说额外的去进行打印sql日志有可能会导致线程阻塞;
这些都需要采用java自带的一些工具进行监测:JMC Java VisualVM;JMC功能比较强大能够定位到线程代码层面能检测到死锁,后者属于轻量级的可自行选择;其实还有数据压缩等等;
比如高并发的代码中写入System,out,prinln就可能造成性能的瓶颈,引用一些第三方的包中有可能使用有误使用这种情况,比如 https://blog.youkuaiyun.com/liangheyan/article/details/111994147
外部因素
比如说带宽,宿主机的配置,网络传输距离等
开启缓存
实在不行就只能用缓存了,牺牲空间换取时间,当然你得考虑合不合适,哈哈哈
性能压测分析
用户数量:并发数量
比如设置用户数量是200;其含义是时刻保持有200个用户在线,这200个用户请求结束了后会立即再次发起请求;在程序中的含义就是200个用户线程
TPS:每秒能处理的请求数量
200用户 TPS是1000
含义是200个用户并发,每秒能处理1000个,看到这里有点抽象,实际是200个线程,每个线程每秒能够处理5个请求,因为用户是会不断的去发的,所以能达到1000TPS;为了测试性能瓶颈还需要加大并发数量,因为提升用户数量会导致线程数量增多,线程数量提升之后,线程之间切换会占用原有线程的时间,在一定程度上会影响原有线程的执行效率,但是如果占用的是原有线程的空闲时间,那么其实是能提升效率的,因此前期提升线程数量能有效的提升性能;但是到了后面线程数量达到一定程度,最初一个请求结束需要耗费0.2S,如果这0.2S都给其余线程占用了,那么这时候就不是提升性能了而是影响别人的请求;这时候性能就开始走下坡路
凡事都有利弊;线程切换本身是耗费性能的;线程数量过低会导致资源利用率过低比如磁盘寻址会很耗时;会有漫长的等待期间;
压测曲线正是两个利弊中间的衡量,前期是线程数量低的弊端远高于线程切换的弊端,较低的线程数量会导致线程运行过程中的磁盘寻址等待时间白白浪费;后期的降低曲线则恰恰说明了线程数量过多造成切换耗时弊端远高于线程线程等待耗时;可以参考Redis单线程设计来参考;原理一样