Druid 数据库连接池深度指南

2 阅读4分钟

高性能 · 可监控 · 安全可靠 —— 基于 Filter 链与 Proxy 代理的智能连接池

一、为什么需要连接池?—— 问题驱动理解

数据库连接(Connection)创建过程涉及:

  • TCP 握手
  • 用户认证
  • 会话初始化

每次请求都新建连接 = 每次开门都要造一把新钥匙,成本极高!

✅ 连接池的作用
提前创建一批“可复用”的连接,像“共享单车”一样按需借用、归还,大幅提升系统吞吐量和响应速度。

二、Druid 是什么?

Druid 不仅是一个连接池,更是一个 可插拔的 JDBC 增强框架。其核心设计思想是:

通过代理(Proxy)包装原始 JDBC 对象,再通过过滤器链(Filter Chain)拦截所有操作,实现监控、审计、日志等非业务功能。

这使得 Druid 在不侵入业务代码的前提下,提供强大的 AOP 能力。


三、核心原理详解(含 Filter 与 Proxy)

3.1 整体架构:Proxy + Filter Chain 模式

Druid 对所有 JDBC 核心对象(ConnectionStatementResultSet 等)都进行了 动态代理封装,并在方法调用前后插入 Filter 链

+---------------------+
|   Application Code  |
+----------+----------+
           |
           v
+----------+----------+
|  Druid DataSource   | ← 返回的是 ProxyConnection
+----------+----------+
           |
           v
+----------+----------+
|  ProxyConnection    | ← 动态代理对象
+----------+----------+
           |
           v
+----------+----------+
|   Filter Chain      | ← 责任链模式:StatFilter → WallFilter → LogFilter → ...
+----------+----------+
           |
           v
+----------+----------+
| Physical Connection | ← 真实的数据库连接(如 MySQL Connection)
+---------------------+

3.2 Filter 链执行流程(以执行 SQL 为例)

当应用执行 statement.executeQuery(sql) 时:

sequenceDiagram
    participant App as 应用代码
    participant ProxyStmt as ProxyPreparedStatement
    participant StatFilter
    participant WallFilter
    participant LogFilter
    participant RealStmt as 物理 PreparedStatement

    App->>ProxyStmt: executeQuery(sql)
    ProxyStmt->>StatFilter: preExecute(stmt, sql)
    StatFilter->>WallFilter: preExecute(stmt, sql)
    WallFilter->>LogFilter: preExecute(stmt, sql)
    LogFilter->>RealStmt: executeQuery(sql)  // 调用真实对象
    RealStmt-->>LogFilter: ResultSet
    LogFilter->>WallFilter: postExecute(result)
    WallFilter->>StatFilter: postExecute(result)
    StatFilter->>ProxyStmt: postExecute(result)
    ProxyStmt-->>App: 返回 ResultSet

🔍 每个 Filter 的作用

  • StatFilter:记录 SQL 执行次数、耗时、并发数,用于监控面板
  • WallFilter:解析 SQL 语法树,判断是否为危险操作(如 DROP TABLE),可配置白名单/黑名单
  • LogFilter:打印 SQL 日志(支持参数脱敏)

3.3 如何配置 Filter?

在 Spring Boot 中,通过 filters 属性指定启用的 Filter:

spring:
  datasource:
    druid:
      filters: stat,wall,log4j2

也可通过 Java Config 精细控制:

@Bean
public DruidDataSource dataSource() {
    DruidDataSource ds = new DruidDataSource();
    ds.setUrl("");

    // 添加 StatFilter
    StatFilter statFilter = new StatFilter();
    statFilter.setSlowSqlMillis(1000);
    statFilter.setLogSlowSql(true);
    ds.getProxyFilters().add(statFilter);

    // 添加 WallFilter(MySQL 模式)
    WallFilter wallFilter = new WallFilter();
    wallFilter.setDbType("mysql");
    WallConfig config = new WallConfig();
    config.setSelectAllow(true);
    wallFilter.setConfig(config);
    ds.getProxyFilters().add(wallFilter);

    return ds;
}

💡 Filter 执行顺序 = 添加顺序!建议:Stat → Wall → Log

四、生产级推荐配置(含 Filter 安全策略)

前提:使用 druid-spring-boot-starter(推荐方式),版本 ≥ 1.2.16

spring:
  datasource:
    url: jdbc:mysql://prod-db-host:3306/your_db?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
    username: your_app_user
    password: ${DB_PASSWORD}  # 推荐从环境变量或配置中心读取
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    druid:
      # === 连接池核心参数 ===
      initial-size: 10
      min-idle: 10
      max-active: 50
      max-wait: 3000                 # 单位:毫秒,建议 2000~5000

      # === 连接有效性检测(关键!)===
      time-between-eviction-runs-millis: 30000   # 30秒运行一次空闲连接检测线程
      min-evictable-idle-time-millis: 600000     # 连接在池中最小生存时间(10分钟)
      max-evictable-idle-time-millis: 900000     # 最大空闲时间(可选,Druid 1.2.6+ 支持)
      validation-query: SELECT 1
      validation-query-timeout: 1                # 校验 SQL 超时(秒)
      test-while-idle: true                      # 后台线程检测空闲连接(推荐开启)
      test-on-borrow: false                      # 借出时不检测(避免性能损耗)
      test-on-return: false                      # 归还时不检测

      # === 过滤器配置(核心功能)===
      filters: stat,wall,slf4j                   # 注意:log4j2 已不推荐,用 slf4j

      # StatFilter 配置(通过 connection-properties 设置)
      connection-properties: >
        druid.stat.mergeSql=true;
        druid.stat.slowSqlMillis=1000;
        druid.stat.logSlowSql=true

      # WallFilter 配置(MySQL 模式)
      wall:
        db-type: mysql
        config:
          drop-table-allow: false
          truncate-allow: false
          alter-table-allow: false
          lock-table-allow: false
          delete-allow: true
          update-allow: true
          select-for-update-allow: false
          # 白名单模式(更安全,按需启用)
          # white-list-enabled: true
          # white-list: com.yourcompany.dao.*

      # === Web 监控(谨慎开放!)===
      web-stat-filter:
        enabled: true
        url-pattern: /*
        exclusions: "*.js,*.css,*.png,*.jpg,*.gif,*.ico,*.woff,*.woff2,/druid/*"
        session-stat-max-count: 1000
        profile-enable: true

      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: false                        # 禁止重置统计数据
        login-username: ${DRUID_MONITOR_USER:admin}
        login-password: ${DRUID_MONITOR_PASS}
        allow: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,127.0.0.1  # 仅内网可访问
        deny:                                      # 可选:拒绝特定 IP

配置项是否正确说明
filters: stat,wall,slf4jlog4j2 在新版 starter 中已不自动集成,推荐 slf4j,日志由应用统一管理
connection-properties必须用此方式配置 StatFilter 参数,直接写 druid.stat.xxx 无效
wall.db-type: mysql必须指定,否则默认为 sql92,可能误判语法
test-on-borrow: false官方强烈建议关闭,性能损耗大;靠 test-while-idle 保证健康
max-wait: 3000避免线程无限阻塞,快速失败更利于系统稳定
allow 列表使用 CIDR 表示法(如 10.0.0.0/8),禁止写 "" 或 *
reset-enable: false生产环境必须关闭,防止监控数据被清空

⚠️ WallFilter 使用建议

  • 生产环境优先使用 白名单模式(只允许指定包下的 DAO 访问)
  • 黑名单模式需谨慎测试,避免误杀合法 SQL

五、Druid vs HikariCP —— 补充 Filter 维度对比

能力DruidHikariCP
JDBC 代理✅ 完整代理 Connection/Statement/ResultSet❌ 仅管理连接,不代理 JDBC 对象
SQL 拦截✅ 通过 Filter 链实现❌ 无法拦截 SQL 内容
SQL 防火墙✅ 基于语法树分析(WallFilter)❌ 不支持
SQL 脱敏日志✅ LogFilter 支持参数脱敏❌ 需自行实现
扩展性✅ 可自定义 Filter 实现 AOP❌ 无扩展点

🎯 结论
如果你需要 SQL 级别的可观测性与安全性,Druid 的 Filter 机制是不可替代的优势。

六、生产注意事项(Filter 相关)

❗ 常见陷阱

问题原因解决方案
WallFilter 误拦截合法 SQLSQL 方言不匹配或规则过严设置 dbType 正确值;先开启 logViolation=true 观察日志
自定义 Filter 未生效未加入 proxyFilters 列表使用 dataSource.getProxyFilters().add(myFilter)
多数据源 Filter 冲突全局 Filter 影响所有数据源为每个 DruidDataSource 单独配置 Filter
性能下降明显开启了 testOnBorrow + 多个 Filter关闭 testOnBorrow,仅保留必要 Filter

✅ 最佳实践

  1. Filter 尽量轻量:避免在 preExecute 中做耗时操作

  2. WallFilter 白名单优先:比黑名单更安全可靠

  3. 慢 SQL 自动告警:结合 StatFilter + 日志系统(如 ELK)实现


七、总结

Druid 的真正威力,源于其  “代理 + 过滤器链”  的架构设计:

  • Proxy:透明包装 JDBC 对象,无侵入
  • Filter Chain:像“流水线”一样处理每个数据库操作,实现监控、安全、日志等横切关注点

✅ 适用场景

  • 需要 SQL 审计、防注入的企业系统
  • 要求慢 SQL 自动发现的高可用服务
  • 合规性强、需操作留痕的金融/政务项目

📌 记住
“Druid 不只是一个池,而是一个 JDBC 增强中间件。”