杰瑞科技汇

Java连接MySQL,连接池如何高效管理连接?

使用连接池是 Java 应用连接数据库的标准实践,它能极大地提升性能和稳定性,下面我将从 “为什么用”、“用什么”“怎么用”,为你提供一个完整且详细的指南。

Java连接MySQL,连接池如何高效管理连接?-图1
(图片来源网络,侵删)

为什么需要使用连接池?(连接池的优势)

想象一下没有连接池的场景:

  1. 每次请求都创建新连接:当一个用户请求需要访问数据库时,程序就要向 MySQL 服务器发起一个连接请求,这个过程包括 TCP 三次握手、验证用户信息等,是比较耗时的。
  2. 用完就关闭:请求处理完毕后,连接被关闭,下一个用户请求再来时,又要重复一遍上述的耗时过程。
  3. 资源浪费:频繁地创建和销毁连接,会消耗大量的 CPU 和内存资源,并且会给数据库服务器带来巨大的压力。

连接池(Connection Pool) 就是为了解决这些问题而生的,它的工作模式就像一个“连接的停车场”:

  1. 初始化:在应用启动时,连接池会预先创建一定数量的数据库连接,并将它们放入“池”中待命。
  2. 获取连接:当你的 Java 代码需要一个数据库连接时,不是去创建一个新的,而是从连接池中“借”一个(获取一个),这个过程非常快。
  3. 使用连接:使用这个连接来执行 SQL 语句、查询数据等。
  4. 归还连接:操作完成后,你不需要关闭这个连接(connection.close()),而是将它“还”给连接池,连接池会对其进行检查和重置,然后放回池中,供下一个请求使用。
  5. 销毁连接:当应用关闭时,连接池会统一关闭池中所有的连接。

核心优势:

  • 性能提升:避免了频繁创建和销毁连接的开销,响应速度更快。
  • 资源复用:连接得到了复用,减少了对数据库的压力。
  • 控制并发:可以限制应用的最大连接数,防止因连接过多而导致数据库崩溃。
  • 管理方便:可以监控连接的使用情况,如活动连接数、空闲连接数等。

主流的 Java 连接池技术

目前市面上最流行、性能最好的连接池主要有以下几个:

Java连接MySQL,连接池如何高效管理连接?-图2
(图片来源网络,侵删)
连接池 特点 官方推荐
HikariCP 性能极高,代码简洁,稳定可靠,是目前公认的最快的连接池。 Spring Boot 2.x 及以上版本的默认连接池。
Druid 性能优秀,功能非常强大,除了连接池功能,还提供了强大的监控功能(如 SQL 监控、Web 监控面板)。 国产优秀开源项目,在国内使用非常广泛。
DBCP2 Apache 出品,稳定可靠,但性能相比 HikariCP 稍弱。 配合 Tomcat 等服务器使用较多。

推荐选择:

  • 追求极致性能:首选 HikariCP
  • 需要强大的监控和管理功能:首选 Druid
  • 项目老旧或特定依赖:可能使用 DBCP2。

本指南将以 HikariCP(推荐首选)和 Druid(功能强大)为例进行讲解。


准备工作

在编写代码之前,请确保你已经:

  1. 安装了 MySQL 数据库,并有一个可用的数据库。

    Java连接MySQL,连接池如何高效管理连接?-图3
    (图片来源网络,侵删)
  2. 创建了数据库和用户,我们创建一个名为 test_db 的数据库,并创建一个用户 java_user,密码为 password123

    CREATE DATABASE test_db;
    CREATE USER 'java_user'@'localhost' IDENTIFIED BY 'password123';
    GRANT ALL PRIVILEGES ON test_db.* TO 'java_user'@'localhost';
    FLUSH PRIVILEGES;
  3. 添加 JDBC 驱动依赖,你需要将 MySQL 的 JDBC 驱动(mysql-connector-j)的 jar 包添加到你的项目中。

    Maven (pom.xml)

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.0.33</version> <!-- 建议使用较新版本 -->
    </dependency>

    Gradle (build.gradle)

    implementation 'com.mysql:mysql-connector-j:8.0.33'

实现步骤(以 HikariCP 为例)

HikariCP 的配置非常简单,主要通过一个 Properties 对象来完成。

步骤 1:创建 HikariConfig 对象并配置参数

这是连接池的核心配置,你需要提供数据库连接的基本信息。

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class HikariCPExample {
    public static void main(String[] args) {
        // 1. 创建 HikariConfig 配置对象
        HikariConfig config = new HikariConfig();
        // 2. 配置数据库连接信息 (非常重要)
        // JDBC URL 格式: jdbc:mysql://[主机名]:[端口]/[数据库名]?[参数]
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC");
        config.setUsername("java_user");
        config.setPassword("password123");
        // 3. (可选但推荐) 配置连接池参数
        config.setDriverClassName("com.mysql.cj.jdbc.Driver"); // 对于新版本驱动,可以不设置,HikariCP会自动检测
        config.setPoolName("MyHikariPool"); // 连接池名称
        // 连接池中维护的最小空闲连接数
        config.setMinimumIdle(5);
        // 连接池中最大连接数
        config.setMaximumPoolSize(15);
        // 一个连接在池中最大空闲时间,超时后将被回收 (单位:毫秒)
        config.setIdleTimeout(30000);
        // 一个连接的生命周期,超时后将被强制关闭 (单位:毫秒)
        config.setMaxLifetime(1800000); // 30 minutes
        // 从连接池获取连接的最大等待时间,超时将抛出异常 (单位:毫秒)
        config.setConnectionTimeout(20000);
        // 4. 创建 HikariDataSource 数据源对象
        HikariDataSource dataSource = new HikariDataSource(config);
        // 5. 使用数据源获取连接,并进行数据库操作
        try (Connection connection = dataSource.getConnection();
             Statement statement = connection.createStatement();
             ResultSet resultSet = statement.executeQuery("SELECT 'Hello, HikariCP!' AS message")) {
            if (resultSet.next()) {
                String message = resultSet.getString("message");
                System.out.println("数据库查询结果: " + message);
            }
            System.out.println("成功获取连接并执行查询!");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 6. (可选) 在应用关闭时关闭数据源
        // dataSource.close();
    }
}

关键配置说明:

  • jdbcUrl: 最核心的配置,必须正确,注意时区参数 serverTimezone=UTC
  • username, password: 数据库的用户名和密码。
  • maximumPoolSize: 非常关键,设置过小可能导致请求排队,设置过大会占用过多数据库资源,建议根据数据库服务器的承载能力和应用的并发量来设定。
  • connectionTimeout: 当所有连接都在使用中时,新的请求需要等待多久,建议设置一个合理的超时时间(如 20-30 秒),避免无限等待。

步骤 2:如何管理 DataSource(最佳实践)

上面的例子中,每次操作都创建一个新的 DataSource 是非常低效的,在实际项目中,DataSource 应该是全局唯一的,通常通过单例模式依赖注入(如 Spring 的 @Bean来管理。

下面是一个使用单例模式管理 DataSource 的示例:

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.SQLException;
// 单例模式 DataSource 管理器
public class DataSourceManager {
    // volatile 关键字确保多线程环境下 instance 的可见性
    private static volatile DataSourceManager instance;
    private HikariDataSource dataSource;
    // 私有构造器
    private DataSourceManager() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC");
        config.setUsername("java_user");
        config.setPassword("password123");
        config.setMaximumPoolSize(10);
        this.dataSource = new HikariDataSource(config);
    }
    // 提供全局访问点
    public static DataSourceManager getInstance() {
        if (instance == null) {
            synchronized (DataSourceManager.class) {
                if (instance == null) {
                    instance = new DataSourceManager();
                }
            }
        }
        return instance;
    }
    // 获取连接
    public Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    // 关闭数据源 (通常在应用关闭时调用)
    public void close() {
        if (dataSource != null && !dataSource.isClosed()) {
            dataSource.close();
        }
    }
}

使用方式:

public class Application {
    public static void main(String[] args) {
        DataSourceManager manager = DataSourceManager.getInstance();
        try (Connection conn = manager.getConnection()) {
            // 使用 conn 进行数据库操作...
            System.out.println("从单例数据源获取连接成功!");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Druid 连接池示例

Druid 的使用方式与 HikariCP 类似,但配置项更多,并且自带监控功能。

步骤 1:添加 Druid 依赖

Maven (pom.xml)

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.18</version> <!-- 建议使用较新版本 -->
</dependency>

步骤 2:编写代码

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.ResultSet;
import java.sql.Statement;
import java.util.Properties;
public class DruidExample {
    public static void main(String[] args) {
        // 方式一:硬编码配置 (不推荐)
        /*
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC");
        dataSource.setUsername("java_user");
        dataSource.setPassword("password123");
        dataSource.setInitialSize(5);
        dataSource.setMaxActive(20);
        */
        // 方式二:使用配置文件 (推荐)
        // 在 resources 目录下创建 druid-config.properties
        Properties props = new Properties();
        try (InputStream is = DruidExample.class.getClassLoader().getResourceAsStream("druid-config.properties")) {
            if (is == null) {
                throw new RuntimeException("找不到 druid-config.properties 文件");
            }
            props.load(is);
            // 1. 通过工厂类创建数据源
            DataSource dataSource = DruidDataSourceFactory.createDataSource(props);
            // 2. 获取连接
            try (Connection connection = dataSource.getConnection();
                 Statement statement = connection.createStatement();
                 ResultSet resultSet = statement.executeQuery("SELECT 'Hello, Druid!' AS message")) {
                if (resultSet.next()) {
                    String message = resultSet.getString("message");
                    System.out.println("数据库查询结果: " + message);
                }
                System.out.println("成功从 Druid 连接池获取连接!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

src/main/resources/druid-config.properties 文件内容:

# 基本配置
url=jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
username=java_user
password=password123
driverClassName=com.mysql.cj.jdbc.Driver
# 连接池配置
initialSize=5
minIdle=5
maxActive=20
maxWait=60000
timeBetweenEvictionRunsMillis=60000
minEvictableIdleTimeMillis=300000
validationQuery=SELECT 1
testWhileIdle=true
testOnBorrow=false
testOnReturn=false
poolPreparedStatements=true
maxPoolPreparedStatementPerConnectionSize=20

Druid 的最大亮点是它的监控,你可以通过配置开启 Web 监控页面,非常方便地查看 SQL 执行情况、连接池状态等。


在 Spring Boot 中集成(最简单的方式)

在现代 Java 开发中,我们大多使用 Spring Boot,它极大地简化了连接池的配置。

添加 spring-boot-starter-data-jpa 依赖 它会自动引入 HikariCP 和 JDBC 驱动。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

application.propertiesapplication.yml 中配置

application.properties

# DataSource Configuration
spring.datasource.url=jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
spring.datasource.username=java_user
spring.datasource.password=password123
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# HikariCP Specific Configuration (可选,使用默认值即可)
spring.datasource.hikari.connection-timeout=30000
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=600000

application.yml (更推荐)

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=UTC
    username: java_user
    password: password123
    driver-class-name: com.mysql.cj.jdbc.Driver
    hikari:
      connection-timeout: 30000
      maximum-pool-size: 10
      minimum-idle: 5
      idle-timeout: 600000

创建实体类和 Repository

// User.java (实体类)
@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    // getters and setters...
}
// UserRepository.java (数据访问层)
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

在 Service 中使用

Spring Boot 会自动配置好一个名为 dataSource 的 Bean,你只需要通过 @Autowired 注入 JpaRepository 即可,Spring Data JPA 会在内部自动处理连接的获取和释放。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    public void addUser(String name) {
        User user = new User();
        user.setName(name);
        userRepository.save(user);
        System.out.println("用户添加成功!");
    }
}

Spring Boot 的自动配置功能让你几乎不需要写任何连接池相关的 Java 代码,非常方便。


方面 说明
核心概念 连接池通过复用预创建的数据库连接,避免了频繁创建/销毁的开销,从而提升性能节省资源控制并发
技术选型 HikariCP (性能之王,Spring Boot 默认)、Druid (功能强大,带监控)。
手动集成 添加 JDBC 驱动和连接池依赖。
创建连接池配置对象 (HikariConfig / DruidDataSource)。
配置数据库连接信息 (url, user, pass) 和连接池参数 (maxPoolSize 等)。
创建数据源 (DataSource) 对象。
关键:通过单例等方式全局管理 DataSource
DataSource 获取 Connection 进行操作。
Spring Boot 集成 极其简单
添加 spring-boot-starter-data-jpa 依赖。
application.yml 中配置数据源参数即可,Spring Boot 会自动配置好 HikariCP。
最佳实践 始终使用连接池,而不是每次都创建新连接,在 Spring Boot 项目中,直接使用其自动配置功能,在传统项目中,使用 HikariCP 并通过单例模式管理 DataSource
分享:
扫描分享到社交APP
上一篇
下一篇