教程中心 / Java教程 / 连接池

数据库连接池

18分钟 数据库编程

引言:为什么需要连接池?

数据库连接是昂贵的资源。每次应用需要访问数据库时,都需要建立连接,这个过程涉及网络通信、认证等开销。使用连接池可以复用已建立的连接,大大提高性能。

连接池类比:共享单车

没有连接池:每次出门都买一辆新车
- 每次都需要花钱(建立连接)
- 用完就扔(关闭连接)
- 非常浪费

有连接池:使用共享单车
- 预先准备一些车(初始化连接)
- 需要时扫码骑车(获取连接)
- 骑完锁车还回去(归还连接)
- 车可以给别人用(复用)
- 省时省力!

连接池 vs 传统方式

传统方式(每次新建连接)

// 每次请求都创建新连接
for (int i = 0; i < 1000; i++) {
    Connection conn = DriverManager.getConnection(url, user, password);
    // 执行SQL
    conn.close();  // 用完就关闭
}
// 问题:创建1000次连接,非常慢!

连接池方式(复用连接)

// 从连接池获取连接
DataSource dataSource = new HikariDataSource(config);
for (int i = 0; i < 1000; i++) {
    Connection conn = dataSource.getConnection();  // 从池中获取
    // 执行SQL
    conn.close();  // 归还到池中,不是真正关闭
}
// 只需创建少量连接,复用1000次!

常用连接池对比

连接池性能功能维护适用场景
HikariCP最高基础活跃高性能需求,Spring Boot默认
Druid中高丰富(监控、过滤)活跃(阿里巴巴)需要监控功能
C3P0较低丰富较少老项目兼容
DBCP较低基础较少Apache项目

HikariCP(推荐)

HikariCP是Spring Boot 2.0+的默认连接池,以高性能著称。

Maven依赖


    com.zaxxer
    HikariCP
    4.0.3

基本配置

HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
config.setDriverClassName("com.mysql.cj.jdbc.Driver");

// 连接池大小
config.setMaximumPoolSize(10);      // 最大连接数
config.setMinimumIdle(5);           // 最小空闲连接

// 连接超时
config.setConnectionTimeout(30000); // 获取连接超时(毫秒)
config.setIdleTimeout(600000);      // 空闲连接超时
config.setMaxLifetime(1800000);     // 连接最大生命周期

DataSource dataSource = new HikariDataSource(config);

// 使用
Connection conn = dataSource.getConnection();
try {
    // 执行SQL
} finally {
    conn.close();  // 归还到连接池
}

HikariCP配置参数详解

参数说明默认值
maximumPoolSize最大连接数10
minimumIdle最小空闲连接等于maximumPoolSize
connectionTimeout获取连接超时30000ms
idleTimeout空闲连接存活时间600000ms
maxLifetime连接最大生命周期1800000ms
poolName连接池名称HikariPool-1

Druid(阿里巴巴)

Druid提供强大的监控和SQL过滤功能。

Maven依赖


    com.alibaba
    druid
    1.2.16

基本配置

DruidDataSource dataSource = new DruidDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");

// 连接池大小
dataSource.setInitialSize(5);     // 初始连接数
dataSource.setMaxActive(20);      // 最大连接数
dataSource.setMinIdle(5);        // 最小空闲

// 连接验证
dataSource.setValidationQuery("SELECT 1");  // 验证SQL
dataSource.setTestWhileIdle(true);          // 空闲时验证
dataSource.setTestOnBorrow(false);          // 借用时验证
dataSource.setTestOnReturn(false);          // 归还时验证

// 连接超时
dataSource.setMaxWait(60000);  // 获取连接最大等待时间

// 启用监控统计
dataSource.setFilters("stat,wall");

Druid监控配置

// Web监控页面
servletRegistrationBean.setServlet(new StatViewServlet());
servletRegistrationBean.addUrlMappings("/druid/*");

// 监控过滤
filterRegistrationBean.setFilter(new WebStatFilter());
filterRegistrationBean.addUrlPatterns("/*");
filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg");

// 通过 http://localhost:8080/druid 访问监控页面

C3P0(老牌连接池)


    com.mchange
    c3p0
    0.9.5.5
ComboPooledDataSource cpds = new ComboPooledDataSource();
cpds.setDriverClass("com.mysql.cj.jdbc.Driver");
cpds.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
cpds.setUser("root");
cpds.setPassword("password");

cpds.setMinPoolSize(5);      // 最小连接数
cpds.setMaxPoolSize(20);     // 最大连接数
cpds.setAcquireIncrement(3); // 获取新连接时增长数

// 连接验证
cpds.setTestConnectionOnCheckout(true);  // 归还时验证
cpds.setIdleConnectionTestPeriod(300);   // 空闲测试周期(秒)

DBCP(Apache Commons)


    org.apache.commons
    commons-dbcp2
    2.9.0
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("root");
dataSource.setPassword("password");
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");

dataSource.setInitialSize(5);     // 初始连接数
dataSource.setMaxTotal(20);      // 最大连接数
dataSource.setMaxIdle(10);       // 最大空闲
dataSource.setMinIdle(5);        // 最小空闲

// 连接验证
dataSource.setValidationQuery("SELECT 1");
dataSource.setTestOnBorrow(true);
dataSource.setTestWhileIdle(true);

Spring Boot配置

application.yml配置HikariCP(默认)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver

    # HikariCP配置
    hikari:
      maximum-pool-size: 10
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000
      pool-name: MyHikariPool

切换到Druid

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      initial-size: 5
      max-active: 20
      min-idle: 5
      stat-view-enabled: true  # 开启监控

连接池参数调优

如何设置连接数?

// 公式:连接数 = ((核心数 * 2) + 磁盘数)
// 例如:4核CPU,1个SSD -> 最佳连接数约 9-10

// 考虑因素:
// 1. 数据库服务器配置
// 2. 应用服务器配置
// 3. 业务特性(长事务还是短事务)

参数设置建议

maximumPoolSize:不要设置过大,通常10-20足够
minimumIdle:设置为maximumPoolSize的一半左右
connectionTimeout:根据业务容忍度设置,通常30秒
idleTimeout:根据数据库服务器超时设置

连接池使用示例

public class UserDAO {
    private DataSource dataSource;

    public UserDAO(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public User findById(Long id) {
        String sql = "SELECT * FROM users WHERE id = ?";

        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setLong(1, id);
            ResultSet rs = pstmt.executeQuery();

            if (rs.next()) {
                User user = new User();
                user.setId(rs.getLong("id"));
                user.setName(rs.getString("name"));
                return user;
            }
        } catch (SQLException e) {
            throw new RuntimeException("查询用户失败", e);
        }
        return null;
    }

    public void insert(User user) {
        String sql = "INSERT INTO users (name, email) VALUES (?, ?)";

        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(sql)) {

            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getEmail());
            pstmt.executeUpdate();

        } catch (SQLException e) {
            throw new RuntimeException("插入用户失败", e);
        }
    }
}

连接池注意事项

常见问题:

  • 连接泄露:获取连接后忘记close(),导致连接耗尽
  • 连接超时:maxWait设置过短,高并发时等待超时
  • 连接池过大:占用过多数据库连接,影响数据库性能

小结

  • 连接池复用连接,避免频繁创建/关闭连接的开销
  • HikariCP:性能最高,Spring Boot默认推荐
  • Druid:功能丰富,支持监控和SQL过滤
  • C3P0/DBCP:老牌连接池,现在较少使用
  • 连接数不是越大越好,通常10-20足够
  • 使用try-with-resources确保连接正确归还
☕ Java 在线代码编辑器
📝 运行结果