性能测试

测试方法

  1. 微基准性能测试
    • 可以精准定位到某个模块或者某个方法的性能问题,例如对比一个方法使用同步实现和非同步实现的性能差异
  2. 宏基准性能测试
    • 宏基准性能测试是一个综合测试,需要考虑到测试环境、测试场景和测试目标
    • 测试环境:模拟线上的真实环境
    • 测试场景:在测试某个接口时,是否有其他业务的接口也在平行运行,进而造成干扰
    • 测试目标
      • 可以通过吞吐量响应时间来衡量系统是否达标,如果不达标,就需要进行优化
      • 如果达标,就继续加大测试的并发数,探底接口的TPS
      • 除了关注接口的吞吐量和响应时间外,还需要关注CPU、内存和IO的使用率情况

干扰因素

热身问题

  1. 在Java编程语言和环境中,.java文件编译成.class文件后,需要通过解析器将字节码转换成本地机器码才能运行
  2. 为了节约内存和执行效率,代码在最初被执行时,解析器会率先解析执行这段代码
  3. 随着代码被执行的次数增加,当JVM发现某个方法或代码块运行得很频繁时,就会把这些代码认定为热点代码
    • 为了提高热点代码的执行效率,在运行时,JVM将通过即时编译器(JIT)把这些代码编译成与本地平台相关的机器码
    • 并进行各层次的优化,然后存储在内存中,之后每次运行代码时,直接从内存中获取
  4. 因此在刚开始运行的阶段,JVM会花费很长的时间来全面优化代码,后面就能以最高性能运行了

测试结果不稳定

  1. 不稳定因素:机器其他进程的影响、网络波动、JVM GC的不确定性
  2. 解决方案:通过多次测试,将测试结果求平均,只要能保证平均值在一个合理的范围之内,并且波动不大即可

多JVM

  1. 任意一个JVM都拥有整个系统的资源使用权
  2. 如果一台机器上只部署单独的一个JVM,在做性能测试时,测试结果会很好,但一台机器上有多个JVM,则不一定
  3. 尽量避免线程环境一台机器部署多个JVM

性能分析

  1. 完成性能测试之后,需要输出一份性能测试报告,测试结果需要包括
    • 测试接口的吞吐量和响应时间(平均、最大、最小)
    • 服务器的CPU、内存、磁盘IO、网络IO使用率、JVM的GC情况
  2. 通过观察性能指标,可以发现性能瓶颈,再通过自下而上的方式分析查找问题
    • 首先从操作系统层面,查看系统的CPU、内存、磁盘IO、网络IO的使用率是否存在异常
    • 再通过命令查找异常日志,通过分析日志,寻找导致性能瓶颈的原因
    • 还可以从Java应用的JVM层面下手,查看JVM的GC频率以及内存分配情况是否存在异常
    • 如果系统和JVM层面都没有出现异常情况,可以查看应用服务业务层是否存在性能瓶颈
      • 例如Java编程的问题、读写数据瓶颈
  3. 分析查找性能问题可以采用自下而上的方式,而解决性能问题,一般采用自上而下的方式逐级优化

性能调优

思路:_业务调优 -> 编程调优 -> 系统调优_

优化代码

  1. 应用层的问题代码往往会因为耗尽系统资源而暴露出来
  2. 例如某段代码导致内存溢出,这往往是将JVM的内存耗尽了
    • 这会引发JVM频繁地发生GC,导致CPU居高不下,此时也会耗尽系统的CPU资源
  3. 还有一些非问题代码导致的性能问题,比较难以发现
    • 例如如果对LinkedList进行for循环遍历,每次循环获取元素时,都会遍历一次list,读效率很低
    • 优化方案:可以采用Iterator

优化设计

  1. 面向对象有很多设计模式,可以用于优化业务层以及中间件层的代码设计,进而达到精简代码和提高整体性能的目的
  2. 例如单例模式在频繁创建对象的场景中,可以共享一个对象,减少频繁创建和销毁对象带来的性能开销

优化算法

  1. 合适的算法可以大大提升系统性能
  2. 例如在不同的场景中,使用合适的查找算法可以降低时间复杂度

时间换空间

  1. 如果系统对查询的速度没有很高的要求,但对存储空间要求苛刻,可以考虑用时间换空间
  2. 例如String的intern方法,可以将重复率比较高的数据存储在常量池,重复使用相同的对象,大大节省内存空间
    • 但由于常量池使用的是HashMap类型,如果存储数据过多,就会导致查询性能下降

空间换时间

  1. 使用存储空间来提升访问速度
  2. 例如MySQL的分库分表

参数调优

  1. 根据业务场景,合理地设置JVM的内存空间和GC算法
  2. 另外,合理地设置Web容器的线程池大小和Linux操作系统的内核参数

兜底策略

  1. 性能优化策略,主要为了提高系统性能,而兜底策略,主要为了确保系统的稳定性
  2. 限流
    • 对系统的入口设置最大访问限制,参考性能测试中探底的接口TPS
    • 同时采用熔断措施,友好地返回没有成功的请求
  3. 智能横向扩容
    • 当访问量超过某一个阈值时,系统可以根据需求自动横向扩容
  4. 提前扩容
    • 常用于高并发系统,例如瞬时抢购
    • 此时智能横向扩容无法满足大量发生在瞬间的请求
  5. Kubernetes可以实现智能横向扩容和提前扩容Docker服务

小结

参考资料

Java性能调优实战