druid-Caused by: java.sql.SQLRecoverableException: IO Error: Connection reset

3,206 阅读2分钟

问题

生产环境报错:

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>

github.com/alibaba/dru…

github.com/alibaba/dru…

最佳实践

一种是数量比较小的配置,一种是数量比较大的配置,这两种都在生产环境使用过。

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