Druid 数据库连接池源码详细解析
一、Druid 整体架构设计
Druid是阿里巴巴开源的高性能数据库连接池,其设计遵循了分层架构,主要包括以下几个核心层次:
- 数据源层:DruidDataSource是核心类,负责管理连接池的生命周期
- 连接管理层:负责物理连接的创建、获取、复用和销毁
- 监控层:提供全面的监控统计功能
- 过滤链层:实现SQL拦截、监控、防火墙等扩展功能
- 适配层:与各种框架(如Spring Boot)的集成适配
二、核心组件源码分析
1. DruidDataSource 核心类
DruidDataSource是Druid连接池的核心实现类,继承自AbstractDataSource:
java
public class DruidDataSource extends AbstractDataSource implements DruidAbstractDataSourceMBean, ManagedDataSource, Referenceable, Closeable {
// 核心属性
private volatile DruidConnectionHolder[] connections; // 连接池数组
private int poolingCount = 0; // 可用连接数
private int minIdle; // 最小空闲连接数
private int maxActive; // 最大连接数
private long maxWaitMillis; // 获取连接的最大等待时间
private volatile boolean closed = false;
private volatile boolean closing = false;
// 初始化方法
@Override
public void init() throws SQLException {
// 初始化参数校验
if (initException != null) {
throw initException;
}
// 初始化连接池数组
connections = new DruidConnectionHolder[maxActive];
// 初始化监控统计
createStatLogger();
// 创建连接
generateAndPutInitializeTask();
// 启动各种后台线程
createAndStartDestroyThread();
createAndStartCreateThread();
createAndStartAbandonedCheckThread();
// 注册JMX
registerMbean();
}
}
2. 连接创建机制
Druid使用PhysicalConnectionInfo表示物理连接信息,通过createPhysicalConnection创建实际的数据库连接:
java
// 创建物理连接
private PhysicalConnectionInfo createPhysicalConnection(String url, Properties info) throws SQLException {
// 获取数据库驱动
Connection conn;
if (getProxyFilters().size() > 0) {
// 如果有代理过滤器,创建代理连接
Connection nativeConnection = getDriver().connect(url, info);
conn = new FilterChainImpl(this).connection_connect(nativeConnection, info);
} else {
// 直接创建连接
conn = getDriver().connect(url, info);
}
// 创建连接信息对象
return new PhysicalConnectionInfo(conn, System.currentTimeMillis());
}
3. 连接获取流程
Druid通过getConnection方法提供连接获取服务:
java
@Override
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
// 检查连接池状态
if (closed) {
throw new DataSourceClosedException("dataSource already closed");
}
// 处理初始化
if (enable) {
connectErrorCountUpdater.compareAndSet(this, 0, 1);
}
// 获取连接
DruidConnectionHolder holder;
if (maxWaitMillis > 0) {
// 有限时等待
holder = pollLast(namespace, maxWaitMillis);
} else {
// 无限等待
holder = takeLast(namespace);
}
// 处理超时和异常
if (holder != null) {
// 创建代理连接
DruidPooledConnection poolalbeConnection = new DruidPooledConnection(holder);
return poolalbeConnection;
}
throw new SQLException("get connection timeout");
}
三、连接池管理机制
1. 连接池数组管理
Druid使用数组存储连接,通过原子操作维护连接状态:
java
// 连接池数组相关字段
private DruidConnectionHolder[] connections; // 连接数组
private int poolingCount; // 当前可用连接数
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty; // 非空条件
private final Condition notFull; // 非满条件
// 添加连接到池中
private void put(DruidConnectionHolder holder) throws InterruptedException, SQLException {
lock.lockInterruptibly();
try {
// 循环直到成功放入或连接池关闭
for (;;) {
if (poolingCount >= maxActive) {
// 连接池已满,等待
notFull.await();
continue;
}
// 放入连接数组
connections[poolingCount++] = holder;
// 通知等待线程
notEmpty.signal();
break;
}
} finally {
lock.unlock();
}
}
// 从池中获取连接
private DruidConnectionHolder takeLast() throws InterruptedException, SQLException {
lock.lockInterruptibly();
try {
DruidConnectionHolder last;
int removeLast = poolingCount - 1;
// 循环直到获取连接或超时
for (;;) {
if (poolingCount == 0) {
// 连接池为空,等待
notEmpty.await();
continue;
}
// 从数组尾部获取连接
last = connections[removeLast];
connections[removeLast] = null;
poolingCount--;
// 检查连接有效性
if (last.isDiscard()) {
continue;
}
return last;
}
} finally {
lock.unlock();
}
}
2. 连接创建和销毁线程
Druid使用后台线程维护连接池大小和连接健康状态:
java
// 创建连接的线程
public class CreateConnectionThread extends Thread {
@Override
public void run() {
while (!closing && !closed) {
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
break;
}
try {
// 判断是否需要创建连接
boolean needCreate = false;
if (poolingCount < minIdle) {
needCreate = true;
}
if (needCreate) {
// 创建物理连接
PhysicalConnectionInfo pyConnectInfo = createPhysicalConnection();
DruidConnectionHolder holder = new DruidConnectionHolder(DruidDataSource.this, pyConnectInfo);
// 添加到连接池
connections[poolingCount++] = holder;
// 通知等待线程
notEmpty.signal();
}
// 休眠一段时间
lock.unlock();
Thread.sleep(100);
} catch (Exception e) {
// 错误处理
}
}
}
}
// 销毁连接的线程
public class DestroyConnectionThread extends Thread {
@Override
public void run() {
while (!closing && !closed) {
try {
// 检查并销毁超时或无效连接
shrink(true, keepAlive);
// 处理空闲连接回收
if (idleTimeout > 0 || keepAlive) {
removeAbandoned();
}
// 休眠检查间隔时间
Thread.sleep(timeBetweenEvictionRunsMillis);
} catch (Exception e) {
// 错误处理
}
}
}
}
四、监控与统计机制
1. 统计过滤器设计
Druid的监控功能基于过滤器链模式实现,StatFilter是核心统计过滤器:
java
public class StatFilter implements Filter {
// SQL执行统计
private final AtomicLong executeCount = new AtomicLong();
private final AtomicLong executeUpdateCount = new AtomicLong();
private final AtomicLong executeQueryCount = new AtomicLong();
private final AtomicLong executeErrorCount = new AtomicLong();
private final AtomicLong executeTimeNano = new AtomicLong();
// 方法拦截
@Override
public ResultSetProxy statement_executeQuery(FilterChain chain, StatementProxy statement, String sql) throws SQLException {
long startNano = System.nanoTime();
try {
// 执行原始方法
ResultSetProxy resultSet = chain.statement_executeQuery(statement, sql);
// 统计成功执行
long nanos = System.nanoTime() - startNano;
incrementExecuteQueryCount();
incrementExecuteTime(nanos);
return resultSet;
} catch (SQLException ex) {
// 统计错误
incrementExecuteErrorCount();
throw ex;
} finally {
// 处理其他统计信息
}
}
// 其他方法的统计实现...
}
2. 代理对象设计
Druid通过代理模式对JDBC对象进行包装,实现监控功能:
java
// Connection代理实现
public class DruidPooledConnection implements Connection, Wrapper, DruidPooledConnectionMBean {
private final DruidConnectionHolder holder;
private final ConnectionProxyImpl connection;
// 方法实现都会委托给实际连接并记录统计信息
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
checkState();
PreparedStatementHolder stmtHolder = new PreparedStatementHolder(this, sql);
PreparedStatementProxyImpl stmtProxy = new PreparedStatementProxyImpl(stmtHolder);
return stmtProxy;
}
// 关闭连接时实际上是归还到池中
@Override
public void close() throws SQLException {
if (this.disable) {
return;
}
DruidConnectionHolder holder = this.holder;
if (holder == null) {
return;
}
DataSource dataSource = holder.getDataSource();
dataSource.recycle(this);
}
}
// Statement代理实现
public class PreparedStatementProxyImpl extends StatementProxyImpl implements PreparedStatementProxy {
@Override
public ResultSet executeQuery() throws SQLException {
return createChain().preparedStatement_executeQuery(this);
}
}
五、过滤器链机制
1. FilterChain实现
Druid使用过滤器链模式处理SQL执行过程,支持多个过滤器的链式调用:
java
public class FilterChainImpl implements FilterChain {
private final DruidDataSource dataSource;
private final List<Filter> filters;
private int pos;
private int filterSize;
// 执行查询方法的过滤链
@Override
public ResultSetProxy preparedStatement_executeQuery(PreparedStatementProxy statement) throws SQLException {
if (this.pos < filterSize) {
// 调用下一个过滤器
return nextFilter().preparedStatement_executeQuery(this, statement);
}
// 所有过滤器执行完毕,调用实际的方法
ResultSet resultSet = statement.getRawObject().executeQuery();
// 包装结果集
return wrap(statement, resultSet);
}
// 获取下一个过滤器
private Filter nextFilter() {
return filters.get(pos++);
}
}
2. 内置过滤器类型
Druid提供多种内置过滤器,用于不同功能:
- StatFilter:统计监控过滤器
- WallFilter:SQL防火墙,防止SQL注入
- Log4jFilter/LogbackFilter:SQL日志记录
- EncodingConvertFilter:编码转换过滤器
- ClassLoaderFilter:类加载器过滤器
六、SQL解析与防火墙
1. WallFilter实现
WallFilter实现了SQL防火墙功能,能够有效防止SQL注入:
java
public class WallFilter extends FilterAdapter {
private WallProvider provider;
@Override
public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql) throws SQLException {
// SQL解析和校验
WallCheckResult checkResult = provider.check(sql);
if (!checkResult.isSqlSafe()) {
throw new SQLException("sql injection violation, sql: " + sql);
}
// 执行原始方法
return chain.connection_prepareStatement(connection, sql);
}
}
2. SQL解析器
Druid内置了强大的SQL解析器,支持多种数据库方言:
java
// SQL解析示例
public class SQLUtils {
public static SQLStatement parseStatements(String sql, String dbType) {
// 根据数据库类型选择解析器
SQLParser parser = SQLParserUtils.createSQLParser(sql, dbType);
// 解析SQL语句
return parser.parseStatement();
}
}
七、与Spring Boot集成
1. DruidDataSourceWrapper
Spring Boot集成Druid主要通过DruidDataSourceWrapper实现:
java
public class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean, DisposableBean, ApplicationListener<ApplicationEvent> {
@Override
public void afterPropertiesSet() throws Exception {
// 初始化Druid数据源
super.afterPropertiesSet();
// 注册监控Servlet和Filter
registerServlet();
registerFilter();
}
// 配置监控页面
private void registerServlet() {
// 注册StatViewServlet用于监控页面
}
private void registerFilter() {
// 注册WebStatFilter用于Web应用监控
}
}
2. 自动配置类
Spring Boot自动配置通过DruidDataSourceAutoConfigure实现:
java
@Configuration
@ConditionalOnClass(DruidDataSource.class)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
@EnableConfigurationProperties({DruidStatProperties.class, DataSourceProperties.class})
@Import({DruidSpringAopConfiguration.class, DruidStatViewServletConfiguration.class, DruidWebStatFilterConfiguration.class})
public class DruidDataSourceAutoConfigure {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource(Environment environment, DataSourceProperties properties) throws Exception {
// 创建并配置DruidDataSource
DruidDataSource dataSource = DruidDataSourceBuilder.create().build(properties);
// 设置其他属性
return dataSource;
}
}
八、性能优化机制
1. 连接复用策略
Druid通过以下机制优化连接复用:
- 高效的连接池数据结构:使用数组而非链表,减少GC压力
- 异步创建连接:后台线程创建连接,不阻塞业务线程
- 连接有效性检查:在获取和归还连接时检查连接有效性
- 最小/最大空闲连接控制:自动维护合适的连接数量
2. 慢SQL检测
Druid内置慢SQL检测机制,可记录执行时间过长的SQL:
java
public class SlowSqlLogFilter extends FilterAdapter {
private long slowSqlMillis = 3000; // 默认3秒
@Override
public ResultSetProxy preparedStatement_executeQuery(FilterChain chain, PreparedStatementProxy statement, String sql) throws SQLException {
long startMillis = System.currentTimeMillis();
ResultSetProxy resultSet = chain.preparedStatement_executeQuery(statement, sql);
long endMillis = System.currentTimeMillis();
// 检测慢SQL
if (endMillis - startMillis > slowSqlMillis) {
LOG.warn("slow sql, time: " + (endMillis - startMillis) + "ms, sql: " + sql);
}
return resultSet;
}
}
九、核心流程总结
1. 连接池初始化流程
- 创建DruidDataSource实例并设置参数
- 调用init()方法初始化
- 初始化连接池数组和监控统计
- 启动后台维护线程(创建连接、销毁连接、检查连接泄漏)
- 预创建initialSize数量的连接
2. 获取连接流程
- 检查连接池状态
- 尝试从连接池数组尾部获取连接
- 若池为空且未达到最大连接数,创建新连接
- 若池为空且已达最大连接数,等待或超时
- 检查连接有效性,无效则丢弃并继续获取
- 包装成DruidPooledConnection返回
3. 关闭连接流程
- 调用DruidPooledConnection的close()方法
- 内部调用dataSource.recycle(connection)
- 检查连接状态和有效性
- 将连接放回连接池数组
- 唤醒等待获取连接的线程
十、总结
Druid作为一款优秀的数据库连接池,其源码实现体现了以下设计亮点:
- 高性能设计:采用数组存储连接,使用原子操作维护状态,避免频繁GC
- 完善的监控体系:基于过滤器链和代理模式,实现全面的性能监控
- 强大的扩展能力:支持自定义过滤器,可轻松扩展功能
- 安全性保障:内置SQL防火墙,有效防止SQL注入攻击
- 可靠性设计:连接有效性检查、超时回收、泄漏检测等机制
通过深入理解Druid的源码实现,开发者可以更好地使用和优化数据库连接池,提升应用性能和稳定性。