在性能优化调优数据库连接池配置

调优效果

修改前:

spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.initial-size = 20
spring.datasource.druid.max-active = 50
spring.datasource.druid.min-idle = 20
spring.datasource.druid.max-wait = 5000
spring.datasource.druid.validation-query = select 1
spring.datasource.druid.test-on-borrow = true
spring.datasource.druid.test-on-return = true
spring.datasource.druid.test-while-idle = true
spring.datasource.druid.time-between-eviction-runs-millis = 60000
spring.datasource.druid.min-evictable-idle-time-millis = 60000

修改后:

spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.initial-size = 3
spring.datasource.druid.max-active = 15
spring.datasource.druid.min-idle = 3
spring.datasource.druid.max-wait = 5000
spring.datasource.druid.validation-query = select 1
spring.datasource.druid.test-on-borrow = false
spring.datasource.druid.test-on-return = false
spring.datasource.druid.test-while-idle = true
spring.datasource.druid.time-between-eviction-runs-millis = 60000
spring.datasource.druid.min-evictable-idle-time-millis = 180000

优化的效果为:

  1. tps提升2倍+
  2. 服务器cpu从350% 下降为 40%

疑问点

因为主要调试的是连接数,但是连接数变小主要是减少数据库连接线程的切换,降低cpu使用,理论上cpu足够时,tps不会出现这么显著的提升,而且cpu降低的过于离谱

既然理论上解释不了,那就实践一波可能的影响因素

通过控制唯一变量的方式,首先修改 spring.datasource.druid.test-on-borrow 这个,将false 修改回 true
这个时候发现:

  1. tps仅比一开始提升1.5倍
  2. 服务器cpu仍然为350%

同样的方式试验其余变量

OK,初步断定参数 test-on-borrow 和 test-on-return 这两个配置项的影响

网上资料

mysql调优中关注的参数:

validationQuery
用来检测连接是否有效的sql,如果validationQuery为空,那么testOnBorrow、testOnReturn、testWhileIdle这三个参数都不会起作用,因为这三个参数都是通过执行参数validationQuery指定的SQL来验证数据库连接的有效性,配置参考:validationQuery=SELECT 1

testWhileIdle
建议配置为true。对性能影响很小,因为是定期检查。如果连接空闲时间大于timeBetweenEvictionRunsMillis指定的毫秒,就会执行参数validationQuery指定的SQL来检测连接是否有效。

testOnBorrow
建议配置为false。获取连接时执行validationQuery检测连接是否有效,这个配置会降低性能。

testOnReturn
建议配置为false。归还连接时执行validationQuery检测连接是否有效,这个配置会降低性能。

如果这4个参数都是按照上面建议进行配置,那么就不会有什么性能问题,
但是,下面这张压测表格 testOnReturn和testOnBorrow 不同值组合的情况下,5000次主键查询的耗时比较:
在这里插入图片描述
由这张压测结果表,我们可以得出如下结论:

testOnReturn和testOnBorrow都为false时性能最好。

如果任意一个参数为true(另一个为false),会导致性能下降5倍多。

testOnReturn和testOnBorrow对性能的影响几乎一样(因为它们都会通过执行参数validationQuery指定的SQL来校验连接的有效性)

事实上,这两个参数在druid中默认值就是false

arthas问题定位

通过apm链路监控,可以得到主要耗时在获取当前数据库连接耗时getConnection,代码入口

@Override
    public DruidPooledConnection getConnection() throws SQLException {
        return getConnection(maxWait);
    }

    public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
        init();

        if (filters.size() > 0) {
            FilterChainImpl filterChain = new FilterChainImpl(this);
            return filterChain.dataSource_connect(this, maxWaitMillis);
        } else {
            return getConnectionDirect(maxWaitMillis);
        }
    }

安装arthas 并追踪链路耗时

wget https://alibaba.github.io/arthas/arthas-boot.jar

java -jar arthas-boot.jar

在这里插入图片描述

通过一层层定位并结合druid代码

trace com.alibaba.druid.pool.DruidDataSource pollLast  '#cost > 10' -n 3

在这里插入图片描述

发现在代码中
在这里插入图片描述

需要加载类 com.mysql.jdbc.MySQLConnection,而当前mysql-connector 8.X的驱动中并不存在,Utils.loadClass方法类加载生吞了异常,所以代码逻辑会频繁对不存在的类进行类加载

trace com.alibaba.druid.util.Utils loadClass  '#cost > 10' -n 3

在这里插入图片描述
接着实验执行10000次,当类存在时需要时间40ms+,当类不存在时需要3500ms,相差两个量级

优化

其实在druid的issue 中也有人提到此问题,且在1.1.24版本已经解决了该问题

所以在前面针对testOnReturn和testOnBorrow 性能5倍的优化是因为druid 1.1.10中的bug的结论,其实试验发现性能大概损耗不到10%,并没有前面得到的结论这么离谱

该问题mysql的官方建议配置项将配置 test-on-borrow 和 test-on-return 设置为false

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐