MySql有一个系统变量,如图:
以上数值,单位为秒。
mysql的连接允许的闲置时间。当超过闲置时间以后,database端就会将此连接单方面废弃。这时如果使用jdbc继续使用之前的连接,则会收到以下异常:
### Cause: java.sql.SQLException: Could not retrieve transation read-only status server at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:26) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:111) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:102) at org.apache.ibatis.binding.MapperMethod.executeForMany(MapperMethod.java:119) at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:63) at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:52) at com.sun.proxy.$Proxy8.getMonthlyChart(Unknown Source) at com.lux.rcc.kpi.bo.ChartABo.getMonthlyChart(ChartABo.java:46) at com.lux.rcc.kpi.servlets.ChatAServlet.doGet(ChatAServlet.java:70) at javax.servlet.http.HttpServlet.service(HttpServlet.java:620) at javax.servlet.http.HttpServlet.service(HttpServlet.java:727) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:722) Caused by: java.sql.SQLException: Could not retrieve transation read-only status server at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1086) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:951) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:941) at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3972) at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3943) at com.mysql.jdbc.PreparedStatement.checkReadOnlySafeStatement(PreparedStatement.java:1258) at com.mysql.jdbc.PreparedStatement.execute(PreparedStatement.java:1278) at sun.reflect.GeneratedMethodAccessor54.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.apache.ibatis.logging.jdbc.PreparedStatementLogger.invoke(PreparedStatementLogger.java:62) at com.sun.proxy.$Proxy10.execute(Unknown Source) at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:59) at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:73) at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:60) at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:267) at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:137) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:96) at org.apache.ibatis.executor.CachingExecutor.query(CachingExecutor.java:77) at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:108) ... 28 more Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: The last packet successfully received from the server was 85,659,759 milliseconds ago. The last packet sent successfully to the server was 85,659,775 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. at sun.reflect.GeneratedConstructorAccessor24.newInstance(Unknown Source) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:525) at com.mysql.jdbc.Util.handleNewInstance(Util.java:411) at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1129) at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3988) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2598) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2778) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2828) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2777) at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1651) at com.mysql.jdbc.ConnectionImpl.isReadOnly(ConnectionImpl.java:3966) ... 44 more Caused by: java.net.SocketException: Software caused connection abort: socket write error at java.net.SocketOutputStream.socketWrite0(Native Method) at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) at java.net.SocketOutputStream.write(SocketOutputStream.java:153) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3969) ... 50 more
mysql允许设置闲置时间,默认是8小时,最大是1年。
大部分客户端都使用连接池以提高性能,如果用户访问量不大,连接池中的连接可能闲置时间超过数据库允许时间,数据库单方面断掉连接,而客户端却不知情。当下一个用户访问时,使用连接池中的连接,则会抛出上述异常。
解决方法可以是增大闲置时间。但这不是一个好方法。闲置时间是有上限的,在极端情况下,还是可能发生异常。此外,长时间保留闲置的连接,会降低数据库性能,消耗内存,最终耗尽数据库的连接数。所以不推荐增大闲置时间。
一般常用的解决方法是在使用一个长时间闲置的连接之前,对它ping一下,确保它还在正常工作。在mybatis自带连接池配置中,是这样做的:
<environment id="development"> <transactionManager type="jdbc" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/kpi?autoReconnect=true" /> <property name="username" value="mysql" /> <property name="password" value="mysql" /> <property name="poolPingEnabled" value="true"/> <property name="poolPingQuery" value="select now() from kpi.lastupdatedlog limit 1"/> <property name="poolPingConnectionsNotUsedFor" value="3600000"/> </dataSource> </environment>
配置连接池时,需要声明三个属性:
poolPingEnabled - 默认值是false,当值为true的时候,将开启ping机制。
poolPingQuery - 对数据库进行ping时所使用的sql。
poolPingConnectionsNotUsedFor - 默认值是0,单位是毫秒。我们不能在每次使用连接池之前,都使用ping机制,这会使每一条sql的执行,都要额外执行一次ping语句。所以使用此属性来避免这种不合理做法。我们只针对闲置时间超过某个时间的连接,进行ping。本例中的值为1小时,当从连接池中拿出的连接闲置超过1小时,才会对它进行ping。