Druid 连接池调优实战:连接获取丝滑到毫秒级
【免费下载链接】druid阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池项目地址: https://gitcode.com/gh_mirrors/druid/druid
高并发下页面一卡,慢的到底是 SQL,还是getConnection()前面的等待?Druid 是一个为监控而生的数据库连接池:DruidDataSource不仅管住连接的借还,还能告诉你哪条 SQL 慢、哪个连接被泄漏了。
它到底在解决什么
Druid 是一个 Java 数据库连接池,核心实现是DruidDataSource。它干三件事:管理物理连接的完整生命周期(预创建、借用、归还、驱逐);内置 Filter 链采集 SQL 执行统计、慢 SQL 与 Web 请求数据;把这些全部暴露到 JMX 和内置 Web 监控页。有了它,数据库层从黑盒变成一块仪表盘。
核心机制拆解
机制一:连接获取——定长数组 + 等待唤醒
一句话:连接放在定长数组里,poolingCount是空闲边界,空了就把线程挂进等待队列,靠"连接归还"信号唤醒,不空转。
while (poolingCount == 0) { emptySignal(); // 通知后台创建新连接 notEmpty.await(estimate, MILLISECONDS); // 等归还信号,maxWait 兜底 } holder = connections[poolingCount]; // 取走空闲位并转为活跃为什么这样设计:ReentrantLock+Condition替代轮询,归还端与创建端互相signal,正常负载下getConnection()一次都不空转;超时任用maxWait控制,超时异常里直接带着 active / maxActive 现场信息。
机制二:SQL 统计——无锁累加 + 直方图
一句话:每次执行都进JdbcSqlStat.addExecuteTime,用 CAS 累加总耗时,并落进 0-1ms / 1-10ms / … / 1s+ 的直方图。
public void addExecuteTime(long nanoSpan) { executeSpanNanoTotalUpdater.addAndGet(this, nanoSpan); // 无锁累加 histogramRecord(nanoSpan); // 按桶计数 }为什么这样设计:统计走AtomicLongFieldUpdater,高 QPS 下不抢锁;存直方图桶而非每次采样值,内存恒定。监控页上的"请求时间(最慢)""总时间"两列,就是这两个字段的换算结果。
从零到跑通
1. 加依赖
Spring Boot 项目引入一个 starter 即可,Boot 3 以上换druid-spring-boot-3-starter:
<dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.17</version> </dependency>2. 配置数据源与监控
spring.datasource.url / username / password照旧填,再叠加 druid 参数:
spring: datasource: druid: initial-size: 5 # 初始连接数 max-active: 20 # 最大连接数 max-wait: 60000 # 获取超时(ms),避免无限等待 validation-query: SELECT 1 test-while-idle: true # 空闲检测,推荐开启 filters: stat,wall # stat 统计,wall SQL 防火墙 stat-view-servlet: enabled: true login-username: admin login-password: your_password3. 启动验证
java -jar app.jar # 浏览器打开 http://127.0.0.1:8080/druid/index.html用账号密码登录后,首页 Stat Index 能看到数据源的ActiveCount(使用中)、PoolingCount(空闲)、WaitThreadCount(等待获取的线程);"Web应用"菜单下每个 URI 的请求次数与时间分布一目了然,慢请求不用猜。
踩坑与调优
坑一:获取超时 GetConnectionTimeoutException
症状:高峰期借连接超时,异常信息形如wait millis xxx, active 20, maxActive 20。根因:多数是连接泄漏或慢 SQL 长时间占用。修复:开发环境开泄漏检测,打印借出堆栈定位,生产别开:
ds.setRemoveAbandoned(true); // 回收未归还的连接 ds.setRemoveAbandonedTimeout(300); // 回收阈值(秒) ds.setLogAbandoned(true); // 泄漏时打印借用堆栈坑二:借到死连接 Communications link failure
症状:偶发Communications link failure或 connection reset。根因:MySQLwait_timeout早已掐断空闲连接,池里没察觉。修复:空闲检测 + 保活,别用testOnBorrow,它每次借都跑一次检测 SQL:
spring: datasource: druid: test-while-idle: true keep-alive: true坑三:慢 SQL 只有数字没有参数
症状:监控页看到某条 SQLMaxTimespan秒级,却说不清是哪些参数慢。根因:慢 SQL 日志默认关闭,且阈值默认 3 秒。修复:调低阈值并打开日志:
spring: datasource: druid: filter: stat: slow-sql-millis: 500 log-slow-sql: true怎么配 / 怎么选
连接池参数调优里变化最大的变量是检测策略,先看 QPS 再选:
| 策略 | 触发时机 | 性能开销 | 适用场景 |
|---|---|---|---|
testOnBorrow | 每次借出 | 每次都执行检测 SQL,最大 | 低 QPS 高可靠事务链路 |
testWhileIdle | 借出时若空闲超阈值 | 只检测空闲连接,小 | 多数生产环境 |
keepAlive | 定时对空闲连接发心跳 | 最小 | 连接常空闲、DB 侧会主动断开 |
| 推荐组合 | testWhileIdle+keepAlive+maxWait=60s | 最小 | 生产默认,必须配validationQuery |
另一个容易配错的是 PSCache(预编译语句缓存):MySQL 不建议开,Oracle / PostgreSQL 建议开;调用setMaxPoolPreparedStatementPerConnectionSize(n)(n>0)会自动开启,不用再手动设poolPreparedStatements。
延伸与参考
- doc/connection-pool-guide.md:容量、检测、回收全参数清单与默认值
- doc/monitoring-guide.md:监控页面、JSON API 与 Prometheus 对接
- doc/wall-security-guide.md:Wall 防火墙配置,拦截危险 SQL 与注入
- druid-demo-petclinic/:Spring Boot 集成 Druid 的可运行示例
- core/src/main/java/com/alibaba/druid/pool/DruidDataSource.java:核心源码,借还、驱逐、检测都在这里
【免费下载链接】druid阿里云计算平台DataWorks(https://help.aliyun.com/document_detail/137663.html) 团队出品,为监控而生的数据库连接池项目地址: https://gitcode.com/gh_mirrors/druid/druid
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考