Alibaba Druid
Java 语言中最好的数据库连接池 · 强大的监控 · 极致的扩展性
Java 8 + Maven 实战指南01. 核心概念与优势
为什么选择 Druid?
Druid 是阿里巴巴开源的数据库连接池,专为监控而生。它不仅提供高性能的连接池功能,还集成了 SQL 监控、黑名单拦截、Web 应用防火墙(WallFilter)等企业级功能,在阿里内部经过大规模生产环境验证。
| 特性 | Druid | HikariCP | DBCP2 | C3P0 |
|---|---|---|---|---|
| 监控能力 | 极强 (内置) | 弱 (需扩展) | 弱 | 弱 |
| 扩展性 | 好 (Filter 链机制) | 一般 | 一般 | 一般 |
| 性能 | 优秀 | 极致 | 一般 | 较差 |
| SQL 防火墙 | 内置 WallFilter | 无 | 无 | 无 |
| 稳定性 | 久经考验 (阿里内部) | 极高 | 一般 | 一般 |
| 连接泄漏检测 | 支持 | 支持 | 支持 | 支持 |
Druid 架构概览
Druid 通过 Filter 链机制实现了对 JDBC 层的全面拦截与增强,所有 SQL 操作都会经过 Filter 处理。
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>
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. 配置详解与最佳实践
关键参数解析
合理的配置能显著提升系统稳定性与性能。以下是生产环境推荐配置说明。
| 参数名 | 默认值 | 说明与建议 |
|---|---|---|
| initialSize | 0 | 初始化时建立物理连接的个数。建议与 minIdle 一致,避免应用启动时的连接创建延迟。 |
| maxActive | 8 | 最大连接池数量。根据系统负载和数据库承载能力设置,通常 20-50。 |
| minIdle | 0 | 最小空闲连接数。保持一定空闲连接可减少突发流量时的连接创建开销。 |
| maxWait | -1 (无限) | 获取连接时最大等待时间(毫秒)。建议设为 60000,避免线程无限阻塞。 |
| validationQuery | null | 检测连接是否有效的 SQL。MySQL 用 SELECT 1,Oracle 用 SELECT 1 FROM DUAL。 |
| testWhileIdle | true | 默认已开启,请勿关闭。空闲时间超过 timeBetweenEvictionRunsMillis 后检测连接有效性。 |
| testOnBorrow | false | 申请连接时检测。开启会影响性能,配合 testWhileIdle 使用通常无需开启。 |
| timeBetweenEvictionRunsMillis | 60000 | 空闲连接检查的间隔时间(毫秒),同时也是 testWhileIdle 的判断依据。 |
| minEvictableIdleTimeMillis | 1800000 | 连接在池中最小空闲时间(毫秒),超过此值且池中空闲连接大于 minIdle 时将被回收。建议设为 300000。 |
| removeAbandoned | false | 是否开启连接泄漏检测。开发/测试环境建议开启,生产环境酌情。 |
| removeAbandonedTimeout | 300 | 连接泄漏检测超时时间(秒),超过此时间未归还的连接将被强制回收。 |
| 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. 连接生命周期
连接状态流转
理解连接的生命周期有助于排查"连接泄漏"或"连接超时"等问题。连接在池中经历创建、空闲、使用、回收四个阶段。
Connection.close() 并不会真正关闭物理连接,而是将连接归还到池中变为 Idle 状态。只有当连接失效或被驱逐(Evict)时才会真正销毁。05. 监控体系
内置监控页面
Druid 内置了一个功能强大的 Web 监控页面,可以实时查看 SQL 执行情况、连接池状态、慢查询统计等。这是 Druid 区别于其他连接池的核心优势。
传统 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
PreparedStatement 传参,这是防止 SQL 注入的根本方法。07. 性能调优
连接池大小计算
连接池大小并非越大越好。业界常用经验公式(引自 HikariCP 文档):
池大小 = (CPU 核心数 * 2) + 有效磁盘数
示例:4 核 CPU + 1 块 SSD
推荐池大小 = (4 * 2) + 1 = 9(可适当上调至 10-15)
关键监控指标
| 指标 | 含义 | 关注阈值 |
|---|---|---|
| ActiveCount | 当前活跃连接数 | 持续接近 maxActive 时需扩容 |
| PoolingCount | 当前池中空闲连接数 | 长期为 0 表示连接不足 |
| WaitThreadCount | 等待获取连接的线程数 | 大于 0 时需立即关注 |
| ErrorCount | 连接错误计数 | 持续增长需排查网络或数据库问题 |
| ExecuteCount | SQL 执行总次数 | 用于评估数据库整体负载 |
慢 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 注解进行区分注入。