一、复现:
项目运行一段时间未访问数据库(不进行任何操作),再次访问时会出现第一次访问报错,再次访问正常的现象。
二、分析原因:
Mysql服务器默认的“wait_timeout”是8小时,也就是说一个connection空闲超过8个小时,Mysql将自动断开该connection。connections如果空闲超过8小时,
Mysql将其断开,而数据源(DBCP或C3P0)并不知道该connection已经失效,如果这时有Client请求connection,连接池将该失效的Connection提供给Client,将会造成异常。
org.apache.jasper.JasperException: javax.servlet.ServletException:
javax.servlet.jsp.JspException: : Communications link failure Last packet sent to the server was 1 ms ago.
org.apache.jasper.servlet.JspServletWrapper.handleJspException
(JspServletWrapper.java:522) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:398)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:342)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:267)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
三、DBCP解决方案
1、DBCP连接池说明:driverClassName url username password上面四个分别是驱动,连接字符串,用户名和密码
原因是DBCP保持连接的超时时间比MySQL连接超时时间长。mysql配置中的wait_timeout值一定要大于等于连接池的idle_timeout值,否则mysql会在wait_timeout的时间后关闭连接,然而连接池还认为该连接可用,这样就会产生异常。
2、那样,在DBCP中我们需要处理好以下问题:
a、对每个连接进行检查
b、对一次操作数据库超过多少时间的连接进行移除
c、每隔多少时间检测一次连接
d、一个连接在连接多少时间后,就必须删除
配置如下:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://192.168.40.10:3336/XXX" />
<property name="username" value="" />
<property name="password" value="" />
<property name="maxWait" value="20000"></property>
<property name="validationQuery" value="SELECT 1"></property>
<property name="testWhileIdle" value="true"></property>
<property name="testOnBorrow" value="true"></property>
<property name="timeBetweenEvictionRunsMillis" value="3600000"></property>
<property name="numTestsPerEvictionRun" value="50"></property>
<property name="minEvictableIdleTimeMillis" value="120000"></property>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="6000000"/>
</bean>
其中
testOnBorrow和
validationQuery 很重要。
testOnBorrow的意思是从数据库连接池中取得连接时,对其的有效性进行检查。
validationQuery 是用来检查的SQL语句,“select 1”执行较快,是一个不错的检测语句。
四、连接池为C3P0解决方案
1. 增加 MySQL 的 wait_timeout 属性的值
a、修改mysql安装目录下的配置文件 my.ini文件(如果没有此文件,复制“my-default.ini”文件,生成“复件 my-default.ini”文件。将“复件 my-default.ini”文件重命名成“my.ini” ),在文件中设置:
wait_timeout=31536000
interactive_timeout=31536000
这两个参数的默认值是
8小时(60*60*
8=28800)。
。。。。。。
b、mysql命令对这两个属性进行修改
mysql> show variables like '%timeout%';
2.减少连接池内连接的生存周期
减少连接池内连接的生存周期,使之小于上一项中所设置的 wait_timeout 的值。
修改 c3p0 的配置文件,设置:
# How long to keep unused connections around(in seconds)
# Note: MySQL times out idle connections after 8 hours(28,800 seconds)
# so ensure this value is below MySQL idle timeout
cpool.maxIdleTime=25200
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="maxIdleTime" value="${cpool.maxIdleTime}" />
<!-- other properties -->
</bean>
3. 定期使用连接池内的连接,使得它们不会因为闲置超时而被 MySQL 断开。
修改 c3p0 的配置文件,设置:
# Prevent MySQL raise exception after a long idle time
cpool.preferredTestQuery='SELECT 1'
cpool.idleConnectionTestPeriod=18000
cpool.testConnectionOnCheckout=true
修改 Spring 的配置文件:
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="preferredTestQuery"
value="${cpool.preferredTestQuery}" />
<property name="idleConnectionTestPeriod"
value="${cpool.idleConnectionTestPeriod}" />
<property name="testConnectionOnCheckout"
value="${cpool.testConnectionOnCheckout}" />
<!-- other properties -->
</bean>