对于一次用户不友好的操作(页面响应时间过长...),我们可以从如下几个方面对涉及该应用的不同层、不同角度对相关性能瓶颈的影响因素进行排查:
硬件瓶颈
cpu
内存
磁盘io
网络io
如果是某个硬件指标有问题,需要深入的进行分析
网络瓶颈
局域网可忽略网络因素
防火墙、动态负载均衡器、交换机等设备
带宽利用率等指标查看
服务器操作系统瓶颈
物理内存不足时,虚拟内存设置也不合理,虚拟内存的交换效率就会大大降低,从而导致行为的响应时间大大增加
中间件瓶颈
参数配置
数据库:慢查SQL,命中率(带缓存、不带缓存等场景),锁、参数设置
tomcat配置
线程池、连接池、GC等,如果是这些指标问题,需要深入分析。
...
应用瓶颈
sql语句
数据库设计
业务逻辑
算法、缓冲、缓存、同步或异步等
...
具体参见下图
解决思路
CPU
CPU资源利用率很高的话,需要看CPU消耗User,Sys,Wait这几种状态下的具体情形:
情形1:如果CPU User非常高,需要查看消耗在哪个进程,可以用top(linux)命令看出,接着用top –H –p 看哪个线程消耗资源高
情形2:如果是java应用,就可以用jstack看出此线程正在执行的堆栈,看资源消耗在哪个方法上,查看源代码就知道问题所在;
情形3:如果是c++应用,可以用gprof性能工具进行分析。
情形4:如果CPU Sys非常高,可以用strace(linux)看系统调用的资源消耗及时间。
情形5:如果CPU Wait非常高,考虑磁盘读写了,可以通过减少日志输出、异步或换速度快的硬盘。
Memory
内存问题主要看某个进程占用内存是否非常大以及是否有大量swap(虚拟内存交换)。
磁盘I/O
磁盘I/O最显著的指标是繁忙率,可以通过减少日志输出、异步或换速度快的硬盘。
网络I/O
网络I/O主要考虑传输内容大小,不能超过硬件网络传输的最大值70%,可以通过压缩、减少内容大小、在本地设置缓存以及分多次传输等。
内核参数
内核参数一般都有默认值,这些内核参数默认值对于一般系统没问题,但是对于压力测试来说,可能运行的参数将会超过内核参数,导致系统出现问题,可以用sysctl来查看及修改。
JVM
jvm主要分析GC/FULL GC是否频繁,以及垃圾回收的时间,可以用jstat命令来查看,对于每个代大小以及GC频繁,通过jmap将内存dump,再借助工具HeapAnalyzer来分析哪地方占用的内存较高以及是否有内存泄漏可能
线程池
如果线程不够用,可以通过参数调整,增加线程;
对于线程池中的线程设置比较大的情况,还是不够用可能的原因是:某个线程被阻塞来不及释放,可能在等锁、方法耗时较长、数据库等待时间很长等原因导致,需要进一步分析才能定位。
JDBC连接池
连接池不够用的情况下,可以通过参数进行调整增加;
但是对于数据库本身处理很慢的情况下,调整没有多大的效果,需要查看数据库方面以及因代码导致连接未释放的原因。
SQL
SQL效率低下也是导致性能差的一个非常重要的原因,可以通过查看执行计划看SQL慢在哪里,一般情况,SQL效率低下原因主要有:
(1) 未建索引,会产生全表扫描
(2) 未利用索引,会产生全表扫描,具体示例如下:
substring(card_no,1,4)=′5378′产生全表扫描
amount/30< 1000产生全表扫描
convert(char(10),date,112)=′19991201′产生全表扫描
where salary<>3000产生全表扫描
name like ‘%张’产生全表扫描
first_name + last_name =’beill cliton’产生全表扫描
id_no in(′0′,′1′)产生全表扫描
select id from t where num=@num有参数也会产生全表扫描
(3) 使用效能低的索引,示例如下:
oder by 非聚族索引,索引性能低
username=’张三’ and age>20,字符串索引低于整形索引
表中列与空NULL值,索引性能低
尽量不要使用IS NULL或IS NOT NULL,索引性能低
(4) 数据量
所有数据量 select *很多列产生大量数据
select id,name表中有几百万行,产生大量数据
嵌套查询先不过滤数据,后过滤数据产生大量无用的数据
关联查询多表进行关联查询,先过滤掉小部分数据,再过滤大部分数据大量关联操作
大数据量插入一次次插入产生大量日志,消耗资源
(5) 锁
锁等待update account set banlance=100 where id=10产生表级锁,将会锁住整个表
死锁A:update a;update b;B:update b;update a;将会产生死锁
游标Cursor Open cursor,fetch;close cursor性能很低
临时表create tmp table 创建临时表产生大量日志
drop table删除临时表需要显示删除,避免系统表长时间锁定
(6) 其他
exist 代替 INselect num from a where num in(select num from b)in会逐个判断,exist有一条就结束
exist 代替select count(*)判断记录是否存在count(*)将累加计算,exist有就结束
between代替INID in(1,2,3)IN逐个判断,between是范围判断
left outer join 代替Not INselect ID from a where ID not in(select b.Mainid from b)NOT IN逐个判断,效率非常低
union all 代替unionselect ID from a union select id from b union删除重复的行,可能会在磁盘进行排序而union all只是简单的将结果并在一起
常用SQL尽量用绑定变量方法insert into A(ID) values(1)直接写SQL每次都要编译,用绑定变量的方法只编译一次,下次就可以用了
策略
日常化的内网的性能测试+定期的真实环境的业务性能测试