问题
生产环境报错:
Caused by: java.sql.SQLRecoverableException: IO Error: Connection reset
其实,就是连接重置的意思。
注:软件 1.数据库连接池是阿里druid 2.数据库是oracle
原因
具体的原因,其实是连接失效。为什么失效?因为没有进行连接失效检查。
再说详细一点,其实就是,连接时间长了,数据库关闭了连接,但是客户端不知道,这个时候如果还是使用已经被断开的连接,就会报错:连接重置。
本质原因是因为数据库连接断开了,具体是被谁断开的?数据库。
解决方法
开启断开连接检查的配置
XXX.testWhileIdle=true
总结
核心配置项
主要是几个比较难以理解比较绕的配置
配置项
1.testOnBorrow
禁止每次检查连接是否失效 //因为会影响性能
2.testWhileIdle
建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
3.timeBetweenEvictionRunsMillis
关闭物理连接的间隔时间1m //默认也是1m。即每间隔1m,检查空闲时间是否大于minEvictableIdleTimeMillis,如果大于就关闭物理连接。
4.minEvictableIdleTimeMillis
最小空闲时间30m //默认也是30m,源码里默认值是30m
5.testOnReturn
归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
值
<property name="testOnBorrow" value="false" /> //默认true,推荐禁止
<property name="testWhileIdle" value="true" /> //默认false,推荐开启
<property name="timeBetweenEvictionRunsMillis" value="60000" /> //默认1分钟,默认即可
<property name="minEvictableIdleTimeMillis" value="300000" /> //默认30分钟,默认即可
<property name="testOnReturn" value="false" /> //默认false,默认即可
testOnBorrow默认是false
官方文档说默认是true,但是生产环境没有配置这个字段,按理来说不会报错,但是仍然会报错:连接重置。所以,怀疑这里有问题,看了一下源码,源码里默认值是false。
官方推荐配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="60000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
最佳实践
一种是数量比较小的配置,一种是数量比较大的配置,这两种都在生产环境使用过。
1.数量比较小的配置
最大连接数量十位数级别(比如20,公式:cpu*2 + 1)
2.数量比较大的配置
最大连接数量百位数级别(比如100)
总结
1.建议不要配置太大,因为配置太大,数据库也处理不过来,而且反而要来回切换连接,反而更是一种消耗。
2.另外,这个值,没有最佳一说,都得看实际情况,多试。
3.最好的办法是,监控起来,观察不同配置的实际运行情况是咋样的。然后,再动态修改配置值。
实际项目配置
1.以前公司配置
XXX.initialSize=10
XXX.minIdle=20
XXX.maxActive=30
XXX.testWhileIdle=true
XXX.testOnBorrow=false
XXX.maxPoolPreparedStatement=50
2.现在公司配置,而且不同项目配置的值不一样
Xxx-server核心交易项目
配置项
<!-- data source dbcp -->
<bean id="dataSource_trade" class="hikefa.core.spring.util.SecretBasicDataSource" destroy-method="close">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="${orderTrade.url}" />
<property name="username" value="${orderTrade.username}" />
<property name="password" value="${orderTrade.password}" />
<property name="defaultAutoCommit" value="false"></property>
<property name="initialSize" value="${orderTrade.inisize}"/>
<property name="maxActive" value="${orderTrade.maxsize}"/>
<property name="minIdle" value="${orderTrade.minidle}" />
<property name="maxIdle" value="${orderTrade.maxidle}" />
<property name="validationQuery" value="select * from dual"/>
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
</bean>
值
orderTrade.inisize=10
orderTrade.maxsize=250
#orderTrade.maxidle=150
orderTrade.minidle=50
trade-base项目
# druid数据源
spring.datasource.druid.mer.initial-size = ${filter.mer.inisize}
spring.datasource.druid.mer.max-active = ${filter.mer.maxsize}
spring.datasource.druid.mer.min-idle = ${filter.mer.minidle}
spring.datasource.druid.mer.default-auto-commit = false
spring.datasource.druid.mer.validation-query = select * from dual
spring.datasource.druid.mer.test-while-idle = true
spring.datasource.druid.mer.time-between-eviction-runs-millis = 60000
spring.datasource.druid.mer.filters = mergeStat,wall
filter.trade.inisize=10 //初始化
filter.trade.minidle=50 //最小空闲
filter.trade.maxsize=100 //最大
manager独立查询项目
配置项
# druid数据源
spring.datasource.druid.manager.initial-size = ${filter.manager.inisize}
spring.datasource.druid.manager.max-active = ${filter.manager.maxsize}
spring.datasource.druid.manager.min-idle = ${filter.manager.minidle}
spring.datasource.druid.manager.default-auto-commit = false
spring.datasource.druid.manager.validation-query = select * from dual
spring.datasource.druid.manager.test-while-idle = false
spring.datasource.druid.manager.time-between-eviction-runs-millis = 600
spring.datasource.druid.manager.filters = mergeStat,wall
值
filter.manager.inisize=10
filter.manager.maxsize=30
filter.manager.minidle=10
solid独立查询项目
配置项
# druid数据源
spring.datasource.druid.mer.initial-size = ${filter.mer.inisize}
spring.datasource.druid.mer.max-active = ${filter.mer.maxsize}
spring.datasource.druid.mer.min-idle = ${filter.mer.minidle}
spring.datasource.druid.mer.default-auto-commit = false
spring.datasource.druid.mer.validation-query = select * from dual
spring.datasource.druid.mer.test-while-idle = true
spring.datasource.druid.mer.time-between-eviction-runs-millis = 60000
spring.datasource.druid.mer.filters = mergeStat,wall
值
filter.mer.inisize=10
filter.mer.maxsize=200
#filter.mer.maxidle=100 //最大空闲配置项,已经被废弃,没用,不用管,也不用配置
filter.mer.minidle=50
search独立查询项目
配置项
# druid数据源
spring.datasource.druid.manager.initial-size = ${filter.mer.inisize}
spring.datasource.druid.manager.max-active = ${filter.mer.maxsize}
spring.datasource.druid.manager.max-idle = ${filter.mer.maxidle}
spring.datasource.druid.manager.min-idle = ${filter.mer.minidle}
spring.datasource.druid.manager.default-auto-commit = false
spring.datasource.druid.manager.validation-query = select * from dual
spring.datasource.druid.manager.test-while-idle = true
spring.datasource.druid.manager.time-between-eviction-runs-millis = 60000
spring.datasource.druid.manager.filters = mergeStat,wall
值
filter.trade.inisize=10
filter.trade.maxsize=200
# filter.trade.maxidle=100
filter.trade.minidle=50