杰瑞科技汇

java 数据库连接池 mysql

为什么需要数据库连接池?

在传统的 JDBC 编程中,每次执行 SQL 语句都需要经历以下步骤:

java 数据库连接池 mysql-图1
(图片来源网络,侵删)
  1. 加载驱动
  2. 建立连接 (DriverManager.getConnection())
  3. 执行 SQL
  4. 关闭连接

这个过程非常耗时,尤其是建立连接,如果高并发场景下,大量用户同时请求,频繁地创建和销毁连接会导致:

  • 性能低下:连接创建和销毁的开销巨大。
  • 资源浪费:数据库能同时处理的连接数是有限的,大量连接请求会耗尽数据库资源,导致系统崩溃。

数据库连接池 就是为了解决这个问题而生的,它像一个“连接仓库”,在应用启动时就预先创建好一批数据库连接,并将它们存放在池中,当需要执行 SQL 时,直接从池中获取一个可用连接,用完后再归还给池,而不是销毁,这样就避免了频繁创建和销毁连接的开销,极大地提升了性能。


主流 Java 数据库连接池

目前最主流、性能最优的连接池是 HikariCP,还有一些经典的选择,如 Druid 和 C3P0。

连接池 特点 推荐度
HikariCP 性能极高,代码简洁,稳定可靠,是目前事实上的标准。 ⭐⭐⭐⭐⭐ (首选)
Druid 性能优秀,功能强大,自带监控、统计、防 SQL 注入等功能。 ⭐⭐⭐⭐ (功能首选)
C3P0 老牌连接池,稳定,但性能相对 HikariCP 和 Druid 稍差。 ⭐⭐ (不推荐新项目)

下面我们将重点介绍 HikariCPDruid 的使用方法。

java 数据库连接池 mysql-图2
(图片来源网络,侵删)

准备工作:添加 MySQL 驱动依赖

无论使用哪个连接池,你都需要先添加 MySQL 的 JDBC 驱动依赖,如果你的项目使用 Maven,在 pom.xml 中添加以下依赖:

<!-- MySQL JDBC 驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.28</version> <!-- 建议使用较新版本 -->
</dependency>

使用 HikariCP (推荐)

HikariCP 的配置非常简单,性能极佳。

添加 HikariCP 依赖

pom.xml 中添加:

<!-- HikariCP 连接池 -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>5.0.1</version> <!-- 建议使用较新版本 -->
</dependency>

创建 HikariConfig 配置对象并初始化连接池

HikariCP 提供了两种配置方式:Java 代码配置和配置文件(如 propertiesyml),推荐使用配置文件,更易于管理。

java 数据库连接池 mysql-图3
(图片来源网络,侵删)

使用 properties 文件

  1. src/main/resources 目录下创建 db.properties 文件:

    # 数据库连接信息
    jdbcUrl=jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
    username=root
    password=your_password
    # 连接池配置
    # 最小空闲连接数
    minimum-idle=5
    # 连接池最大连接数
    maximum-pool-size=15
    # 连接在池中最大存活时间,0表示永久存活
    max-lifetime=1800000 # 30分钟
    # 空闲连接最大存活时间,0表示永不丢弃
    idle-timeout=600000  # 10分钟
  2. 在 Java 代码中加载配置并创建连接池:

    import com.zaxxer.hikari.HikariConfig;
    import com.zaxxer.hikari.HikariDataSource;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.io.InputStream;
    import java.util.Properties;
    public class HikariCPUtil {
        private static HikariDataSource dataSource;
        static {
            try (InputStream is = HikariCPUtil.class.getClassLoader().getResourceAsStream("db.properties")) {
                Properties props = new Properties();
                props.load(is);
                HikariConfig config = new HikariConfig();
                config.setJdbcUrl(props.getProperty("jdbcUrl"));
                config.setUsername(props.getProperty("username"));
                config.setPassword(props.getProperty("password"));
                config.setMinimumIdle(Integer.parseInt(props.getProperty("minimum-idle")));
                config.setMaximumPoolSize(Integer.parseInt(props.getProperty("maximum-pool-size")));
                config.setMaxLifetime(Long.parseLong(props.getProperty("max-lifetime")));
                config.setIdleTimeout(Long.parseLong(props.getProperty("idle-timeout")));
                dataSource = new HikariDataSource(config);
            } catch (Exception e) {
                throw new RuntimeException("Failed to initialize HikariCP connection pool", e);
            }
        }
        // 获取连接
        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
        // 关闭连接池 (通常在应用关闭时调用)
        public static void closeDataSource() {
            if (dataSource != null && !dataSource.isClosed()) {
                dataSource.close();
            }
        }
    }

纯 Java 代码配置

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
public class HikariCPUtil_Code {
    private static HikariDataSource dataSource;
    static {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC&characterEncoding=utf8");
        config.setUsername("root");
        config.setPassword("your_password");
        config.setMinimumIdle(5);
        config.setMaximumPoolSize(15);
        config.setMaxLifetime(1800000);
        config.setIdleTimeout(600000);
        dataSource = new HikariDataSource(config);
    }
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
}

使用连接池进行数据库操作

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDao {
    public String findUserNameById(int id) {
        String sql = "SELECT username FROM users WHERE id = ?";
        Connection connection = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        String username = null;
        try {
            // 1. 从连接池获取连接
            connection = HikariCPUtil.getConnection();
            // 2. 创建 PreparedStatement
            pstmt = connection.prepareStatement(sql);
            pstmt.setInt(1, id);
            // 3. 执行查询
            rs = pstmt.executeQuery();
            if (rs.next()) {
                username = rs.getString("username");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            // 4. 关闭资源 (非常重要!)
            // 注意:这里不是关闭连接,而是将连接归还给连接池
            try {
                if (rs != null) rs.close();
                if (pstmt != null) pstmt.close();
                // connection.close(); // 千万不要调用这个!
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return username;
    }
}

使用 Druid

Druid 由阿里巴巴开源,除了高性能外,还提供了强大的监控功能。

添加 Druid 依赖

pom.xml 中添加:

<!-- Druid 连接池 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.16</version> <!-- 建议使用较新版本 -->
</dependency>

创建 DruidDataSource 配置并初始化连接池

同样,推荐使用配置文件。

  1. src/main/resources 目录下创建 db.properties 文件:

    # 数据库连接信息
    jdbcUrl=jdbc:mysql://localhost:3306/your_database?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
    username=root
    password=your_password
    # 连接池配置
    initialSize=5
    minIdle=5
    maxActive=15
    maxWait=60000
    timeBetweenEvictionRunsMillis=60000
    minEvictableIdleTimeMillis=300000
    validationQuery=SELECT 1
    testWhileIdle=true
    testOnBorrow=false
    testOnReturn=false
    poolPreparedStatements=true
    maxPoolPreparedStatementPerConnectionSize=20
  2. 在 Java 代码中加载配置并创建连接池:

    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.pool.DruidDataSourceFactory;
    import javax.sql.DataSource;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.SQLException;
    import java.util.Properties;
    public class DruidUtil {
        private static DataSource dataSource;
        static {
            try (InputStream is = DruidUtil.class.getClassLoader().getResourceAsStream("db.properties")) {
                Properties props = new Properties();
                props.load(is);
                dataSource = DruidDataSourceFactory.createDataSource(props);
            } catch (Exception e) {
                throw new RuntimeException("Failed to initialize Druid connection pool", e);
            }
        }
        public static Connection getConnection() throws SQLException {
            return dataSource.getConnection();
        }
        public static DataSource getDataSource() {
            return dataSource;
        }
        public static void closeDataSource() {
            if (dataSource instanceof DruidDataSource) {
                ((DruidDataSource) dataSource).close();
            }
        }
    }

使用 Druid 的监控功能 (Druid 的特色)

Druid 提供了一个内置的监控页面,非常方便查看 SQL 执行情况、连接池状态等。

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
// 如果使用 Spring Boot,通常通过配置类来注册 Servlet 和 Filter
// 如果是原生 Servlet 项目,可以这样配置
// 监控页面 Servlet
@WebServlet(urlPatterns = "/druid/*", name = "druidStatViewServlet")
public class DruidStatViewServlet extends StatViewServlet {
    // ... 省略
}
// 监控 Filter
@WebFilter(urlPatterns = "/*", filterName = "druidWebStatFilter")
public class DruidWebStatFilter extends WebStatFilter {
    // ... 省略
}

访问 http://yourserver:port/your-app/druid/index.html 即可看到监控页面。


最佳实践与总结

  1. 选择 HikariCP:对于绝大多数新项目,HikariCP 是首选,它的性能是业界标杆,配置简单,稳定可靠。
  2. 单例模式:连接池对象应该是全局唯一的,通常使用静态变量和静态代码块来初始化。
  3. 资源管理Connection, PreparedStatement, ResultSet 等资源必须在使用后关闭,最好在 finally 块中关闭。切记不要调用 connection.close() 来关闭连接,而是要关闭 StatementResultSet,连接会自动归还给池。
  4. 配置调优maximum-pool-size (最大连接数) 和 minimum-idle (最小空闲连接数) 是最重要的两个参数,需要根据你的应用并发量和数据库服务器的承受能力来调整,一个经验法则是:maximum-pool-size = (核心数 * 2) + 有效磁盘数
  5. 整合 Spring/Spring Boot:在实际开发中,我们很少手动创建连接池,通常使用 SpringSpring Boot 框架,它们会自动配置和管理连接池,你只需要在 application.propertiesapplication.yml 文件中配置连接信息即可,框架会自动为你创建并注入 DataSource bean。

在 Spring Boot 中,你只需要配置:

# application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/your_db
spring.datasource.username=root
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# Spring Boot 2.x 默认使用 HikariCP
# 可以通过 spring.datasource.type 指定为其他连接池,如 com.alibaba.druid.pool.DruidDataSource

Spring Boot 会自动根据这些属性创建一个 HikariCP 连接池,并将其注入到需要 @Autowired DataSource dataSource; 的地方,这是最推荐、最简单的方式。

分享:
扫描分享到社交APP
上一篇
下一篇