性能优化有感
性能优化是个枯燥,却又有趣的过程
性能优化我大致分为几个方面
- 代码优化
- 线程优化、异步
- JVM优化
- 数据库优化
- 缓存优化
- 架构优化
下面来展开谈谈感悟,也可以参考美团技术团队常见性能优化
代码优化
代码是跟我们接触最多的东西,代码优化主要有
- 代码结构优化,可以方便日后扩展和代码标准化
- 是否可以进行抽象避免冗余代码
- 是否可以利用设计模式
- 代码是否遵循阿里巴巴Java开发手册
这里可以采用阿里巴巴Java开发规范插件
- 代码是否有多重循环,无效的查询等
- 多重循环可以根据数据量采用HashMap进行查询,属于利用空间换取时间
- 无效的查询等可以进行删除
这里推荐Findbugs
线程优化
线程优化是一个复杂的过程,多线程如何运用,如何避免死锁、饥饿、活锁等问题
-
线程使用的地方
- 在查询中可以运用到多线程,把串行化操作变化为并行化操作
- I/O阻塞的操作,最经典的莫过于BIO
- 计算量大的操作,使用多线程可以更好的运用CPU的运算能力
-
线程池
需要了解线程池的状态,线程池的参数,以及内部原理,并选择合适的线程池
这里推荐一篇文章Java线程池实现原理及其在美团业务中的实践 再次感谢美团技术团队- 线程池分为
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- newScheduleThreadPool
- forkjoinPool
注意:这里不建议使用Executors来创建线程池,方式OOM等问题,参考阿里巴巴开发规范,建议使用new ThreadPoolExecutor()
- 线程池分为
-
异步
异步建议采用MQ,使用范围例如insert、update操作,可以丢到MQ,这里要注意MQ消息丢失问题,同时MQ也可以实现延迟队列,例如30分钟关闭订单,采用Job的方式并不是一个好的解决方案,可以采用MQ延时队列实现,这里推荐rocketmq(阿里巴巴牛逼) -
并发容器的选择
concurrenthashmap和synchronizedmap性能差距很大,建议选择合适的并发容器
JVM优化
JVM优化这里本人也不是特别的熟练,只是讲一些自己所知道的
- 无日志不优化
在没有日志的情况下,该怎么优化,如何优化,哪些方面需要优化,这里都是未知的,所以需要日志才能进行优化 - 工具
优化要有趁手的工具,好在JDK本身就为我们提供了不少,在JDK的bin目录下有一堆自带的工具,这里介绍几个我熟悉且用过的- jps:查看当前机器上的java程序
- jcmd:跟jps差不多但是能看到启动命令
- jstat:查看JVM信息,例如GC信息
- jinfo:查看JVM参数
- jmap:JVM在内存中的情况,可以导出Dump
- jstack:栈信息,可以用来查看死锁等
- jconsole:一个可视化工具
- jvisualvm:也是一个可视化工具比jconsole功能强一些,但是都有局限性,在服务器上无法使用
- eclipse memory analyer:分析dump文件
- 参数优化
使用上面的工具对服务器进行监控,可以根据信息适当的调整堆大小,GC收集器等。建议开启-XX:+HeapDumpOnOutOfMemoryError
数据库优化
数据库是优化中重要的一环,具体优化方法如下
- SQL优化:是否可以去掉不必要的查询,使用explain对sql进行分析,避免回表等
- 分库分表:一台数据库可能存在性能瓶颈,可以采用分库分表的方式,拆分主要采用冷热数据分离、日期分割、HASH取模等。
- 读写分离:对数据的修改可以操作主库,对数据的查询可以操作从库,进行读写分离
分库分表以及读写分离后可能会出现分布式事务、读写分离操作的复杂性等问题这里需要引入中间件例如:MyCat sharding jdbc
缓存优化
缓存是个优化的好办法但是也有弊端
- 缓存分类
- JVM缓存:concurrenthashmap、guava缓存
- Redis等NoSQL数据库
- 缓存拆分
可以在代码中对接口进行拆分,可以缓存的和不可以缓存的写成多个方法,对可以缓存的进行缓存 - 缓存带来的弊端
缓存并不是越多越好,缓存给系统带来了复杂性,例如:何时添加缓存、何时删除缓存、缓存雪崩、缓存击穿、缓存穿透、缓存预热、Redis集群如何实现、Redis哨兵、Redis脑裂等问题
架构优化
架构优化其实就程序员来说能做的并不是很多
- 限流:GateWay进行限流,限流方法有滑动窗口、漏桶、令牌桶算法,这里建议采用合适的方法进行限流防止应用崩溃
- 熔断:可以采用hystrix防止服务雪崩
- CDN优化:对静态资源采用CDN可以提交响应速率
- DNS优化:这个暂时不熟可以交给运维处理
- 服务扩容:可以采用K8S和Docker的技术对服务进行动态扩容,这里也是运维实现
测试
优化了这么多,体现当然是压力测试,这里因为不熟悉性能指标,单机TPS部分接口可以达到800+ RT在300ms以内,希望大家提供一个单机性能标准,以上采用的是LoadRuner测试的结果
洋洋洒洒写了这么多,主要的还是平时的积累以及思考,也希望您对内容进行补充。