调优策略分类
代码
这应该是性能调优第一步,检查相关代码。定位性能瓶颈,再去考虑具体的优化策略。
数据库
sql语句调优
每一个技术人员都应该掌握基本的SQL调优手段(包括方法、工具、辅助系统等),比如自带的慢查询日志或者开源的慢查询系统定位到具体的出问题的SQL,然后使用explain、profile等工具来逐步调优。
数据库架构
一般包括读写分离,多从库负载均衡,水平和垂直分库分表等。
连接池调优
为了实现数据库连接的高效获取、对数据库连接的限流等目的,通常会采用连接池类的方案,即每一个应用节点都管理了一个到各个数据库的连接池,业务增长以后,需要结合当前使用连接池的原理、具体的连接池监控数据和当前的业务量作一个综合的判断,通过反复的几次调试得到最终的调优参数。另外做了业务拆分以及分布式处理后,多台机器会各自链接数据库,可交由数据库中间件如atlas去统一管理连接。
缓存
本地缓存(HashMap/ConcurrentHashMap、Ehcache、Guava Cache等)
缓存服务(Redis/Tair/Memcache等)
选型考虑
更新策略
LRU、修改DB时更新、过期时间
防止缓存击穿
击穿:即缓存失效的一瞬间,有无数请求打进来,达到了数据库,造成了数据库压力。
解决:一般通过例如Redis的SETNX(set if not exist)来构造一个mutex,即一个互斥条件,也可以认为是个分布式锁,让一个线程去DB取数据并放入缓存,其它请求线程sleep一下再去重试从缓存拿数据。
异步
好处:
1.对于不需要立即拿到结果的情况下,更快的返回,用户体验好。
2.避免线程长时间运行,防止线程池被占用完。
3.线程长时间运行会引起系统load,CPU使用率,机器整体性能下降等一系列问题。
NoSQL
如果业务数据不需要和其他数据作关联,不需要事务或者外键之类的支持,而且有可能写入会异常频繁,这个时候就比较适合用NoSQL(比如HBase)。
JVM调优
什么时候调?
通过监控系统(如没有现成的系统,自己做一个简单的上报监控的系统也很容易)上对一些机器关键指标(gc time、gc count、各个分代的内存大小变化、机器的Load值与CPU使用率、JVM的线程数等)的监控报警,也可以看gc log和jstat等命令的输出,再结合线上JVM进程服务的一些关键接口的性能数据和请求体验,基本上就能定位出当前的JVM是否有问题,以及是否需要调优。
怎么调?
1.如果发现高峰期CPU使用率与Load值偏大,这个时候可以观察一些JVM的thread count以及gc count(可能主要是young gc count),如果这两个值都比以往偏大(也可以和一个历史经验值作对比),基本上可以定位是young gc频率过高导致,这个时候可以通过适当增大young区大小或者占比的方式来解决。
2.如果发现关键接口响应时间很慢,可以结合gc time以及gc log中的stop the world的时间,看一下整个应用的stop the world的时间是不是比较多。如果是,可能需要减少总的gc time,具体可以从减小gc的次数和减小单次gc的时间这两个维度来考虑,一般来说,这两个因素是一对互斥因素,我们需要根据实际的监控数据来调整相应的参数(比如新生代与老生代比值、eden与survivor比值、MTT值、触发cms回收的old区比率阈值等)来达到一个最优值。
3.如果发生full gc或者old cms gc非常频繁,通常这种情况会诱发STW的时间相应加长,从而也会导致接口响应时间变慢。这种情况,大概率是出现了“内存泄露”,Java里的内存泄露指的是一些应该释放的对象没有被释放掉(还有引用拉着它)。那么这些对象是如何产生的呢?为啥不会释放呢?对应的代码是不是出问题了?问题的关键是搞明白这个,找到相应的代码,然后对症下药。所以问题的关键是转化成寻找这些对象。怎么找?综合使用jmap和MAT,基本就能定位到具体的代码。
其他
前端、分布式文件系统、CDN、全文索引、空间索引等几方面。
其它知识点
有些业务场景在程序里修改了数据库内容会做一些事情,而对于人工直接对数据库的操作却感知不到,这个时候可以通过Databus去解决这个问题。