1.问题描述

项目里之前用的是原生druid数据源,后来为了解决多个数据源的事务一致性问题,引入了atomikos数据源。
java配置如下

 @Bean(name = "bdpDatasource")
    public DataSource bdpDatasource() {
        DruidXADataSource druidXADataSource = new DruidXADataSource();
        druidXADataSource.setUrl(bdpProperties.getUrl());
        druidXADataSource.setPassword(bdpProperties.getPassword());
        druidXADataSource.setUsername(bdpProperties.getUsername());
        druidXADataSource.setConnectionProperties(bdpProperties.getConnectionProperties());
        druidXADataSource.setDriverClassName(bdpProperties.getDriverClassName());
        druidXADataSource.setMaxActive(bdpProperties.getMaxActive());
        druidXADataSource.setMaxWait(bdpProperties.getMaxWait());
        druidXADataSource.setMinIdle(bdpProperties.getMinIdle());
        druidXADataSource.setInitialSize(bdpProperties.getInitialSize());
        druidXADataSource.setValidationQuery(bdpProperties.getValidationQuery());
        druidXADataSource.setValidationQueryTimeout(bdpProperties.getValidationQueryTimeout());
        druidXADataSource.setTestOnBorrow(bdpProperties.getTestOnBorrow());
        druidXADataSource.setTestOnReturn(bdpProperties.getTestOnReturn());
        druidXADataSource.setTestWhileIdle(bdpProperties.getTestWhileIdle());
        druidXADataSource.setTimeBetweenEvictionRunsMillis(bdpProperties.getTimeBetweenEvictionRunsMillis());
        druidXADataSource.setMinEvictableIdleTimeMillis(bdpProperties.getMinEvictableIdleTimeMillis());
        try {
            druidXADataSource.setFilters(bdpProperties.getFilters());
        } catch (SQLException e) {
            e.printStackTrace();
        }
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(druidXADataSource);
        xaDataSource.setMaxPoolSize(bdpProperties.getMaxActive());
        xaDataSource.setMinPoolSize(bdpProperties.getMinIdle());
        xaDataSource.setMaxLifetime(bdpProperties.getMaxWait());
        xaDataSource.setTestQuery(bdpProperties.getValidationQuery());
        xaDataSource.setUniqueResourceName("bdpDatasource");
        return xaDataSource;
    }

properties配置如下

spring.datasource.druid.driverClassName=com.mysql.jdbc.Driver
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=20000
spring.datasource.druid.minIdle=5
spring.datasource.druid.initialSize=5
spring.datasource.druid.validationQuery=select 1
spring.datasource.druid.validationQueryTimeout=30000
spring.datasource.druid.testOnBorrow=true
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.testWhileIdle=false
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
#spring.datasource.druid.max-evictable-idle-time-millis=900000
spring.datasource.druid.filters=stat,wall,slf4j,config

结果开发环境偶尔报如下错误
在这里插入图片描述
上线到生产环境后报如下错误
在这里插入图片描述

2.问题重现

开始看生产环境的报错日志,怀疑是数据库那边的问题,因为测试环境从没有报过生产环境这样的错误,所以排除代码原因。
后来同事反馈,可能是测试和生产并发量不一样,因为生产的并发量多,其中有个接口是隔20秒就会调一次的,于是造成连接耗尽。
于是开始使用jemter压测,测试的环境如下

- 使用现有atomikos数据源配置

1.使用jmeter模拟2000个并发调用任意一个接口,结果重现了生产环境的错误。
2.将数据源配置的maxActive参数改为100,再使用jmeter模拟2000个并发调用接口,结果没有任何问题

- 使用以前的druid数据源配置

maxActive改回20,再使用jmeter模拟2000个并发调用接口,结果没有任何问题

3.分析推测

看来是连接数太小,导致atomikos连接耗尽。
但是之前咱们用的是原生druid数据源,maxActive配置也只有20,为什么没有耗尽呢?

查看如下druid的数据源监控界面
在这里插入图片描述
发现在maxActive相同的前提下,当使用原生druid时

活跃连接数在并发操作结束后,很快恢复到最小连接数左右

当使用atomikos时

活跃连接数在并发操作结束后,需要等一段时间才能恢复到最小连接数左右

4.结论

DruidXADataSource的连接归还速度远小于原生druid,导致并发量大的情况下,由于本来占用连接就多,归还速度又慢,最终出现上述问题。

5.解决

调大maxActive为100

6.参考资料

https://blog.csdn.net/taxuexunmei414923794/article/details/14450181

7.后续方案

后来发现这种方式不够彻底,因为当并发量继续增加时,maxActive参数不得不继续调大,否则还是可能出现上述问题。
最终,尝试将DruidXADataSource 换成MysqXADataSource ,在maxActive依旧是20的情况下,经过压力测试,发现问题得以彻底解决。

@Bean(name = "bdpDatasource")
    public DataSource bdpDatasource() {
        MysqlXADataSource mysqlXADataSource = new MysqlXADataSource();
        mysqlXADataSource.setUrl(bdpProperties.getUrl());
        mysqlXADataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXADataSource.setUser(bdpProperties.getUsername());
        Properties properties = druidDatasource.getConnectProperties();
        PasswordDecryptUtil config = new PasswordDecryptUtil();
        mysqlXADataSource.setPassword(config.decryptPassword(properties, bdpProperties.getPassword()));

        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXADataSource);
        xaDataSource.setMaxPoolSize(bdpProperties.getMaxActive());
        xaDataSource.setMinPoolSize(bdpProperties.getMinIdle());
        xaDataSource.setMaxLifetime(bdpProperties.getMaxWait());
        xaDataSource.setTestQuery(bdpProperties.getValidationQuery());
        xaDataSource.setUniqueResourceName("bdpDatasource");
        return xaDataSource;
    }
Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐