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

2025-12-12 0 774

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

一、Druid 整体架构设计

Druid是阿里巴巴开源的高性能数据库连接池,其设计遵循了分层架构,主要包括以下几个核心层次:

  • 数据源层:DruidDataSource是核心类,负责管理连接池的生命周期
  • 连接管理层:负责物理连接的创建、获取、复用和销毁
  • 监控:提供全面的监控统计功能
  • 过滤链层:实现SQL拦截、监控、防火墙等扩展功能
  • 适配:与各种框架(如Spring Boot)的集成适配

二、核心组件源码分析

1. DruidDataSource 核心类

DruidDataSource是Druid连接池的核心实现类,继承自AbstractDataSource:

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创建实际的数据库连接:

// 创建物理连接
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方法提供连接获取服务:

@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使用数组存储连接,通过原子操作维护连接状态:

// 连接池数组相关字段
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使用后台线程维护连接池大小和连接健康状态:

// 创建连接的线程
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是核心统计过滤器:

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对象进行包装,实现监控功能:

// 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执行过程,支持多个过滤器的链式调用:

public class FilterChainImpl implements FilterChain {
    private final DruidDataSource dataSource;
    private final List 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注入:

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解析器,支持多种数据库方言:

// 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实现:

public class DruidDataSourceWrapper extends DruidDataSource implements InitializingBean, DisposableBean, ApplicationListener {
    @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实现:

@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:

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的源码实现,开发者可以更好地使用和优化数据库连接池,提升应用性能和稳定性。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 Druid 数据库连接池源码详细解析 https://www.zuozi.net/35701.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务