原创

Java应用调优实录

本文记录下近期的Java应用调优(线上环境:JDK8 + mysql5.7 + CPU4核 + 8G内存下的调优)

起因

缘自上帝(客户)要求,上帝的要求就是坚持下去的动力,要求如下:

  • 各功能TPS要在300以上 (TPS和响应时间、)
  • 压测时FGC要满足6小时最多一次,8小时内最多两次
  • 压测时FGC单次耗时要尽量在50ms内,最多100ms
  • 极限压测状态下TPS要平稳,无报错、异常(压力测试tps性能下降问题解决方案
  • CPU使用在90%以下,负载在10以内。。。
  • 数据库无慢sql、异常报错

经过

首先,基于这样的要求首先优化方向就确定了,基本有如下几方面:

  • 数据库调优
    • 索引优化
    • 数据库缓存
    • 报错、异常、慢sql
    • 配置调整
  • JVM调优
    • 内存比例调整
    • GC收集器调整
    • 内存回收、占用分析
  • 代码调优
    • 增加缓存
    • 代码分析
  • 硬件调优
    • 加CPU
    • 加内存
    • 加带宽
    • 加机器

下面就基于以上各模块进行分析:

数据库优化

效果最明显、最快的优化就是数据库的优化了,应当优先考虑此方面。

索引优化

一般情况下,QPS上不去很有可能跟数据库有关系,开启下慢sql监控看下。根据慢sql结合数据库explain好好分析下扫到的rows,看看是不是很高,通常存在索引优化情况(索引应尽量控制数量,不要加太多,过多可以考虑使用复合索引或调整表结构),QPS都能有大比例的提升,但前提是待有足够的数据量供测试,最好有百万级以上的数据,效果比较明显。

数据库缓存

如果有过多的查询场景,可以适当开启数据库缓存,增加查询性能。

报错、异常

检查sql错误日志是否有异常日志。

配置调整

数据库连接数、超时时间等等数据库配置,可自行百度。

JVM调优

JVM调优对应用的流畅体验还是很明显的,置于调整的合适点还是要观察分析出来的,JVM可调的也就内存比例、垃圾收集器选取,再有就是根据内存回收分析潜在问题,比如内存溢出。

内存比例调整

内存比例调整基本要调整+观察,没有确定的值,没有明确的答案,这里只说不好的情况:

元数据空间

最好调整一个比实际占用大一点的大小,默认是比较小的,如果大小不够会触发FGC

新老比例
新生代设置过小会导致YGC耗时短且频率高、老年代很容易满,当然老年代足够大就另说了;
新生代过大YGC耗时会增加;
Eden:Sur比例过大会导致容易进入老年代,过小Eden容易满时YGC会频繁,Sur一般为复制算法应考虑来回复制的开销;
老年代过小FullGC耗时会短且频繁,且有可能溢出;
老年代过大FullGC耗时会过长;

总的来说比例调整要结合调优目标、收集器、服务器配置进行调整,找到一个合适的度即可。

GC收集器调整

选择垃圾收集器要根据实际要求、场景来:

  • 想要少量的内存和并行开销选择串行收集器Serial GC;
  • 对吞吐量有要求可以使用Parallel GC;
  • 需要尽量少的停顿时间使用CMS
  • 大的内存处理要求用G1;
  • 高版本的JDK的大数据收集器ZGC,致力于停顿时间小于10ms(可以关注);

内存分析

内存分析主要在老年代的分析上,一般对象在创建后生命周期不会过长,正常情况FGC后内存应该基本稳定在一个稳定的比例,偶有浮动,但不能出现持续性的上升情况,如果存在持续性的上升,回收不掉,且回收率越来越大,就应该考虑是不是对象持续性的创建未释放的情况,一般导出FGC前后的堆进行对比,FGC后的堆中的前几名应该就是重要线索,很稳,不稳请留言。

硬件优化

上面优化做完如果离目标还有距离就可以考虑在机器硬件上优化了,当然如果时间充裕的话也可以先考虑代码优化。

加CPU

计算型应用可考虑适当加CPU,CPU负载尽量控制在10以内,线程数调整会有明显的变化(Dubbo性能调优参数及原理

加内存

内存型应用加内存就成了,加多少根据实际场景权衡

加带宽

网络跟不上带宽调整下

加机器

没有什么问题是加机器不能解决的,如果不行就再加一批

代码优化

如果代码没有bug,依然达不到理想状态,就要有话较多时间的觉悟了,是不是代码设计有问题?如果没有那就不用忘下看了(拜拜啦),如果必须做优化,那以下代码仅供参考,也可参照另一篇优化思路的文章寻找突破口:

缓存

是不是系统中查询都是直接请求数据库的?针对变动不频繁的考虑下加缓存,对于重要的数据,缓存的失效、更新等策略也最好考虑下。
reids基本可作为缓存首选:

设计分析

通常没有bug,也没有缓存什么可以优化,就需要考虑是不是功能设计、开发风格的问题了。

对于功能设计可以考虑是不是需要进行拆分、解耦、分布、异步等处理措施

  • 是不是业务路径处理过长?
  • 是不是与数据源交互次数过多、量过大?
  • 是不是CPU计算耗时过度?
  • 是不是内存申请过于频繁?
  • 是不是数据内容可以压缩?
  • 是不是需要加一个缓冲器,如MQ?
  • 是不是需要加一些池?
  • 是不是有可以避免的重复处理?
  • 是不是可以拆分?分模块、分步骤?
  • 是不是可以异步?异步一时爽,一直异步一直爽?(异步尽量可控)
  • 是不是可以避免锁?避免线程安全问题?

结果

优化之后CPU、内存、GC频率基本都达到了要求,目前单次FGC耗时未满足50\100ms以下,目前最佳状态再300ms,再整就是降老年代内存或者增加CPU核数啦,再往深了挖就是在分析下老年代有没有再可以归置归置的内容了。

正文到此结束