Druid配置项与连接生命周期全解析

📅 2026/7/18 18:35:47 👁️ 阅读次数 📝 编程学习
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 < maxActive40
max-active最大活跃连接数,连接池容量上限,决定connections[]数组大小120

约束关系initial-sizemin-idlemax-active

⚠️ 易误解min-idle的补充逻辑有前提条件——只有activeCount + poolingCount < maxActive(池未满)时,CreateConnectionThread 才会创建连接补充到minIdle。池满时直接empty.await()阻塞,不会建连。


1.2 连接获取

配置项作用生产值
max-waitgetConnection()最大等待毫秒数,超时抛GetConnectionTimeoutException10000

机制:控制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-attemptstime-between-error才有意义。

链路

建连失败 → 等 time-between-error(10s) → 重试 → 重复 retry-attempts(3) 次 → break=true → CreateConnectionThread 死亡 → CreatingCount 永久=0

1.4 连接验证

配置项作用生产值
validation-query验证用的 SQLSELECT 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-millisDestroyThread 检查间隔(所有回收逻辑的心跳周期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是否开启 PSCachetrue
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是否记录慢 SQLtrue
filter.stat.merge-sql是否合并相似 SQLtrue
filter.wall.xxxSQL 防火墙配置启用
filter.log4j.xxxSQL 日志配置

⚠️ 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/denyIP 白名单/黑名单
stat-view-servlet.reset-enable是否允许重置统计true
web-stat-filter.enabled是否开启 Web 请求监控(独立于 stat-view-servlettrue
web-stat-filter.url-patternWeb 监控 URL 模式/*
web-stat-filter.session-stat-enable是否统计 Sessiontrue
time-between-log-stats-millis统计日志输出间隔60000
aop-patternsAOP 监控的包路径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-nameJDBC 驱动类oracle.jdbc.OracleDriver
url连接 URLjdbc:oracle:thin:@db.xdxt.gsnx.dc:1522/xdxt
username/password认证凭据xdxt / ****
connection-propertiesJDBC 连接属性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-sizemin-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 监控页看到的ActiveCountPoolingCount分不出区别。唯一的判断依据是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 监控项判断表

PoolingCountActiveCountCreatingCount判断
> 0任意0 或 1✅ 正常,取连接 0ms
0< maxActive1⚠️ 池空了,正在建连
0< maxActive(如 25/120)0CreateConnectionThread 已终止,break=true 触发过
0= maxActive0连接池耗尽(非本次故障场景)

生产故障状态

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 决策流程、三种场景对比、连接完整生命周期、活跃连接堆栈机制等内容。