📖 数据密集型设计

连接池与连接复用

深入探讨数据库连接池原理与连接管理策略

一、连接池概述

数据库连接是一种昂贵的资源,创建和销毁连接会消耗大量时间和资源。连接池通过预先创建一组连接,供应用程序复用,显著提升性能。

二、连接池原理

2.1 连接池架构

graph TD A[应用程序] --> B[连接池管理器] B --> C[连接池] C --> D[空闲连接] C --> E[活跃连接] D --> D1[连接1] D --> D2[连接2] D --> D3[连接3] E --> E1[连接4] E --> E2[连接5] B --> F{请求连接} F -->|有空闲| G[分配空闲连接] F -->|无空闲| H{池未满} H -->|是| I[创建新连接] H -->|否| J[等待] B --> K[释放连接] K --> L[归还到空闲池]

2.2 连接池核心组件

组件 职责 实现要点
连接池管理器 管理连接的分配和回收 线程安全、超时控制
空闲连接池 存储可用连接 队列管理、连接验证
活跃连接池 存储正在使用的连接 引用计数、超时检测
连接工厂 创建新连接 连接字符串、初始化

三、连接池工作流程

3.1 获取连接流程

flowchart TD A[获取连接] --> B{空闲池有连接} B -->|是| C[取出连接] C --> D{连接有效} D -->|是| E[分配连接] D -->|否| F[销毁连接] F --> B B -->|否| G{池未满} G -->|是| H[创建新连接] H --> I[加入活跃池] I --> E G -->|否| J{等待超时} J -->|是| K[抛出异常] J -->|否| L[等待] L --> B

3.2 释放连接流程

flowchart TD A[释放连接] --> B{连接有效} B -->|是| C{池未满} C -->|是| D[归还到空闲池] C -->|否| E[销毁连接] B -->|否| F[销毁连接] D --> G[重置连接状态] G --> H[等待下次使用]

四、连接池实现

4.1 简单连接池实现

public class ConnectionPool
{
    private readonly Queue<DbConnection> _idleConnections = new Queue<DbConnection>();
    private readonly HashSet<DbConnection> _activeConnections = new HashSet<DbConnection>();
    private readonly string _connectionString;
    private readonly int _maxPoolSize;
    private readonly object _lock = new object();
    
    public ConnectionPool(string connectionString, int maxPoolSize = 100)
    {
        _connectionString = connectionString;
        _maxPoolSize = maxPoolSize;
        InitializePool();
    }
    
    private void InitializePool()
    {
        for (int i = 0; i < 10; i++)
        {
            _idleConnections.Enqueue(CreateConnection());
        }
    }
    
    private DbConnection CreateConnection()
    {
        var connection = new SqlConnection(_connectionString);
        connection.Open();
        return connection;
    }
    
    public DbConnection GetConnection()
    {
        lock (_lock)
        {
            if (_idleConnections.Count > 0)
            {
                var connection = _idleConnections.Dequeue();
                if (connection.State == ConnectionState.Open)
                {
                    _activeConnections.Add(connection);
                    return connection;
                }
                connection.Dispose();
            }
            
            if (_activeConnections.Count < _maxPoolSize)
            {
                var connection = CreateConnection();
                _activeConnections.Add(connection);
                return connection;
            }
            
            throw new InvalidOperationException("连接池已满");
        }
    }
    
    public void ReleaseConnection(DbConnection connection)
    {
        lock (_lock)
        {
            if (_activeConnections.Remove(connection))
            {
                if (_idleConnections.Count < _maxPoolSize)
                {
                    _idleConnections.Enqueue(connection);
                }
                else
                {
                    connection.Dispose();
                }
            }
        }
    }
}

4.2 使用连接池

var pool = new ConnectionPool("Server=localhost;Database=app;", 100);

using (var connection = pool.GetConnection())
{
    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT * FROM users";
        using (var reader = command.ExecuteReader())
        {
            while (reader.Read())
            {
                Console.WriteLine(reader["username"]);
            }
        }
    }
}

pool.ReleaseConnection(connection);

五、连接池配置

5.1 关键配置参数

参数 说明 推荐值
maxPoolSize 最大连接数 100-200
minPoolSize 最小空闲连接数 10-20
connectionTimeout 连接超时时间 15-30秒
idleTimeout 空闲连接超时时间 300秒
maxIdleTime 最大空闲时间 600秒

5.2 ADO.NET连接池配置

// 连接字符串配置
"Server=localhost;Database=app;User Id=sa;Password=123456;" +
"Max Pool Size=100;Min Pool Size=10;Connection Timeout=15;" +
"Idle Timeout=300;Connection Lifetime=600;"

// 禁用连接池
"Server=localhost;Database=app;Pooling=false;"

// 清空连接池
SqlConnection.ClearPool(connection);
SqlConnection.ClearAllPools();

5.3 JDBC连接池配置

// HikariCP配置
spring.datasource.hikari.maximum-pool-size=100
spring.datasource.hikari.minimum-idle=10
spring.datasource.hikari.connection-timeout=15000
spring.datasource.hikari.idle-timeout=300000
spring.datasource.hikari.max-lifetime=600000

// Apache Commons DBCP配置
BasicDataSource dataSource = new BasicDataSource();
dataSource.setMaxTotal(100);
dataSource.setMinIdle(10);
dataSource.setMaxWaitMillis(15000);

六、连接池监控

6.1 监控指标

public class ConnectionPoolMonitor
{
    public PoolMetrics GetMetrics(ConnectionPool pool)
    {
        return new PoolMetrics
        {
            TotalConnections = pool.TotalConnections,
            ActiveConnections = pool.ActiveConnections,
            IdleConnections = pool.IdleConnections,
            MaxPoolSize = pool.MaxPoolSize,
            MinPoolSize = pool.MinPoolSize,
            WaitCount = pool.WaitCount,
            WaitTime = pool.WaitTime
        };
    }
    
    public void LogMetrics(PoolMetrics metrics)
    {
        _logger.LogInformation($"连接池状态: 总数={metrics.TotalConnections}, " +
            $"活跃={metrics.ActiveConnections}, 空闲={metrics.IdleConnections}, " +
            $"等待次数={metrics.WaitCount}, 平均等待时间={metrics.WaitTime}ms");
    }
}

public class PoolMetrics
{
    public int TotalConnections { get; set; }
    public int ActiveConnections { get; set; }
    public int IdleConnections { get; set; }
    public int MaxPoolSize { get; set; }
    public int MinPoolSize { get; set; }
    public long WaitCount { get; set; }
    public double WaitTime { get; set; }
}

6.2 监控告警

public class ConnectionPoolAlertService
{
    public void CheckAndAlert(PoolMetrics metrics)
    {
        var usageRate = (double)metrics.ActiveConnections / metrics.MaxPoolSize;
        
        if (usageRate > 0.9)
        {
            _alertService.SendAlert("连接池使用率过高", 
                $"使用率: {usageRate:P}, 活跃连接: {metrics.ActiveConnections}");
        }
        
        if (metrics.WaitCount > 100)
        {
            _alertService.SendAlert("连接池等待次数过多", 
                $"等待次数: {metrics.WaitCount}, 平均等待时间: {metrics.WaitTime}ms");
        }
        
        if (metrics.IdleConnections == 0)
        {
            _alertService.SendAlert("连接池无空闲连接", 
                $"活跃连接: {metrics.ActiveConnections}, 最大连接数: {metrics.MaxPoolSize}");
        }
    }
}

七、连接池最佳实践

7.1 合理设置最大连接数

最大连接数不是越大越好,需要考虑数据库服务器的承载能力:

graph TD A[连接数过少] --> B[请求排队] B --> C[响应延迟] D[连接数过多] --> E[资源竞争] E --> F[性能下降] G[合理连接数] --> H[最佳性能]

7.2 及时释放连接

// 正确方式:使用using语句自动释放连接
using (var connection = pool.GetConnection())
{
    // 使用连接
}

// 错误方式:忘记释放连接
var connection = pool.GetConnection();
// 使用连接但忘记释放
// 连接泄漏!

7.3 连接验证

public bool ValidateConnection(DbConnection connection)
{
    try
    {
        if (connection.State != ConnectionState.Open)
            return false;
        
        using (var command = connection.CreateCommand())
        {
            command.CommandText = "SELECT 1";
            command.ExecuteScalar();
            return true;
        }
    }
    catch
    {
        return false;
    }
}

7.4 连接池预热

public void WarmUpPool(int connections)
{
    var warmUpConnections = new List<DbConnection>();
    
    for (int i = 0; i < connections; i++)
    {
        warmUpConnections.Add(_pool.GetConnection());
    }
    
    foreach (var connection in warmUpConnections)
    {
        _pool.ReleaseConnection(connection);
    }
}

7.5 连接池隔离

public class ConnectionPoolManager
{
    private readonly Dictionary<string, ConnectionPool> _pools = new Dictionary<string, ConnectionPool>();
    
    public ConnectionPool GetPool(string poolName)
    {
        if (!_pools.TryGetValue(poolName, out var pool))
        {
            pool = new ConnectionPool(GetConnectionString(poolName));
            _pools[poolName] = pool;
        }
        return pool;
    }
}

八、连接池问题排查

8.1 连接泄漏排查

-- 查看MySQL连接状态
SHOW PROCESSLIST;

-- 查看连接数
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';

-- 查看连接池状态(HikariCP)
SELECT * FROM information_schema.processlist 
WHERE USER = 'app_user';

8.2 连接超时排查

// 启用连接池日志
spring.datasource.hikari.leak-detection-threshold=60000
spring.datasource.hikari.log-leak-detection=true

// 监控连接等待时间
poolMetrics.WaitTime > 500ms // 需要关注

九、总结

连接池是数据库性能优化的重要手段,通过复用连接减少连接创建开销。合理配置连接池参数、及时释放连接、监控连接池状态,能够确保数据库连接的高效利用,提升系统整体性能。