C3p0连接配置问题
2016 年 01 月 10 日
db

    在日常开发中,经常会涉及到数据库读写访问,对数据库访问通常会通过数据库连接池来获取数据库连接,再进行SQL,最近涉及到迁移数据,在日志中偶尔出现一些MySQL数据库连接错误,通过DB配置连接池配置最终解决。

    服务器日志中的错误信息大致如:

    com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
    
    The last packet successfully received from the server was 20,024 milliseconds ago.  The last packet sent successfully to the server was 14 milliseconds ago.
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    	at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
    	at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1122)
    	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:3056)
    	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2942)
    	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3485)
    	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1960)
    	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2114)
    	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2690)
    	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2619)
    	at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1465)
    	at com.mchange.v2.c3p0.impl.NewProxyStatement.executeQuery(NewProxyStatement.java:35)
    	at org.haolin.kb.C3p0Test$1.run(C3p0Test.java:40)
    	at java.lang.Thread.run(Thread.java:745)
    Caused by: java.io.EOFException: Can not read response from server. Expected to read 4 bytes, read 0 bytes before connection was unexpectedly lost.
    	at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:2503)
    	at com.mysql.jdbc.MysqlIO.reuseAndReadPacket(MysqlIO.java:2953)
    	... 10 more
    com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure
        

    这样的错误重启应用后,便能暂时解决,但过不了多久又会出现,而且这个错误有可能一直持续下去。最终发现与MySQL超时连接配置有关

    [mysqld]
    # 单位为秒,默认为28800,即8小时
    wait_timeout=100
    interactive_timeout=100
        

    wait_timeoutinteractive_timeout需要同时设置才会生效。当数据库连接超过wait_timeout这段时间内,没有任何数据库操作,MySQL将主动关闭服务端连接,因此客户端再次进行数据库操作时,会报该错误,所以将这两个值设置得比较大,但其实设置过大,对安全和资源也有一定的损失

    ComboPooledDataSource pool = new ComboPooledDataSource();
    pool.setDriverClass("com.mysql.jdbc.Driver" );
    pool.setJdbcUrl("jdbc:mysql://localhost:3306/user_db");
    pool.setUser("root");
    pool.setPassword("");
    // 检测连接配置
    pool.setPreferredTestQuery("SELECT 1");
    pool.setIdleConnectionTestPeriod(5);
    // 获取到连接时就同步检测
    // pool.setTestConnectionOnCheckout(true);
        

    所以最好开启c3p0连接池的自动检测功能,这样即便之前的连接被MySQL关闭了,也不容易出现上述的错误,除非连接池还没来得及检测连接是否可用,就被应用拿来使用了,但c3p0提供了同步检测连接的方式pool.setTestConnectionOnCheckout(true);,但这对应用性能是有所损失的。

    上面的错误在数据库重启的时候也有可能报错,并且错误日志中会多出一部分MySQL关闭的信息:

    com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Server shutdown in progress
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    	at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    	at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
    	at com.mysql.jdbc.Util.getInstance(Util.java:384)
    	at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
    	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3562)
    	at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3494)
    	at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1960)
    	at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2114)
    	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2690)
    	at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2619)
    	at com.mysql.jdbc.StatementImpl.executeQuery(StatementImpl.java:1465)
    	at com.mchange.v2.c3p0.impl.NewProxyStatement.executeQuery(NewProxyStatement.java:35)
    	at org.haolin.kb.C3p0Test$1.run(C3p0Test.java:42)
    	at java.lang.Thread.run(Thread.java:745)
        

    但这种情况下,即便不启用c3p0的连接检测机制,只要应用的数据库操作间隔时间未超过MySQL配置的wait_timeout,下一次数据库操作仍能连接上。

    c3p00.9.5以上开始支持JDBC 4Connection.isValid(),所以可以不用设置检测语句pool.setPreferredTestQuery("SELECT 1")

好人,一生平安。