JDBC MySQL 连接问题 - 尝试重新连接 3 次。放弃

JDBC MySQL Connection Issue - Attempted reconnect 3 times. Giving up

我有一个休息服务应用运行 Java Spring 框架。该应用程序依赖于与外部 MySQL 数据库的连接,该数据库通过 JDBC.

连接

我的问题是在其余服务和 MySQL 数据库之间保持牢固的连接。我有一个我认为基本的连接故障保护,看起来像这样:

public Connection getConnection() throws SQLException {
    if(connection == null){
         this.buildConnection();
    }
    else if(!connection.isValid(10)){ //Rebuild connection if it is no longer valid
        connection.close();
        this.buildConnection();
    }
    return connection;
}

使用此方法应确保在执行任何查询之前连接有效。我的问题是调用此方法时我会定期抛出异常:

Could not create connection to database server. Attempted reconnect 3 times. Giving up. SQLState: 08001. ErrorCode: 0.

让我超级困惑的是:

  1. 此错误只会定期发生。很多时候连接工作只是找到。
  2. 我在我的开发机器上测试了同一个应用程序,但从未发生过这个错误。

我在自己的服务器上自定义配置了 MySQL 数据库,因此我控制了它的所有配置选项。由此我知道这个问题与允许的最大连接数或连接超时无关。

编辑 - 更新 1:

编辑 - 更新 2

经过一些重构后,我能够成功实现 HikariCP 连接池,如下面的@M.Deinum 所述。不幸的是,同样的问题仍然存在。在我的本地机器上一切正常,所有单元测试都通过了,但是当我将它推送到 Azure 并在请求之间等待几分钟后,我在尝试从池中获取连接时收到此错误:

springHikariCP - Connection is not available, request timed out after 38268ms. SQLState: 08S01. ErrorCode: 0.

我的HikariCP配置如下:

//Set up connection pool
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.jdbc.Driver"); 
config.setJdbcUrl("jdbc:mysql://dblocation");

//Connection pool properties
Properties prop = new Properties();
prop.setProperty("user", "Username");
prop.setProperty("password", "Password");
prop.setProperty("verifyServerCertificate", "false");
prop.setProperty("useSSL","true");
prop.setProperty("requireSSL","true");
config.setDataSourceProperties(properties);

config.setMaximumPoolSize(20);
config.setConnectionTestQuery("SELECT 1");
config.setPoolName("springHikariCP");
config.setLeakDetectionThreshold(5000); 

config.addDataSourceProperty("dataSource.cachePrepStmts", "true");
config.addDataSourceProperty("dataSource.prepStmtCacheSize", "250");
config.addDataSourceProperty("dataSource.prepStmtCacheSqlLimit", "2048");
config.addDataSourceProperty("dataSource.useServerPrepStmts", "true");

dataSource = new HikariDataSource(config);

如有任何帮助,我们将不胜感激。

我建议使用像 HikariCP 这样的适当 JDBC 连接池,连同将按正确时间间隔执行的验证查询应该每次都能为您提供新鲜和正确的连接。

假设您使用 Spring 和 xml 配置数据源。

<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
    <property name="poolName" value="springHikariCP" />
    <property name="dataSourceClassName"       value="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" />
    <property name="dataSourceProperties">
        <props>
            <prop key="url">${jdbc.url}</prop>
            <prop key="user">${jdbc.username}</prop>
            <prop key="password">${jdbc.password}</prop>
        </props>
    </property>
</bean>

它默认在结帐时验证连接。我建议尝试一下。

当你使用 java 基础配置时,我建议如下

@Bean
public DataSource dataSource() {
    HikariDataSource ds = new HikariDataSource();
    ds.setPoolName("springHikariCP");
    ds.setMaxPoolSize(20);
    ds.setLeakDetectionThreshold(5000);
    ds.setDataSourceClassName("com.mysql.jdbc.jdbc2.optional.MysqlDataSource");
    ds.addDataSourceProperty("url", url);
    ds.addDataSourceProperty("user", username);
    ds.addDataSourceProperty("password", password);
    ds.addDataSourceProperty("cachePrepStmts", true);
    ds.addDataSourceProperty("prepStmtCacheSize", 250);
    ds.addDataSourceProperty("prepStmtCacheSqlLimit", 2048);
    ds.addDataSourceProperty("useServerPrepStmts", true);
    ds.addDataSourceProperty("verifyServerCertificate", false);
    ds.addDataSourceProperty("useSSL", true);
    ds.addDataSourceProperty("requireSSL", true);

    return ds;
}

好像是MySQL的系统变量wait_timeout引起的。

对于MySQL5.0, 5.1, 5.5, 5.6wait_timeout的默认值为28800秒(8小时),wait_timeout的最大值:

  • Linux : 31536000 秒(365 天,一年)
  • Windows : 2147483 秒(2^31 毫秒,24 天 20 小时 31 分 23 秒)

The number of seconds the server waits for activity on a noninteractive connection before closing it. This timeout applies only to TCP/IP and Unix socket file connections, not to connections made using named pipes, or shared memory.

所以我认为你可以尝试使用 jdbc 连接来保持 ping 间隔几秒钟,或者直接使用一种 JDBC 连接池框架来自动管理 jdbc 连接.

希望对您有所帮助。最好的问候。

假设您现在拥有的用于“//设置连接池”的代码被调用仅一次,就像创建 bean 一样,以初始化数据源。

这样,您的 getConnection() 将只是:

public Connection getConnection() throws SQLException {
   return dataSource.getConnection();
}

并确保 mysql 中的 wait_timeout 比 hikaricp 中的默认 30[=22] 中的 maxLifetime 设置为分钟 更多 =]分钟

希望对您有所帮助。