常见性能优化策略的总结( 二 )


功能
常见做法
一种方法是打开额外的线程 。这里可以采用多开一个线程或者使用线程池的做法,在IO线程以外的线程中处理相应的任务(处理请求响应),让IO线程中的第一个线程 。返回 。
如果为异步线程处理的任务设计的数据量很大,可以引入阻塞队列进一步优化 。具体方法是让一批异步线程不断往阻塞队列中丢数据,然后再启动一个额外的处理线程从队列中批量取出一批预设大小的数据进行批量处理(比如发送一批远程服务)请求),这进一步提高了性能 。
另一种方法是使用消息队列 (MQ) 中间件服务,它本质上是异步的 。一些额外的任务可能不需要我的系统来处理,而是需要其他系统来处理 。这时候可以先把它封装成一个消息,扔到消息队列中,通过消息中间件的可靠性将消息传递给关心它的系统,然后让系统做相应的处理 。
比如C端完成提单后,可能需要其他终端做一系列的事情,但是这些事情的结果不会立即影响到C端用户,所以可以先响应向C端订单请求先返回给用户,再向MQ发送消息再返回 。而这些事情不应该是C端的责任,所以这个时候用MQ的方式来解决这个问题是最合适的 。
NoSQL
缓存和缓存的区别
首先,我解释一下,这里介绍的部分与缓存部分不同 。虽然可能使用相同的数据存储方案(如 Redis 或 Tair),但使用方式不同 。本节介绍它作为 DB 使用 。如果作为DB使用,需要有效保证数据存储方案的可用性和可靠性 。
场景
需要结合具体的业务场景,看这个业务涉及的数据是否适合NoSQL存储,数据操作方式是否适合NoSQL操作,或者NoSQL的一些附加特性(比如加减法等) 。
如果业务数据不需要与其他数据关联,不需要事务或外键等支持,并且可能写入非常频繁,此时NoSQL(如HBase)更适合 。
例如,美团点评有一个内部监控系统 。如果应用系统出现严重故障,可能会在短时间内产生大量数据 。如果此时选择 MySQL,MySQL 的瞬时写入压力会飙升 。容易造成MySQL服务器性能急剧恶化、主从同步延迟等问题 。这种场景比较适合Hbase这样的NoSQL存储 。
JVM 调优
什么时候调整?
通过对机器的一些关键指标(gc time,gc count,每一代的内存大小变化,机器负载)也可以看gc log和jstat等命令的输出,结合在线JVM进程服务的一些关键接口的性能数据和请求体验,基本上可以发现有没有问题当前的 JVM 以及是否需要调整 。
如何调音?
如果发现高峰期CPU使用率和Load值过大,此时可以观察一些JVM 和gc (可能主要是young gc ) 。一个历史经验值进行对比),基本可以确定young gc的频率太高了 。这时候可以通过适当增加young区的大小或比例来解决 。如果你发现关键接口的响应时间很慢,可以结合gc日志中的gc时间和stop the world time,看看整个应用的stop the world time是不是比较长 。如果是,则可能需要减少总 gc 时间 。具体可以从减少gc的次数和减少单次gc的时间两个维度来考虑 。一般来说,这两个因素是一对相互排斥的因素 。我们需要调整相应的参数(比如新生代与老年代的比例,eden与老年代的比例,MTT值,触发cms恢复的老区比例阈值,等)根据实际监测数据达到最优值 。如果full gc或old cms gc出现的非常频繁,这种情况通常会导致STW时间相应增加,也会导致接口响应时间变慢 。在这种情况下,“内存泄漏”的可能性很高 。Java中的内存泄漏意味着一些应该释放的对象没有被释放(并且引用正在拉它) 。那么这些对象是如何创建的呢?为什么不发布?对应的代码有问题吗?问题的关键在于理解这一点,找到对应的代码,然后对症下药 。所以问题的症结在于转化为找到这些对象 。如何找到它?综合使用jmap和MAT,基本可以定位到具体的代码 。