Alibaba Druid

Java 语言中最好的数据库连接池 · 强大的监控 · 极致的扩展性

Java 8 + Maven 实战指南

01. 核心概念与优势

为什么选择 Druid?

Druid 是阿里巴巴开源的数据库连接池,专为监控而生。它不仅提供高性能的连接池功能,还集成了 SQL 监控、黑名单拦截、Web 应用防火墙(WallFilter)等企业级功能,在阿里内部经过大规模生产环境验证。

特性DruidHikariCPDBCP2C3P0
监控能力极强 (内置)弱 (需扩展)
扩展性好 (Filter 链机制)一般一般一般
性能优秀极致一般较差
SQL 防火墙内置 WallFilter
稳定性久经考验 (阿里内部)极高一般一般
连接泄漏检测支持支持支持支持

Druid 架构概览

Druid 通过 Filter 链机制实现了对 JDBC 层的全面拦截与增强,所有 SQL 操作都会经过 Filter 处理。

Application DruidDataSource Filter Chain (Stat, Wall, Log) Connection Pool Database getConnection() TCP/IP

02. 快速集成 (Java 8 + Maven)

1. 添加 Maven 依赖

pom.xml 中添加 Druid 依赖。推荐使用 druid-spring-boot-starter 简化 Spring Boot 集成。

<!-- 方式一:原生 Druid(适用于非 Spring Boot 项目) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.23</version>
</dependency>

<!-- 方式二:Spring Boot Starter(推荐) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.2.23</version>
</dependency>

<!-- MySQL 驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <version>8.3.0</version>
</dependency>
从 MySQL Connector 8.x 起,groupId 已变更为 com.mysql,artifactId 为 mysql-connector-j,旧的 mysql:mysql-connector-java 已弃用。

2. 基础代码示例 (纯 JDBC)

不使用 Spring 框架时,通过代码直接创建和配置 Druid 数据源。

import com.alibaba.druid.pool.DruidDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class DruidDemo {
    private static DruidDataSource dataSource;

    static {
        dataSource = new DruidDataSource();
        // 基础配置
        dataSource.setUrl("jdbc:mysql://localhost:3306/test_db?useSSL=false&serverTimezone=Asia/Shanghai");
        dataSource.setUsername("root");
        dataSource.setPassword("password");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        // 连接池配置
        dataSource.setInitialSize(5);
        dataSource.setMinIdle(5);
        dataSource.setMaxActive(20);
        dataSource.setMaxWait(60000);
        // 连接保活
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTestWhileIdle(true);
        dataSource.setTimeBetweenEvictionRunsMillis(60000);
        dataSource.setMinEvictableIdleTimeMillis(300000);
    }

    public static void main(String[] args) {
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement("SELECT 1")) {
            ResultSet rs = stmt.executeQuery();
            while (rs.next()) {
                System.out.println("Result: " + rs.getInt(1));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 应用关闭时调用
    public static void shutdown() {
        if (dataSource != null) {
            dataSource.close();
        }
    }
}
注意:dataSource.close() 仅在应用关闭时调用,不要在每次获取连接后关闭数据源。Connection.close() 在 Druid 中是归还连接,而非销毁。

03. 配置详解与最佳实践

关键参数解析

合理的配置能显著提升系统稳定性与性能。以下是生产环境推荐配置说明。

参数名默认值说明与建议
initialSize0初始化时建立物理连接的个数。建议与 minIdle 一致,避免应用启动时的连接创建延迟。
maxActive8最大连接池数量。根据系统负载和数据库承载能力设置,通常 20-50。
minIdle0最小空闲连接数。保持一定空闲连接可减少突发流量时的连接创建开销。
maxWait-1 (无限)获取连接时最大等待时间(毫秒)。建议设为 60000,避免线程无限阻塞。
validationQuerynull检测连接是否有效的 SQL。MySQL 用 SELECT 1,Oracle 用 SELECT 1 FROM DUAL
testWhileIdletrue默认已开启,请勿关闭。空闲时间超过 timeBetweenEvictionRunsMillis 后检测连接有效性。
testOnBorrowfalse申请连接时检测。开启会影响性能,配合 testWhileIdle 使用通常无需开启。
timeBetweenEvictionRunsMillis60000空闲连接检查的间隔时间(毫秒),同时也是 testWhileIdle 的判断依据。
minEvictableIdleTimeMillis1800000连接在池中最小空闲时间(毫秒),超过此值且池中空闲连接大于 minIdle 时将被回收。建议设为 300000。
removeAbandonedfalse是否开启连接泄漏检测。开发/测试环境建议开启,生产环境酌情。
removeAbandonedTimeout300连接泄漏检测超时时间(秒),超过此时间未归还的连接将被强制回收。
filters配置扩展 Filter,如 stat(监控)、wall(防火墙)、slf4j(日志)。

Spring Boot 配置文件示例 (application.yml)

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: ${DB_PASSWORD:secret}
    druid:
      # 连接池核心配置
      initial-size: 5
      min-idle: 5
      max-active: 20
      max-wait: 60000
      # 连接保活与回收
      validation-query: SELECT 1
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      time-between-eviction-runs-millis: 60000
      min-evictable-idle-time-millis: 300000
      # 监控与拦截
      filters: stat,wall,slf4j
      # 连接泄漏检测(开发环境推荐开启)
      remove-abandoned: true
      remove-abandoned-timeout: 1800
      log-abandoned: true
      # 监控页面配置
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        login-username: admin
        login-password: admin
        reset-enable: false
      # Web 关联监控
      web-stat-filter:
        enabled: true
        url-pattern: /*
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
使用 druid-spring-boot-starter 时,上述配置即可自动生效,无需手动注册 Servlet 和 Filter。密码建议通过环境变量注入,避免明文写入配置文件。

04. 连接生命周期

连接状态流转

理解连接的生命周期有助于排查"连接泄漏"或"连接超时"等问题。连接在池中经历创建、空闲、使用、回收四个阶段。

Does Not Exist Idle Active (In Use) Create getConnection() close() / Return Evict / Error
关键点:调用 Connection.close() 并不会真正关闭物理连接,而是将连接归还到池中变为 Idle 状态。只有当连接失效或被驱逐(Evict)时才会真正销毁。

05. 监控体系

内置监控页面

Druid 内置了一个功能强大的 Web 监控页面,可以实时查看 SQL 执行情况、连接池状态、慢查询统计等。这是 Druid 区别于其他连接池的核心优势。

SQL Execution StatFilter Merge & Count Memory (JMX) Web UI

传统 Web 项目配置 (web.xml)

在非 Spring Boot 项目中,需要在 web.xml 中手动配置 Servlet 和 Filter:

<!-- 1. 监控页面 Servlet -->
<servlet>
    <servlet-name>DruidStatView</servlet-name>
    <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
    <init-param>
        <param-name>resetEnable</param-name>
        <param-value>false</param-value>
    </init-param>
    <init-param>
        <param-name>loginUsername</param-name>
        <param-value>admin</param-value>
    </init-param>
    <init-param>
        <param-name>loginPassword</param-name>
        <param-value>admin</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>DruidStatView</servlet-name>
    <url-pattern>/druid/*</url-pattern>
</servlet-mapping>

<!-- 2. Web 关联监控 Filter -->
<filter>
    <filter-name>DruidWebStatFilter</filter-name>
    <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
    <init-param>
        <param-name>exclusions</param-name>
        <param-value>*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>DruidWebStatFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
生产环境务必设置 resetEnable=false,防止误清统计数据;同时修改默认用户名和密码,或限制访问 IP。

Spring Boot 注解配置方式

使用 druid-spring-boot-starter 时,可通过 Java Config 方式注册监控:

import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DruidMonitorConfig {
    @Bean
    public ServletRegistrationBean<StatViewServlet> statViewServlet() {
        ServletRegistrationBean<StatViewServlet> bean =
            new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
        bean.addInitParameter("loginUsername", "admin");
        bean.addInitParameter("loginPassword", "securePassword");
        bean.addInitParameter("resetEnable", "false");
        return bean;
    }

    @Bean
    public FilterRegistrationBean<WebStatFilter> webStatFilter() {
        FilterRegistrationBean<WebStatFilter> bean =
            new FilterRegistrationBean<>(new WebStatFilter());
        bean.setUrlPatterns(java.util.Collections.singletonList("/*"));
        bean.addInitParameter("exclusions", "*.js,*.css,*.ico,/druid/*");
        return bean;
    }
}

06. 安全防护 (WallFilter)

SQL 防火墙

Druid 内置的 WallFilter 基于语义分析实现 SQL 防火墙功能,能有效防御 SQL 注入攻击,并可禁止危险的 SQL 操作(如 DROP TABLE、TRUNCATE 等)。

Java 代码配置

import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import java.util.Arrays;

// 自定义 WallFilter 配置
WallFilter wallFilter = new WallFilter();
WallConfig wallConfig = new WallConfig();
wallConfig.setDeleteAllow(false);         // 禁止 DELETE
wallConfig.setDropTableAllow(false);      // 禁止 DROP TABLE
wallConfig.setTruncateAllow(false);       // 禁止 TRUNCATE
wallConfig.setAlterTableAllow(false);     // 禁止 ALTER TABLE
wallConfig.setMultiStatementAllow(false); // 禁止多语句执行
wallFilter.setConfig(wallConfig);
wallFilter.setDbType("mysql");

DruidDataSource dataSource = new DruidDataSource();
dataSource.setProxyFilters(Arrays.asList(wallFilter));

Spring Boot YAML 配置

spring:
  datasource:
    druid:
      filters: stat,wall,slf4j
      filter:
        wall:
          enabled: true
          db-type: mysql
          config:
            delete-allow: false
            drop-table-allow: false
            truncate-allow: false
            alter-table-allow: false
            multi-statement-allow: false
WallFilter 仅作为安全增强手段,不可替代参数化查询。所有 SQL 执行都应使用 PreparedStatement 传参,这是防止 SQL 注入的根本方法。

07. 性能调优

连接池大小计算

连接池大小并非越大越好。业界常用经验公式(引自 HikariCP 文档):

池大小 = (CPU 核心数 * 2) + 有效磁盘数

示例:4 核 CPU + 1 块 SSD
推荐池大小 = (4 * 2) + 1 = 9(可适当上调至 10-15)
大多数场景下,20 个连接已足够支撑高并发。过大的连接池反而会因数据库上下文切换导致性能下降。

关键监控指标

指标含义关注阈值
ActiveCount当前活跃连接数持续接近 maxActive 时需扩容
PoolingCount当前池中空闲连接数长期为 0 表示连接不足
WaitThreadCount等待获取连接的线程数大于 0 时需立即关注
ErrorCount连接错误计数持续增长需排查网络或数据库问题
ExecuteCountSQL 执行总次数用于评估数据库整体负载

慢 SQL 检测与连接泄漏排查

spring:
  datasource:
    druid:
      filter:
        stat:
          enabled: true
          slow-sql-millis: 3000    # 超过 3 秒视为慢 SQL
          log-slow-sql: true       # 记录慢 SQL 日志
          merge-sql: true          # 合并相同结构的 SQL 统计
      # 连接泄漏检测
      remove-abandoned: true
      remove-abandoned-timeout: 1800   # 30 分钟未归还则强制回收
      log-abandoned: true              # 记录泄漏连接的堆栈信息
连接泄漏检测会带来一定性能开销,建议仅在开发和测试环境开启 remove-abandoned,生产环境排查完毕后关闭。

08. 常见问题与解决方案

Q: discard connection 异常怎么处理?

A: 通常因为连接在数据库端已超时关闭,但连接池不知情。解决方案:开启 testWhileIdle=true,配置正确的 validationQuery,并确保 timeBetweenEvictionRunsMillis 小于数据库的 wait_timeout

Q: 如何去除 Druid 监控页面的广告?

A: 可通过编写 Servlet Filter 过滤响应中的广告脚本,或升级到较新版本(部分版本已移除或可配置关闭内置广告)。

Q: Oracle 数据库 validationQuery 怎么写?

A: Oracle 必须使用 SELECT 1 FROM DUAL;PostgreSQL 可使用 SELECT 1;SQL Server 使用 SELECT 1

Q: GetConnectionTimeoutException 频繁出现?

A: 说明连接池中的连接已耗尽。排查方向:1) 检查是否存在连接泄漏(未正确关闭 Connection);2) 增大 maxActive;3) 检查慢 SQL 是否占用连接过久;4) 开启 removeAbandoned 定位泄漏代码。

Q: Druid 与 HikariCP 如何选择?

A: 如果项目需要 SQL 监控、防火墙等企业级功能,选择 Druid;如果追求极致连接性能且不需要内置监控,选择 HikariCP。两者都是成熟可靠的连接池。Spring Boot 2.x 默认使用 HikariCP,但 Druid 通过 starter 可轻松替换。

Q: 多数据源场景下如何配置 Druid?

A: 在 Spring Boot 中,可通过 @ConfigurationProperties 绑定不同前缀的配置,分别创建多个 DruidDataSource Bean,配合 @Primary@Qualifier 注解进行区分注入。