Druid版本1.1.21出现ERROR c.alibaba.druid.pool.DruidDataSource - discard connection

3,254 阅读5分钟

Druid版本:1.1.21

1. 问题描述

使用 Druid数据库连接池,闲置一段时间后就出现以下错误:

ERROR c.alibaba.druid.pool.DruidDataSource - discard connection
com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 63,213,665 milliseconds ago. The last packet sent successfully to the server was 63,213,666 milliseconds ago. is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.

大概意思是当前的connection所进行过的最新请求是在63,213秒之前,这个时间是大于服务所配置的wait_timeout(默认28,800秒)时间的

2. 原因分析

原因分析:

MySQL连接时,服务器默认的“wait_timeout”是8小时,也就是说一个connection空闲超过8个小时,Mysql将自动断开该connection。connections如果空闲超过8小时,Mysql将其断开,而Druid连接池并不知道该connection已经失效,如果这时有Client请求connection,Druid将该失效的Connection提供给Client,将会造成异常。

官方之前有两个该问题的issue:

(1) github.com/alibaba/dru…

该issue显示在1.0.26版本中已经出现该问题,官方回复在1.0.27版本修复,但是评论回复1.2.23版本中又出现该问题。

(2) github.com/alibaba/dru…

该issue显示在1.1.1版本后又出现该报错问题。该issue处于open状态,官方也没有进行回复,暂不清楚该问题是否解决。

3. 解决方案

根据分析,有以下几种解决方案。

(1) 版本升级

(2) 添加autoReconnect=true

根据网上资料:stackoverflow.com/questions/9…

以及报错提示using the Connector/J connection property 'autoReconnect=true' to avoid this problem。

官网对autoReconnect的解释如下:

autoReconnect
Should the driver try to re-establish stale and/or dead connections? If enabled the driver will throw an exception for a queries issued on a stale or dead connection, which belong to the current transaction, but will attempt reconnect before the next query issued on the connection in a new transaction. The use of this feature is not recommended, because it has side effects related to session state and data consistency when applications don't handle SQLExceptions properly, and is only designed to be used when you are unable to configure your application to handle SQLExceptions resulting from dead and stale connections properly. Alternatively, as a last option, investigate setting the MySQL server variable "wait_timeout" to a high value, rather than the default of 8 hours.
Default: false
Since version: 1.1

其他解决方案:www.cnblogs.com/mracale/p/9…

(3) 增加MySQL的wait_timeout属性的值不推荐

修改mysql安装目录下的配置文件 my.ini文件(如果没有此文件,复制“my-default.ini”文件,生成“复件my-default.ini”文件。将“复件my-default.ini”文件重命名成“my.ini”),在文件中设置:

wait_timeout=31536000 
interactive_timeout=31536000 

这两个参数的默认值是8小时(60608=28800)。 注意: 1.wait_timeout的最大值只允许2147483 (24天左右)

(4) 减少连接池内连接的生存周期

减少连接池内连接的生存周期,使之小于上一项中所设置的wait_timeout 的值。

(5) 定期使用连接池内的连接

定期使用连接池内的连接,使得它们不会因为闲置超时而被 MySQL 断开。 

附录:

DruidDataSource配置的语意。

配置缺省值说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错。详情-点此处
url连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里
driverClassName根据url自动识别这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxPoolPreparedStatementPerConnectionSize-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。
validationQueryTimeout单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
keepAlivefalse (1.0.28)连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作。
timeBetweenEvictionRunsMillis1分钟(1.0.14)有两个含义: 1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun30分钟(1.0.14)不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis连接保持空闲而不被驱逐的最小时间
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall
proxyFilters类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
     <!-- 基本属性 url、user、password -->
     <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="minIdle" value="1" />

     <!-- 配置获取连接等待超时的时间 -->
     <property name="maxWait" value="60000" />

     <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
     <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>