Druid 数据库连接池源码详细解析

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. 连接池初始化流程

  1. 创建DruidDataSource实例并设置参数
  2. 调用init()方法初始化
  3. 初始化连接池数组和监控统计
  4. 启动后台维护线程(创建连接、销毁连接、检查连接泄漏)
  5. 预创建initialSize数量的连接

2. 获取连接流程

  1. 检查连接池状态
  2. 尝试从连接池数组尾部获取连接
  3. 若池为空且未达到最大连接数,创建新连接
  4. 若池为空且已达最大连接数,等待或超时
  5. 检查连接有效性,无效则丢弃并继续获取
  6. 包装成DruidPooledConnection返回

3. 关闭连接流程

  1. 调用DruidPooledConnection的close()方法
  2. 内部调用dataSource.recycle(connection)
  3. 检查连接状态和有效性
  4. 将连接放回连接池数组
  5. 唤醒等待获取连接的线程

十、总结

Druid作为一款优秀的数据库连接池,其源码实现体现了以下设计亮点:

  1. 高性能设计:采用数组存储连接,使用原子操作维护状态,避免频繁GC
  2. 完善的监控体系:基于过滤器链和代理模式,实现全面的性能监控
  3. 强大的扩展能力:支持自定义过滤器,可轻松扩展功能
  4. 安全性保障:内置SQL防火墙,有效防止SQL注入攻击
  5. 可靠性设计:连接有效性检查、超时回收、泄漏检测等机制

通过深入理解Druid的源码实现,开发者可以更好地使用和优化数据库连接池,提升应用性能和稳定性。

相关推荐
货拉拉技术2 小时前
大规模 Kafka 消费集群调度方案
后端
oak隔壁找我2 小时前
MyBatis Plus 源码深度解析
java·后端
剽悍一小兔2 小时前
Nginx 基本使用配置大全
后端
LCG元2 小时前
性能排查必看!当Linux服务器CPU/内存飙高,如何快速定位并"干掉"罪魁祸首进程?
linux·后端
oak隔壁找我2 小时前
MyBatis 源码深度解析
java·后端
lang201509282 小时前
Spring 4.1新特性:深度优化与生态整合
java·后端·spring
纳就这样吧2 小时前
Spring Cloud中@EnableDiscoveryClient注解详解
spring boot·后端·spring cloud
李慕婉学姐2 小时前
【开题答辩过程】以《重庆市社区养老服务小程序设计与实现》为例,不会开题答辩的可以进来看看
java·spring boot
DBLens数据库管理和开发工具2 小时前
GROUP BY隐性排序:MySQL 5.x 与 8.x 的行为大不同
后端