1. 概述

连接池(Connection Pooling)是数据访问领域一个广为人知的设计模式,核心目标是降低数据库连接创建与销毁带来的性能开销

说白了,连接池本质上就是一个数据库连接的缓存机制,通过复用已有连接,避免频繁建立和断开连接,从而显著提升应用性能。

本文会先介绍几个主流的 Java 连接池框架,然后手搓一个简易版连接池,帮助你理解其底层原理。✅


2. 为什么需要连接池?

这个问题看似废话,但你真搞懂了吗?

我们先看一次典型的数据库连接生命周期:

  1. 通过数据库驱动建立连接
  2. 打开 TCP 套接字用于读写
  3. 通过套接字传输数据
  4. 关闭连接
  5. 关闭套接字

⚠️ 关键点来了:数据库连接是一个高开销操作,尤其是网络握手、身份认证这些步骤,耗时动辄几十甚至上百毫秒。

如果每次操作都新建连接,那你的应用性能基本就废了——大量时间浪费在“准备阶段”,而不是真正的数据处理。

连接池的出现就是为了解决这个问题:把用过的连接“回收”再利用,避免重复“建-连-断”这一整套流程。简单粗暴,但效果拔群。


3. 主流 JDBC 连接池框架

从实用角度讲,自己造轮子基本没必要——已经有多个成熟、稳定、高性能的连接池框架可供选择。

但从学习角度,了解它们的用法有助于我们后续自己实现。先来看看几个主流选手:

3.1. Apache Commons DBCP

Apache 提供的 DBCP(Database Connection Pool)是老牌连接池,功能完整,适合传统项目。

使用方式也很简单,封装一个数据源类即可:

public class DBCPDataSource {
    
    private static BasicDataSource ds = new BasicDataSource();
    
    static {
        ds.setUrl("jdbc:h2:mem:testdb");
        ds.setUsername("sa");
        ds.setPassword("sa");
        ds.setMinIdle(5);
        ds.setMaxIdle(10);
        ds.setMaxOpenPreparedStatements(100);
    }
    
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    
    private DBCPDataSource(){ }
}

获取连接也是一行搞定:

Connection con = DBCPDataSource.getConnection();

✅ 适合学习,但性能不是最强,线上高并发场景建议换更轻量的。


3.2. HikariCP

HikariCP 是目前性能最强、最推荐的连接池,由 Brett Wooldridge 开发,号称“闪电般快速”。

它的设计极为精简,几乎没有额外开销,是 Spring Boot 2+ 的默认连接池。

配置示例:

public class HikariCPDataSource {
    
    private static HikariConfig config = new HikariConfig();
    private static HikariDataSource ds;
    
    static {
        config.setJdbcUrl("jdbc:h2:mem:testdb");
        config.setUsername("sa");
        config.setPassword("sa");
        config.addDataSourceProperty("cachePrepStmts", "true");
        config.addDataSourceProperty("prepStmtCacheSize", "250");
        config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
        ds = new HikariDataSource(config);
    }
    
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
    
    private HikariCPDataSource(){}
}

获取连接方式一致:

Connection con = HikariCPDataSource.getConnection();

✅ 推荐所有新项目直接用 HikariCP,性能和稳定性都经受过大规模验证。


3.3. C3P0

C3P0 是一个老牌连接池,功能丰富,支持自动重连、连接测试等特性,但性能和配置复杂度不如 HikariCP。

示例代码:

public class C3p0DataSource {

    private static ComboPooledDataSource cpds = new ComboPooledDataSource();

    static {
        try {
            cpds.setDriverClass("org.h2.Driver");
            cpds.setJdbcUrl("jdbc:h2:mem:testdb");
            cpds.setUser("sa");
            cpds.setPassword("sa");
        } catch (PropertyVetoException e) {
            throw new RuntimeException("C3P0 配置失败", e);
        }
    }
    
    public static Connection getConnection() throws SQLException {
        return cpds.getConnection();
    }
    
    private C3p0DataSource(){}
}

使用方式相同:

Connection con = C3p0DataSource.getConnection();

⚠️ 老项目可能还在用,新项目不推荐,HikariCP 更优。


4. 手写一个简易连接池

光用现成的不够过瘾?来,我们自己实现一个最简版连接池,理解其核心思想。

4.1. 定义接口

先定义一个 ConnectionPool 接口,明确核心行为:

public interface ConnectionPool {
    Connection getConnection();
    boolean releaseConnection(Connection connection);
    String getUrl();
    String getUser();
    String getPassword();
}

4.2. 基础实现

实现类 BasicConnectionPool 初始化 10 个连接,放入池中:

public class BasicConnectionPool implements ConnectionPool {

    private String url;
    private String user;
    private String password;
    private List<Connection> connectionPool;
    private List<Connection> usedConnections = new ArrayList<>();
    private static int INITIAL_POOL_SIZE = 10;
    
    public static BasicConnectionPool create(
        String url, String user, 
        String password) throws SQLException {
 
        List<Connection> pool = new ArrayList<>(INITIAL_POOL_SIZE);
        for (int i = 0; i < INITIAL_POOL_SIZE; i++) {
            pool.add(createConnection(url, user, password));
        }
        return new BasicConnectionPool(url, user, password, pool);
    }
    
    // 构造函数略
    
    @Override
    public Connection getConnection() {
        Connection connection = connectionPool
          .remove(connectionPool.size() - 1);
        usedConnections.add(connection);
        return connection;
    }
    
    @Override
    public boolean releaseConnection(Connection connection) {
        connectionPool.add(connection);
        return usedConnections.remove(connection);
    }
    
    private static Connection createConnection(
        String url, String user, String password) 
        throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
    
    public int getSize() {
        return connectionPool.size() + usedConnections.size();
    }

    // getter 方法略
}

关键点解析

  • 连接池启动时预创建 10 个连接
  • getConnection() 从池中取出一个连接,移到 usedConnections 列表
  • releaseConnection() 将用完的连接“归还”到池中,不真正关闭
  • 整个过程不调用 connection.close(),实现复用

⚠️ 注意:这个实现是非线程安全的,仅用于理解原理,生产环境切勿直接使用。


5. 使用示例

测试一下我们的连接池能否正常工作:

@Test
public void whenCalledgetConnection_thenCorrect() {
    ConnectionPool connectionPool = BasicConnectionPool
      .create("jdbc:h2:mem:testdb", "sa", "sa");
 
    assertTrue(connectionPool.getConnection().isValid(1));
}

跑通就说明基本功能 OK。


6. 可优化方向

虽然简陋,但可以在此基础上扩展更多生产级特性:

6.1. 支持最大连接数

当池中无可用连接且未达上限时,动态扩容:

@Override
public Connection getConnection() throws SQLException {
    if (connectionPool.isEmpty()) {
        if (usedConnections.size() < MAX_POOL_SIZE) {
            connectionPool.add(createConnection(url, user, password));
        } else {
            throw new RuntimeException("连接池已满,无可用连接!");
        }
    }

    Connection connection = connectionPool.remove(connectionPool.size() - 1);

    // 检查连接是否有效,避免返回失效连接
    if(!connection.isValid(MAX_TIMEOUT)){
        connection = createConnection(url, user, password);
    }

    usedConnections.add(connection);
    return connection;
}

⚠️ 注意:接口需同步修改为抛出 SQLException


6.2. 添加关闭机制

优雅关闭连接池,释放所有资源:

public void shutdown() throws SQLException {
    usedConnections.forEach(this::releaseConnection);
    for (Connection c : connectionPool) {
        c.close();
    }
    connectionPool.clear();
}

6.3. 其他生产级特性(可选)

  • ✅ 连接存活检测(testOnBorrow / testOnReturn)
  • ✅ 预编译语句缓存(PreparedStatement Pooling)
  • ✅ 连接泄漏检测
  • ✅ 线程安全(使用 synchronizedConcurrentLinkedQueue
  • ✅ JMX 监控支持

但本文为了简洁,这些就不展开了。


7. 总结

  • 连接池的核心是复用连接,避免频繁创建销毁
  • 生产环境优先使用 HikariCP,性能最优
  • DBCP 和 C3P0 可作为备选,但不推荐新项目使用
  • 手写连接池有助于理解原理,但别在生产环境“炫技”
  • 关键是理解:获取连接 ≠ 新建连接,释放连接 ≠ 关闭连接

所有示例代码已托管至 GitHub:https://github.com/yourname/java-connection-pool-demo


原始标题:A Simple Guide to Connection Pooling in Java