现象:
系统即将上线,再做压力测试时,突然性能直线下降,原本几十ms的服务,性能下降了几十倍;
分析调用链日志,发现性能瓶颈全部集中在某一个微服务应用内;于是展开对该应用的分析;
分析过程:
1、在灰度环境(使用生产库)单独调用出现性能问题的服务,性能表现正常;由此可排除该服务使用的sql问题及索引问题;
2、查看生产应用的资源使用情况(cpu占用10%,内存占用达到90%)。
3、查看gc日志:有大量Full GC Allocation Failure错误。
(concurrent mode failure): 2804992K->2804991K(2804992K), 8.3314947 secs] 3111679K->3111637K(3111680K), [Metaspace: 113455K->113455K(1153024K)], 8.3316136 secs] [Times: user=8.02 sys=0.32, real=8.34 secs]
2235.607: [Full GC (Allocation Failure) 2235.607: [CMS: 2804991K->2804991K(2804992K), 5.2187601 secs] 3111679K->3111636K(3111680K), [Metaspace: 113455K->113455K(1153024K)], 5.2188652 secs] [Times: user=4.98 sys=0.25, real=5.22 secs]
2240.827: [GC (CMS Initial Mark) [1 CMS-initial-mark: 2804991K(2804992K)] 3111636K(3111680K), 0.1321992 secs] [Times: user=0.17 sys=0.00, real=0.13 secs]
2240.959: [CMS-concurrent-mark-start]
2240.961: [Full GC (Allocation Failure) 2240.961: [CMS2242.818: [CMS-concurrent-mark: 1.854/1.858 secs] [Times: user=1.86 sys=0.00, real=1.86 secs]
ps:堆内存划分为 Eden、Survivor (分两块)和 Tenured/Old 空间
GC前,young(eden+s0)的占用是100%,且从Allocation Failure可以看出old满了
由于young无法通过Young GC回收,升级到old,但old也满了就导致 Allocation Failure 触发Full GC,而且耗时达到8.3 secs!STW导致整个系统都暂停响应了。
GC后,young依旧没有变化,还是100%,是内存泄漏导致无法回收,系统OOM了。
还有些gc日志中,在GC后,young释放了资源,说明young增长过快导致young gc来不及回收,从而把old挤满;
这时又有新bug反应出来:某服务超时,不过从上面的分析过程来看,超时也是迟早的事。
根据上面的分析,应该是某个服务不断创建对象导致的。现在需要定位到底是哪个服务导致的。
检查期间的服务调用日志(其实应该先联系dba检查下系统慢sql)。
果然,发现有个千万级的表查询时条件中没有加用户id。
检查代码发现,mybatis写法:
select xxxx from xxxx
where 1=1
<if "a_no"!=0>
and a_no=#{a_no}
</if>
<if "b_no"!=0>
and b_no=#{b_no}
</if>
总结
1、长期:做代理层的优化,禁止全部查询,避免这样的sql再度出现
2、短期:检查所有类似写法的,要在数据层做好限制。