应用程序建立的数据库连接,也就是非交互式连接 是什么时候开始的?什么时候结束?连接结束后 会影响应用程序操作db失败吗? 还有就是如果连接关闭了 会立马重新建立新的连接吗?

问题:

应用程序建立的数据库连接,也就是非交互式连接 是什么时候开始的?什么时候结束?连接结束后 会影响应用程序操作db失败吗? 还有就是如果连接关闭了 会立马重新建立新的连接吗?

 

解答:

我们讨论的是应用程序通过连接池(如HikariCP)与MySQL建立的连接。这些连接是非交互式连接。

  1. 连接何时开始?当应用程序从连接池请求一个连接时,如果连接池中没有可用的空闲连接,则会新建一个连接。这个连接建立的时刻就是开始。

  2. 连接何时结束?当连接被关闭(返回到连接池)或者因为超时(如wait_timeout)被服务器关闭时,连接结束。

  3. 连接结束后,如果应用程序尝试使用这个已经关闭的连接来操作数据库,就会失败,会抛出异常(如CommunicationsException)。

  4. 如果连接被关闭,应用程序再次请求连接时,连接池会尝试重新建立新的连接(如果连接池中有空闲连接且未关闭,则使用空闲连接;如果没有,则新建)。

但是,注意连接池通常会有一些机制来检测连接的有效性,比如在将连接交给应用程序之前,可能会执行一个测试查询(如SELECT 1)来验证连接是否还有效。如果连接已经无效,连接池会丢弃这个连接并新建一个。

所以,当MySQL服务器因为wait_timeout关闭了连接,而连接池并不知道,如果连接池试图将这个已经关闭的连接分配给应用程序,应用程序就会操作失败。为了避免这种情况,连接池通常会有验证机制。

下面详细解释:

连接的生命周期:

  • 创建:当连接池需要新连接时,会创建新的数据库连接。

  • 使用:应用程序从连接池获取连接,执行SQL,然后归还连接。

  • 关闭:连接池可以主动关闭连接(例如,连接空闲时间超过连接池设置的空闲超时,或者连接存活时间超过最大生命周期),或者因为数据库服务器关闭了连接(如wait_timeout)而被动关闭。

当数据库服务器因为wait_timeout关闭了连接,这个连接在连接池中就变成了一个无效的连接。如果连接池没有验证机制,应用程序拿到这个无效连接就会报错。

因此,连接池通常会有以下配置来避免这种情况:

  • 验证查询(validationQuery):例如,一个简单的SQL语句(如SELECT 1)用来在连接被取出前验证其有效性。

  • 测试连接的有效性(testOnBorrow):在将连接交给应用程序之前,执行验证查询。如果验证失败,则丢弃该连接并尝试获取另一个连接。

  • 空闲连接验证(testWhileIdle):在连接空闲时,定期执行验证查询,确保空闲连接仍然是有效的。

  • 最大生命周期(maxLifetime):连接在连接池中的最长存活时间,即使数据库的wait_timeout很长,连接池也会在达到最大生命周期后关闭连接,然后新建连接,这样可以避免因为数据库连接长时间空闲而被服务器关闭。

所以,如果连接关闭了,应用程序操作DB会失败,但连接池机制可以尽量减少这种情况,并在连接失效时重新建立新的连接。

总结:

  • 连接由连接池创建,并在需要时分配给应用程序。

  • 连接结束的原因包括:主动关闭(连接池因超时或生命周期结束)或被动关闭(数据库服务器因超时关闭)。

  • 连接结束后,如果应用程序使用该连接,会失败。

  • 连接池通过验证机制可以检测到无效连接并重新建立新连接,从而避免应用程序失败。

但是,如果连接池没有配置验证,或者验证不够及时,应用程序可能会拿到已经关闭的连接,从而导致操作失败。因此,合理配置连接池的参数非常重要。

数据库连接的生命周期

连接的开始和结束

连接开始:当应用程序从连接池获取连接或直接创建新连接时

// 连接开始的时刻
Connection conn = dataSource.getConnection(); // 连接建立
// 或者
Connection conn = DriverManager.getConnection(url, user, password);

连接结束:

  1. 主动关闭:应用程序显式调用conn.close()

  2. 超时关闭:MySQL服务器的wait_timeout到达

  3. 连接池回收:连接池因空闲超时或最大生命周期回收连接

  4. 网络异常:网络中断或数据库服务器重启

连接关闭对应用程序的影响

会影响应用程序操作DB失败吗? - 会的!

try {
    // 获取连接
    Connection conn = dataSource.getConnection();
    
    // 假设这个连接在接下来10分钟空闲后被MySQL关闭
    Thread.sleep(600000);
    
    // 尝试使用已关闭的连接 - 会失败!
    Statement stmt = conn.createStatement(); // 可能抛出异常
    ResultSet rs = stmt.executeQuery("SELECT * FROM users");
    
} catch (SQLException e) {
    // 捕获类似:Communications link failure 的异常
    System.out.println("操作失败: " + e.getMessage());
}

连接池的工作机制

连接关闭后的重新建立

连接关闭后会立即重新建立吗? - 取决于连接池的配置和实现

// HikariCP 配置示例
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("user");
config.setPassword("password");

// 关键配置参数
config.setMinimumIdle(5);           // 最小空闲连接数
config.setMaximumPoolSize(20);      // 最大连接数
config.setIdleTimeout(300000);      // 连接空闲超时(5分钟)
config.setMaxLifetime(1800000);     // 连接最大生命周期(30分钟)
config.setConnectionTimeout(30000); // 获取连接超时时间
config.setValidationTimeout(5000);  // 验证连接超时

// 连接有效性验证
config.setConnectionTestQuery("SELECT 1");
config.setLeakDetectionThreshold(60000);

HikariDataSource dataSource = new HikariDataSource(config);

连接池的自动恢复机制

// 当应用程序请求连接时,连接池的行为:
public Connection getConnection() throws SQLException {
    // 1. 首先尝试从空闲连接池获取
    Connection conn = borrowFromIdlePool();
    
    if (conn != null) {
        // 2. 验证连接是否有效
        if (!isConnectionValid(conn)) {
            // 连接已失效,关闭并创建新连接
            closeConnection(conn);
            conn = createNewConnection();
        }
        return conn;
    }
    
    // 3. 如果没有空闲连接且未达上限,创建新连接
    if (currentPoolSize < maxPoolSize) {
        return createNewConnection();
    }
    
    // 4. 等待其他连接释放或超时
    return waitForAvailableConnection();
}

实际场景分析

场景1:连接被MySQL关闭后的恢复

// 应用程序代码
@Service
public class UserService {
    
    @Autowired
    private DataSource dataSource;
    
    public User getUserById(Long id) {
        // 获取连接时,连接池会检查连接有效性
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
            
            stmt.setLong(1, id);
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    return mapResultSetToUser(rs);
                }
            }
            
        } catch (SQLException e) {
            // 如果连接失效,这里会捕获异常
            // 但连接池会在下次请求时自动创建新连接
            logger.error("查询用户失败", e);
            throw new RuntimeException("数据库操作失败", e);
        }
        return null;
    }
}

场景2:连接池的完整工作流程

image

 

最佳实践配置

连接池配置建议

# HikariCP 推荐配置
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=300000          # 5分钟 < MySQL的wait_timeout
spring.datasource.hikari.max-lifetime=1800000         # 30分钟
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.validation-timeout=5000
spring.datasource.hikari.leak-detection-threshold=60000

# MySQL 服务器配置建议
# wait_timeout = 600        # 10分钟
# interactive_timeout = 1800 # 30分钟

监控和诊断

@Component
public class ConnectionPoolMonitor {
    
    @Autowired
    private HikariDataSource dataSource;
    
    @Scheduled(fixedRate = 60000) // 每分钟监控一次
    public void monitorConnectionPool() {
        HikariPoolMXBean pool = dataSource.getHikariPoolMXBean();
        
        logger.info("连接池状态 - 活跃连接: {}, 空闲连接: {}, 等待线程: {}, 总连接: {}",
            pool.getActiveConnections(),
            pool.getIdleConnections(),
            pool.getThreadsAwaitingConnection(),
            pool.getTotalConnections());
    }
}

 

总结

  1. 连接开始:应用程序从连接池获取连接时

  2. 连接结束:主动关闭、超时关闭、连接池回收或网络异常

  3. 影响:连接关闭后继续使用会导致操作失败

  4. 重新建立:连接池会自动检测失效连接并在下次请求时创建新连接

  5. 关键:合理配置连接池参数,确保连接有效性检查和自动恢复机制正常工作

通过正确的连接池配置,应用程序可以透明地处理连接的失效和重建,保证系统的稳定性和可靠性。

posted @ 2025-11-14 18:35  苹果芒  阅读(0)  评论(0)    收藏  举报