记一次服务性能优化
记一次服务性能优化
某次服务发布上线后,对线上服务切分并进行压测。结果十分不理想,TPS只有几十。通过一周的优化,最终单机TPS达到了几千的数量级,还算可以的结果,顺利上线。以下分别介绍优化方向:日志配置、数据库配置、存储过程、代码逻辑。
日志配置
原因
服务使用logback作为日志输出。其中有一个控制台输出的配置
<!-- 控制台输出日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36}-%M -%msg%n</pattern>
</layout>
</appender>
.
.
.
<logger name="com">
<level value="info"/>
<appender-ref ref="CONSOLE"/>
</logger>
而ConsoleAppender的输出最终调用System.out进行处理,System.out方法方法内部使用synchronized进行同步。而网络框架使用netty+业务线程池的模式,业务线程池默认100并发,导致多线程压测时性能损耗巨大。
其次,其他日志配置也有些许问题。日志被重复打印到不同文件;线上日志级别开的太低,为debug。都对性能存在影响。
处理方式
删除日志的控制台输出,删除多余日志打印配置,调整日志级别到info或者更高。
数据库配置
原因
由于该项目由之前项目继承而来,很多配置,例如数据库配置,没有仔细核对,导致上线后发现很多地方配置不合理,例如数据库dataSource:
<property name="maxActive" value="1"/>
<property name="initialSize" value="1" />
<property name="minIdle" value="0" />
此处为刚开始的上线配置,数据库线程池初始、最大线程为1,最小线程为0,在高并发情况下,数据库请求会有很大的压力。
处理方式
修改数据库配置:
<property name="maxActive" value="100"/>
<property name="initialSize" value="100" />
<property name="minIdle" value="100" />
将数据库连接池的连接数设为100,提高IO利用率。
数据库存储过程
原因
经过以上两个步骤,大部分接口性能已经有了很大的提升,只有某个接口存在问题。TPS始终与其他接口有数量级差别,并且DBA反应压测此接口对数据库cpu压力极高,TPS500的时候数据库cpu达到了80%,接口服务器却没有丝毫性能压力。经过排查,将问题定位到了一段存储过程A上。存储过程A的sql使用字符串拼接参数的方式进行调用,而不是绑定参数。此写法导致数据库对每个请求都要做一遍sql的硬解析,极度消耗cpu性能,从而造成数据库性能瓶颈。
PS:这个存储过程还是特地让DBA帮忙写的,结果。。。
处理方式
重写此存储过程A,将sql调用方式从字符串拼接改为绑定参数。
结果
以上优化逻辑中,性能提升最高的是日志和数据库配置的修改,几乎提升了百倍的性能。存储过程的修改,将接口性能提升了2~3倍。
上一篇: 一次性能优化最佳实践