Druid配置项与连接生命周期全解析
Druid 连接池配置项与连接生命周期全解析
项目:XXX
Druid 版本:1.2.22
数据库:Oracle 12c RAC (2 节点, SCAN: db.xdxt.gsnx.dc:1522)
整理日期:2026-07-17
目录
- 一、Druid 全部配置项及作用
- 1.1 连接池大小
- 1.2 连接获取
- 1.3 故障恢复
- 1.4 连接验证
- 1.5 空闲回收
- 1.6 KeepAlive 保活
- 1.7 PreparedStatement 缓存
- 1.8 Filter 过滤器
- 1.9 监控与统计
- 1.10 泄漏回收
- 1.11 事务
- 1.12 JDBC 属性
- 二、配置项间的关联关系
- 2.1 关联总览图
- 2.2 核心关联链路详解
- 三、连接池内部结构
- 四、getConnection 决策流程
- 五、三种场景连接池状态变化
- 六、连接完整生命周期
- 6.1 阶段一:创建(CreateConnectionThread)
- 6.2 阶段二:使用(业务线程借还循环)
- 6.3 阶段三:销毁(DestroyThread)
- 6.4 三线程协调关系
- 七、活跃连接堆栈
- 八、生产故障关键判断速查
一、Druid 全部配置项及作用
(补充内容)完整流程
1.1 连接池大小
| 配置项 | 作用 | 生产值 |
|---|---|---|
initial-size | 启动时创建的初始连接数,init()时一次性创建 | 40 |
min-idle | 最小空闲连接数,CreateConnectionThread 补充到此值(前提:active+pooling < maxActive) | 40 |
max-active | 最大活跃连接数,连接池容量上限,决定connections[]数组大小 | 120 |
约束关系:initial-size≤min-idle≤max-active
⚠️ 易误解:
min-idle的补充逻辑有前提条件——只有activeCount + poolingCount < maxActive(池未满)时,CreateConnectionThread 才会创建连接补充到minIdle。池满时直接empty.await()阻塞,不会建连。
1.2 连接获取
| 配置项 | 作用 | 生产值 |
|---|---|---|
max-wait | getConnection()最大等待毫秒数,超时抛GetConnectionTimeoutException | 10000 |
机制:控制notEmpty.park(maxWait)的超时时间。与break-after-acquire-failure配合——break=true 时 signal 无人响应,max-wait 到期必超时。
1.3 故障恢复
| 配置项 | 作用 | 生产值 |
|---|---|---|
break-after-acquire-failure | 建连连续失败后是否终止建连线程 | true |
connection-error-retry-attempts | 终止前最大建连失败次数 | 3 |
time-between-connect-error-millis | 建连失败重试间隔 | 10000 |
⚠️ 三个必须一起看:break-after-acquire-failure=true时,retry-attempts和time-between-error才有意义。
链路:
建连失败 → 等 time-between-error(10s) → 重试 → 重复 retry-attempts(3) 次 → break=true → CreateConnectionThread 死亡 → CreatingCount 永久=01.4 连接验证
| 配置项 | 作用 | 生产值 |
|---|---|---|
validation-query | 验证用的 SQL | SELECT 1 FROM DUAL |
validation-query-timeout | 验证 SQL 超时秒数 | 1 |
test-while-idle | 空闲连接定时验证(DestroyThread 周期执行) | true |
test-on-borrow | 借出连接时验证(每次取连接都验证,开销大) | false |
test-on-return | 归还连接时验证(失败则丢弃不回池) | false |
依赖关系:validation-query是所有 test 操作的前提;test-while-idle依赖time-between-eviction-runs-millis。
推荐组合:test-while-idle=true+test-on-borrow=false+test-on-return=false(空闲时验证,借还不验证,性能与安全兼顾)
1.5 空闲回收
| 配置项 | 作用 | 生产值 |
|---|---|---|
time-between-eviction-runs-millis | DestroyThread 检查间隔(所有回收逻辑的心跳周期) | 60000 |
min-evictable-idle-time-millis | 连接最小空闲时间,超过才可回收 | 300000 |
max-evictable-idle-time-millis | 连接最大空闲时间,超过强制回收(必须 > min-evictable) | 900000 |
phy-timeout-millis | 物理连接累计存活时间,超时触发销毁(与 idle 时间无关) | 0(不限) |
不配
time-between-eviction-runs-millis则 DestroyThread 不运行,所有回收逻辑失效。
1.6 KeepAlive 保活
| 配置项 | 作用 | 生产值 |
|---|---|---|
keep-alive | 是否对空闲连接做保活 | false |
keep-alive-between-time-millis | 保活检查间隔 | 120000 |
与 min-evictable 配合:
- 未超
min-evictable但超keep-alive-between→ 执行validation-query保活(不回收) - 超
min-evictable→ 回收销毁
1.7 PreparedStatement 缓存
| 配置项 | 作用 | 生产值 |
|---|---|---|
pool-prepared-statements | 是否开启 PSCache | true |
max-open-prepared-statements | 全局最大 PSCache 数(0=不限) | 0 |
max-pool-prepared-statement-per-connection-size | 每连接最大 PSCache 数,超过时 LRU 淘汰 | 20 |
依赖关系:pool-prepared-statements=true是另外两个配置的前提。
1.8 Filter 过滤器
| 配置项 | 作用 | 生产值 |
|---|---|---|
filters | 过滤器列表(stat, wall, log4j, slf4j) | stat,wall |
filter.stat.slow-sql-millis | 慢 SQL 阈值 | 3000 |
filter.stat.log-slow-sql | 是否记录慢 SQL | true |
filter.stat.merge-sql | 是否合并相似 SQL | true |
filter.wall.xxx | SQL 防火墙配置 | 启用 |
filter.log4j.xxx | SQL 日志配置 | — |
⚠️ wall filter 会拦截 PL/SQL 匿名块
BEGIN...END,需改用SELECT ... FROM DUAL或去掉 wall。
1.9 监控与统计
| 配置项 | 作用 | 生产值 |
|---|---|---|
stat-view-servlet.enabled | 是否开启监控页 | true |
stat-view-servlet.login-username/password | 监控页认证 | admin/123456 |
stat-view-servlet.allow/deny | IP 白名单/黑名单 | — |
stat-view-servlet.reset-enable | 是否允许重置统计 | true |
web-stat-filter.enabled | 是否开启 Web 请求监控(独立于 stat-view-servlet) | true |
web-stat-filter.url-pattern | Web 监控 URL 模式 | /* |
web-stat-filter.session-stat-enable | 是否统计 Session | true |
time-between-log-stats-millis | 统计日志输出间隔 | 60000 |
aop-patterns | AOP 监控的包路径 | com.dhcc.cms.* |
依赖关系:监控统计类配置依赖filters包含stat(否则无数据可展示)。
1.10 泄漏回收
| 配置项 | 作用 | 生产值 |
|---|---|---|
remove-abandoned | 是否回收泄漏连接 | false |
remove-abandoned-timeout | 连接被借出多久算泄漏(秒) | 300 |
log-abandoned | 是否记录泄漏连接堆栈 | false |
remove-abandoned=true是另外两个的前提。log-abandoned=true时,Druid 监控页"活跃连接堆栈"才有数据。
1.11 事务
| 配置项 | 作用 | 生产值 |
|---|---|---|
default-auto-commit | 默认自动提交 | true |
default-read-only | 默认只读 | false |
default-transaction-isolation | 默认事务隔离级别 | -1(使用驱动默认) |
1.12 JDBC 属性
| 配置项 | 作用 | 生产值 |
|---|---|---|
driver-class-name | JDBC 驱动类 | oracle.jdbc.OracleDriver |
url | 连接 URL | jdbc:oracle:thin:@db.xdxt.gsnx.dc:1522/xdxt |
username/password | 认证凭据 | xdxt / **** |
connection-properties | JDBC 连接属性 | oracle.jdbc.ReadTimeout=60000 等 |
Oracle 关键超时参数(通过connection-properties或 URL 参数设置):
oracle.net.CONNECT_TIMEOUT:建连 TCP 握手超时oracle.jdbc.ReadTimeout:Socket 读写超时,影响所有 SQL 执行(含 validation-query)
二、配置项间的关联关系
2.1 关联总览图
max-active (池容量上限) ├─ initial-size → 启动时创建 ├─ min-idle → CreateConnectionThread 最低水位 ├─ break-after-acquire-failure ─┬─ connection-error-retry-attempts │ └─ time-between-connect-error-millis └─ max-wait → 取连接等待超时 time-between-eviction-runs-millis (DestroyThread 心跳) ├─ test-while-idle ──┐ │ ├─ validation-query ── validation-query-timeout │ └─ (test-on-borrow / test-on-return 也依赖 validation-query) ├─ min-evictable-idle-time-millis ── max-evictable-idle-time-millis ├─ keep-alive ── keep-alive-between-time-millis ├─ remove-abandoned ─┬─ remove-abandoned-timeout │ └─ log-abandoned └─ phy-timeout-millis filters (过滤器) ├─ stat ─┬─ stat-view-servlet (监控页) │ ├─ time-between-log-stats-millis (统计日志) │ ├─ filter.stat.slow-sql-millis (慢SQL) │ └─ web-stat-filter (Web监控) ├─ wall → filter.wall.xxx (SQL防火墙) └─ log4j → filter.log4j.xxx (SQL日志) pool-prepared-statements (PSCache开关) ├─ max-open-prepared-statements (全局上限) └─ max-pool-prepared-statement-per-connection-size (单连接上限)2.2 核心关联链路详解
链路 1:连接池容量控制
max-active (上限) ├─ ≥ initial-size (启动创建量) ├─ ≥ min-idle (最低水位) └─ + max-wait → getConnection 超时策略max-active决定了connections[]数组的容量,initial-size和min-idle不能超过它。max-wait控制池满或无法建连时的等待超时。
链路 2:故障恢复(生产故障核心)
break-after-acquire-failure = true ├─ connection-error-retry-attempts = 3 (失败3次) └─ time-between-connect-error-millis = 10000 (间隔10秒) ↓ 建连失败 → 等10秒 → 重试 → 重复3次 → break=true ↓ CreateConnectionThread 死亡 ↓ CreatingCount 永久 = 0 ↓ getConnection 发 empty.signal() 无人响应 ↓ max-wait(10秒) 到期 → GetConnectionTimeoutException这是生产故障的核心链路:Oracle 卡住 → 建连失败 3 次 → 线程死亡 → 池子有空位但建不了连 → 超时。
链路 3:连接验证与回收
time-between-eviction-runs-millis = 60000 (每60秒检查一次) │ ├─ 对空闲连接(pooling): │ ├─ test-while-idle=true → 执行 validation-query 验证 │ │ ├─ 验证通过 → 保留 │ │ └─ 验证失败 → 丢弃(DiscardCount++) │ ├─ 空闲时间 > min-evictable(5分钟) → 可回收 │ └─ 空闲时间 > max-evictable(15分钟) → 强制回收 │ └─ 对活跃连接(active): └─ remove-abandoned=true 时 → 借出 > remove-abandoned-timeout(5分钟) → 强制回收链路 4:Filter 与监控
filters = stat,wall │ ├─ stat → 数据收集 │ ├─ stat-view-servlet → /druid/* 监控页 │ ├─ time-between-log-stats-millis → 统计日志输出 │ ├─ filter.stat.slow-sql-millis → 慢SQL标记 │ └─ web-stat-filter → Web请求维度统计 │ └─ wall → SQL防火墙 └─ 拦截不合规SQL(如 BEGIN...END 匿名块)三、连接池内部结构
DruidDataSource 内部维护一个DruidConnectionHolder[] connections数组,这就是"池子"。数组被两个游标分为三个区域:
connections[] 数组: ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐ │ P0 │ P1 │ P2 │ A3 │ A4 │ A5 │ 空位 │ 空位 │ └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘ ←── poolingCount=3 ──→ ←── activeCount=3 ──→ ←── 空位 ──→ ↑ maxActive=8, 已用6个, 剩余2个空位- 前
poolingCount个:空闲连接(Pooling),等着被取走 - 中间
activeCount个:已借出的连接(Active),正在被业务使用 - 后面:空位,可以放入新建的连接
约束:poolingCount + activeCount ≤ maxActive
四、getConnection 决策流程
getConnection(maxWait)的核心逻辑有三条路径:
getConnection(maxWait=10000) │ ├─ 路径A: poolingCount > 0? │ ├─ YES → 从 connections[poolingCount-1] 取走连接 │ │ → poolingCount--, activeCount++ │ │ → (remove-abandoned=true 时记录活跃堆栈) │ │ → 返回连接 (0ms) ✅ │ │ │ └─ NO → 进入路径B/C判断 │ ├─ 路径B: poolingCount=0, activeCount < maxActive? │ ├─ YES → empty.signal() ← 唤醒 CreateConnectionThread 去建连 │ │ → notEmpty.park(maxWait) ← 当前线程等待 │ │ │ │ │ ├─ 建连成功 → notEmpty.signal() → 拿到新连接 ✅ │ │ └─ maxWait超时 → GetConnectionTimeoutException ❌ │ │ │ └─ NO → 进入路径C │ └─ 路径C: poolingCount=0, activeCount=maxActive (池满) │ → notEmpty.park(maxWait) ← 纯等别人归还,不建连 │ ├─ 有人归还 → notEmpty.signal() → 拿到归还的连接 ✅ │ └─ maxWait超时 → GetConnectionTimeoutException ❌路径B的子场景:break=true(生产故障)
路径B判断: poolingCount=0, activeCount=25 < maxActive=120 → empty.signal() ← 发出信号 → notEmpty.park(10000) ← 等待 │ ├─ CreatingCount=1 (线程活着)? │ → CreateConnectionThread 收到信号,尝试建连 │ └─ CreatingCount=0 (线程已死, break=true)? → empty.signal() 发了但无人响应 ❌ → 10秒后 → GetConnectionTimeoutException → 堆栈中包含业务代码 (如 user_row_qry)关键:路径B和break=true子场景的连接池状态完全相同(pooling=0, active<max),从 Druid 监控页看到的ActiveCount和PoolingCount分不出区别。唯一的判断依据是CreatingCount。
五、三种场景连接池状态变化
场景A:正常取连接(poolingCount > 0)
Before: After: ┌────┬────┬────┬────┬────┐ ┌────┬────┬────┬────┬────┐ │ P0 │ P1 │ A2 │ A3 │ 空 │ │ P0 │ A1 │ A2 │ A3 │ 空 │ └────┴────┴────┴────┴────┘ └────┴────┴────┴────┴────┘ pooling=2 active=2 pooling=1 active=3 maxActive=5 maxActive=5 → 从 connections[1] 取走 P1 → P1 变成 A1 → poolingCount 2→1, activeCount 2→3 → 0ms 返回 ✅场景B:池空建新连(pooling=0, active < max, 线程活着)
Before: After: ┌────┬────┬────┬────┬────┐ ┌────┬────┬────┬────┬────┐ │ A0 │ A1 │ A2 │ 空 │ 空 │ │ A0 │ A1 │ A2 │ A3 │ 空 │ └────┴────┴────┴────┴────┘ └────┴────┴────┴────┴────┘ pooling=0 active=3 pooling=0 active=4 maxActive=5 maxActive=5 → empty.signal() → CreateConnectionThread 建连 → 新连接填入 connections[3] → 变成 A3 → activeCount 3→4 ✅场景C:生产故障(pooling=0, active < max, 线程已死)
Before: After (10秒后): ┌────┬────┬────┬────┬────┐ ┌────┬────┬────┬────┬────┐ │ A0 │ A1 │ A2 │ 空 │ 空 │ │ A0 │ A1 │ A2 │ 空 │ 空 │ └────┴────┴────┴────┴────┘ └────┴────┴────┴────┴────┘ pooling=0 active=3 pooling=0 active=3 maxActive=5 maxActive=5 CreatingCount=0 (线程已死!) → 10秒超时 ❌ → empty.signal() 发出但无人响应 → notEmpty.park(10000) 等待 → 10秒后 GetConnectionTimeoutException → 池状态无任何变化(有2个空位但填不进去)场景B和C的连接池状态完全相同,唯一区别是CreatingCount:B=1(活着),C=0(已死)。
六、连接完整生命周期
连接的生命周期由 Druid 内部三个线程协作管理:
6.1 阶段一:创建(CreateConnectionThread)
┌─────────────────────────────────────────────────────────┐ │ CreateConnectionThread │ │ │ │ 触发方式1: init() 启动时 │ │ → 循环创建 initial-size(40) 个连接 │ │ → 全部放入 connections[] → poolingCount = 40 │ │ │ │ 触发方式2: 主动补充 │ │ → 循环检查: pooling+active < min-idle(40) ? │ │ → YES 且 < max-active → createPhysicalConnection() │ │ → 放入 connections[] → poolingCount++ │ │ → 继续检查直到 ≥ min-idle 或 ≥ max-active │ │ → empty.await() 等待下次信号 │ │ │ │ 触发方式3: 被动响应 │ │ → 收到 getConnection 的 empty.signal() │ │ → 如果 active+pooling < max-active │ │ → createPhysicalConnection() │ │ → 放入 connections[] → poolingCount++ │ │ → notEmpty.signal() → 唤醒等待的 getConnection │ │ │ │ ⚠️ 故障处理: │ │ → 建连失败 → 等 time-between-error(10s) │ │ → 重试 retry-attempts(3) 次 │ │ → 超过 → break=true → 线程死亡 │ │ → CreatingCount 永久=0 │ │ │ │ 涉及配置: │ │ initial-size, min-idle, max-active, │ │ break-after-acquire-failure, │ │ connection-error-retry-attempts, │ │ time-between-connect-error-millis │ └─────────────────────────────────────────────────────────┘6.2 阶段二:使用(业务线程借还循环)
┌─────────────────────────────────────────────────────────┐ │ 业务线程借还循环 │ │ │ │ 借出 (getConnection): │ │ → poolingCount > 0? │ │ │ YES → 取 connections[poolingCount-1] │ │ │ poolingCount--, activeCount++ │ │ │ (remove-abandoned=true 时记录活跃堆栈) │ │ │ → 返回连接 │ │ │ │ │ │ NO → activeCount < maxActive? │ │ │ YES → empty.signal() + notEmpty.park(maxWait) │ │ │ → 拿到新连接 or 超时 │ │ │ NO → notEmpty.park(maxWait) 等归还 │ │ │ → 拿到归还连接 or 超时 │ │ │ │ │ 使用: │ │ → test-on-borrow=true? 执行 validation-query 验证 │ │ → 执行业务 SQL │ │ → SQL 执行完毕 │ │ │ │ 归还 (recycle): │ │ → test-on-return=true? 执行 validation-query 验证 │ │ │ 通过 → 放回 connections[] → poolingCount++ │ │ │ 失败 → 丢弃连接 → DestroyCount++ │ │ │ │ │ → test-on-return=false → 直接放回池中 │ │ → notEmpty.signal() → 唤醒等待的 getConnection │ │ │ │ connections[] 数组状态变化: │ │ 借出前: |P0|P1|P2|A3|A4| pooling=3, active=2 │ │ 借出后: |P0|P1|A2|A3|A4| pooling=2, active=3 │ │ 归还后: |P0|P1|P2|A3|A4| pooling=3, active=2 │ │ │ │ 涉及配置: │ │ max-wait, test-on-borrow, test-on-return, │ │ validation-query, validation-query-timeout, │ │ remove-abandoned, log-abandoned │ └─────────────────────────────────────────────────────────┘6.3 阶段三:销毁(DestroyThread)
┌─────────────────────────────────────────────────────────┐ │ DestroyThread │ │ │ │ 心跳: 每 time-between-eviction-runs-millis(60s) 唤醒 │ │ │ │ 对空闲连接(pooling)处理: │ │ 遍历 connections[0..poolingCount-1]: │ │ │ │ │ ├─ test-while-idle=true? │ │ │ → 执行 validation-query 验证 │ │ │ → 验证超时(>validation-query-timeout)? │ │ │ YES → 丢弃 → DiscardCount++ │ │ │ → 验证失败(SQL异常)? │ │ │ YES → 丢弃 → DiscardCount++ │ │ │ │ │ ├─ 空闲时间 > min-evictable(5分钟)? │ │ │ YES → 可回收 → physicalConnection.close() │ │ │ → DestroyCount++ │ │ │ │ │ ├─ 空闲时间 > max-evictable(15分钟)? │ │ │ YES → 强制回收 → physicalConnection.close() │ │ │ → DestroyCount++ │ │ │ │ │ ├─ phy-timeout-millis > 0 且 累计存活 > phy-timeout? │ │ │ YES → 回收(与空闲时间无关) │ │ │ │ │ └─ keep-alive=true 且 超过 keep-alive-between? │ │ → 执行 validation-query 保活(不回收) │ │ → KeepAliveCheckCount++ │ │ │ │ 对活跃连接(active)处理: │ │ 遍历 connections[poolingCount..poolingCount+active-1]: │ │ │ │ │ └─ remove-abandoned=true? │ │ → 借出时间 > remove-abandoned-timeout(300秒)? │ │ YES → 强制回收连接 │ │ → log-abandoned=true 时输出借出时的堆栈 │ │ → RemoveAbandonedCount++ │ │ → 回收后 notEmpty.signal() │ │ │ │ 最终: physicalConnection.close() → 物理连接断开 │ │ │ │ 涉及配置: │ │ time-between-eviction-runs-millis, │ │ test-while-idle, validation-query, │ │ validation-query-timeout, │ │ min-evictable-idle-time-millis, │ │ max-evictable-idle-time-millis, │ │ phy-timeout-millis, keep-alive, │ │ keep-alive-between-time-millis, │ │ remove-abandoned, remove-abandoned-timeout, │ │ log-abandoned │ └─────────────────────────────────────────────────────────┘6.4 三线程协调关系
connections[] 数组 ┌─────────────────┐ CreateThread ────→│ 添加连接(+) │ │ │ 业务线程 ←───→│ 借出/归还 │ │ │ DestroyThread ──→│ 移除连接(-) │ └─────────────────┘ 协调信号: empty Condition: CreateThread 等待/唤醒 notEmpty Condition: 业务线程的 getConnection 等待/唤醒 数据流: CreateThread → connections[] → 业务线程(借出) ↓ 业务线程(归还) → connections[] ↓ DestroyThread ← connections[] (定期扫描回收)三个线程各司其职:
- CreateConnectionThread:往池里加连接(init创建 + 补充到minIdle + 响应signal建连)
- 业务线程:从池里借/还连接(getConnection + recycle)
- DestroyThread:从池里移除连接(验证失败回收 + 空闲超时回收 + 泄漏回收 + 物理超时回收)
七、活跃连接堆栈
监控的是什么
每个被借出(active)的连接,在借出那一刻的调用线程堆栈。当某个线程调用getConnection()拿走一个连接还没归还时,Druid 记录这个线程当时的完整堆栈——哪个类、哪个方法、第几行代码借的连接。
开启条件
只有remove-abandoned: true时,Druid 才会在每次getConnection()时记录堆栈。remove-abandoned: false时活跃堆栈页面为空。
spring:datasource:druid:remove-abandoned:trueremove-abandoned-timeout:300log-abandoned:true记录时机
时间线: T1: 线程A调用 getConnection() ├─ poolingCount=0, 有空位 ├─ empty.signal() → CreateConnectionThread 开始建物理连接 ├─ 线程A notEmpty.park() 等待 │ T2: CreateConnectionThread 建连成功 ├─ 新连接放入 connections[] 数组 ├─ poolingCount++ ├─ notEmpty.signal() │ T3: 线程A 被唤醒 ├─ 从 connections[] 取走这个新连接 ├─ poolingCount--, activeCount++ ├─ ★ 这里记录活跃堆栈 ★ ← 是这一步! ├─ 返回连接给业务代码 └─ 线程A拿着连接去执行 SQL关键:堆栈记录的是线程A在T3时刻拿到连接的调用栈(“谁在调 getConnection”),不是 CreateConnectionThread 的内部建连堆栈。
用途
| 用途 | 说明 |
|---|---|
| 连接泄漏定位 | 连接借走后长时间不归还,看堆栈就知道是哪段代码借的 |
| 慢SQL占用诊断 | activeCount=25 时,看25个连接分别卡在哪段代码 |
| 故障类型区分 | 池耗尽(120个堆栈全是慢SQL) vs 建连线程终止(25个堆栈, pooling=0, creatingCount=0) |
访问方式
# 监控页 http://<host>:<port>/druid/datasource.html → 点击数据源 → "活跃连接堆栈" # JSON 接口 http://<host>:<port>/druid/activeConnectionStackTrace.json性能开销
每次getConnection()记录一次Thread.getStackTrace(),开销在微秒级,对性能影响可忽略。生产环境建议开启remove-abandoned: true。
八、生产故障关键判断速查
Druid 监控项判断表
| PoolingCount | ActiveCount | CreatingCount | 判断 |
|---|---|---|---|
| > 0 | 任意 | 0 或 1 | ✅ 正常,取连接 0ms |
| 0 | < maxActive | 1 | ⚠️ 池空了,正在建连 |
| 0 | < maxActive(如 25/120) | 0 | ❌CreateConnectionThread 已终止,break=true 触发过 |
| 0 | = maxActive | 0 | 连接池耗尽(非本次故障场景) |
生产故障状态
ActiveCount = 25/120 (21%) ← 远未占满 PoolingCount = 0 ← 没有空闲连接 CreatingCount = 0 ← 建连线程已死 CreateErrorCount > 0 ← 建连失败过 → 有 95 个空位但建连线程已死,无法填充池子 → getConnection 10秒超时 → GetConnectionTimeoutException → 堆栈含 user_row_qry 业务代码常见误区
| 误区 | 正确理解 |
|---|---|
| “poolingCount=0 说明连接池耗尽了” | 不一定,active<max 且 creating=0 才是线程终止;active=max 才是池耗尽 |
| “停掉监听能复现生产故障” | lsnrctl stop 只阻断新连接握手,已有连接不受影响,无法复现 |
| “min-idle 会一直补充到 minIdle 个” | 前提是 active+pooling < maxActive,池满时不触发 |
| “建连超时就是超时报错” | TCP拒绝是瞬间失败,日志是"create connection error"不是"timeout" |
| “活跃堆栈记录的是建连过程” | 记录的是业务线程拿到连接的调用栈,不是建连线程的内部堆栈 |
文档结束
本文档基于甘肃农信 xdxt 系统生产故障分析整理,涵盖 Druid 1.2.22 全部配置项、配置间关联关系、连接池内部结构、getConnection 决策流程、三种场景对比、连接完整生命周期、活跃连接堆栈机制等内容。