Spring Boot 2.x整合Druid数据源:连接池配置与监控实战

发布时间:2026/10/13 4:06:26
Spring Boot 2.x整合Druid数据源:连接池配置与监控实战
1. 为什么要专门讲一次 Datasource 的创建1.1 直连数据库的问题就出在“反复建立连接”上很多人刚开始学 Spring Boot 时写数据访问层的代码都是先从 JDBC 的DriverManager.getConnection开始的。我见过不少项目联调环境一切正常一上并发测试就开始报Connection is not available, request timed out或者数据库端直接提示too many connections。问题往往不在 SQL 本身的效率而在于一条连接的生命周期管理得太随意每来一个请求就现开一条数据库连接用完就close掉。数据库连接的建立和销毁成本比你想象的高很多它要经历 TCP 三次握手、服务端认证、系统变量初始化、字符集设置等一整套链路高频请求下来完全是在浪费资源。连接池解决的就是这个复用问题。池子在启动阶段预先创建一批连接放在那里请求来了从里面取用完再放回去连接始终保活只有空闲太久或者确认失效时才真正关闭。这个原理听起来很简单真正落地时却有不少细节要敲定池子初始创建几条连接最大能膨胀到多少条拿不到连接时是立刻报错还是排队等待连接空闲多久算失效这些问题如果全用默认值糊弄过去生产环境迟早会用故障的方式来提醒你。1.2 HikariCP 很好但 Druid 补齐了“可视化”这块短板Spring Boot 2.x 默认的数据源是 HikariCP它经常标榜自己是“史上最快连接池”。从我实际跑过的压力测试看单论getConnection的吞吐和延迟HikariCP 的表现确实很稳。可它有一个短板对连接池内部运行情况基本是黑盒没有自带可视化监控面板也没有 SQL 级别的统计和拦截能力。你怀疑某个接口占住连接不放或者想看看哪条 SQL 慢得离谱用默认数据源很难直观定位。Druid 是 Alibaba 开源的数据库连接池组件在 Spring Boot 生态里用得非常多。它除了做连接复用还自带了 SQL 拦截、慢 SQL 统计、监控面板、WallFilter 防火墙等功能。一个池子把连接管理、性能分析和安全拦截都覆盖了对于需要做 SQL 治理和容量评估的中大型项目非常实用。这一篇就基于 Spring Boot 2.x从零把 Druid 数据源建起来并把它的监控核心能力真正跑通。2. 环境准备与依赖引入2.1 版本选择别让 Spring Boot 版本拖后腿这一篇既然是 SpringBoot2.x 系列构建环境我就默认是 JDK 8 到 JDK 11 这个范围Spring Boot 版本建议选 2.3.x 到 2.7.x 之间。为什么刻意避开 2.6 之前的太老版本和 2.8 之后的变更版本因为 Spring Boot 3.x 已经把javax包名换成了jakarta大量组件的兼容性处理完全是另一套话题Druid 的配置方式也会有差异。正在维护 Spring Boot 2.x 项目的建议直接选官方druid-spring-boot-starter版本用 1.2.x 系列比如 1.2.15 之后的稳定版本。依赖配置用 Maven 示范dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.15/version /dependency这里要特别强调druid-spring-boot-starter不是普通的功能等价物它比druid-core多了 Spring Boot 自动装配逻辑会把spring.datasource.druid.*配置自动映射到 DruidDataSource 的属性上。如果只引了druid-core很多配置项就得自己写配置类去绑定不仅麻烦还容易因为配置项拼写错误排查半天。数据库驱动部分以最常用的 MySQL 举例dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependencySpring Boot 2.x 的依赖管理已经帮你锁定了兼容版本如无特殊需要自己不用重写version。PostgreSQL、Oracle 这些数据库同理连接池原理一致只是 URL 和驱动类名不同。2.2 先想清楚你到底要不要写 DataSource Bean很多教程为了展示“如何创建 Druid 数据源”会在代码里写一个Configuration类Bean ConfigurationProperties(prefix spring.datasource) public DataSource dataSource() { return new DruidDataSource(); }这种方式不是不能用但我要提醒一句如果你已经加了druid-spring-boot-starter再用这段代码Spring 容器里很可能出现两个数据源实例。一个来自自动配置一个来自你手写的 Bean。大部分情况下业务操作不会立刻爆炸但连接统计、事务切换、监控面板展示都会变得混乱。我见过有人一边用 starter一边保留了手动 Bean结果 Druid 监控页里有两个数据源条目其中一个始终没有物理连接排查了半天才明白是重复注册。所以正确的思路是能用 starter 自动配置解决的就别手写。博主设计这个系列教程时第五篇特意把“创建 Datasource”单独拿出来讲就是因为这里最容易出现“会配置却不懂原理”的夹生状态。我们先把自动配置跑通再谈要不要手写额外 Bean。3. 创建 Druid DataSource 的两条路线3.1 最基础的配置让 Spring Boot 认准 Druid先看最直接的做法在application.yml里配置spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password为什么一定要写spring.datasource.typeSpring Boot 2.x 的自动数据源配置优先走 HikariCP哪怕 classpath 里已经有了 Druid 的类你不写这个type默认池子还是 HikariCP。只有显式告诉容器“我要用 Druid”后续的过滤器链、监控统计、防火墙拦截才能跟着激活。这一步看似简单却是很多新人折腾一下午最后发现“监控面板里啥都没有”的根本原因。只做这一步Druid 已经能作为连接池正常工作了但各种参数还是默认值。默认initialSize是 0启动时不会预创建连接要等第一个请求来了才现建这会让接口首次访问偏慢。要做完整配置继续往下走。3.2 更完整的配置用官方 Starter 的 Druid 配置入口引了druid-spring-boot-starter之后推荐这样配置spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/demo?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: your_password druid: initial-size: 5 min-idle: 5 max-active: 20 max-wait: 60000 time-between-eviction-runs-millis: 60000 min-evictable-idle-time-millis: 300000 validation-query: SELECT 1 test-while-idle: true test-on-borrow: false test-on-return: false pool-prepared-statements: true max-pool-prepared-statement-per-connection-size: 20 filters: stat,wall,log4j2 connection-properties: druid.stat.mergeSqltrue;druid.stat.slowSqlMillis5000spring.datasource.druid.*这套前缀是 starter 提供的配置入口它会把参数映射到DruidDataSource对应的属性。filters: stat,wall,log4j2分别代表统计过滤器、SQL 防火墙、日志输出器。这里最容易犯的错是漏掉stat一旦漏了监控页面的 SQL 统计就是空的。很多文章只会告诉你“加上 filters”却不说清楚每个 filter 到底管什么这正是我后面要重点拆解的地方。4. 关键参数和背后的逻辑4.1 连接池容量不是拍脑袋定的参数调整最怕的是“听说某个值好就直接抄”。实际要结合并发量、数据库负载、MySQL 最大连接数上限来决定。MySQL 默认的max_connections通常是 151如果你的应用起了多个实例每台机器再把max-active配到 200数据库服务器分分钟会拒绝新连接。合理的思路是先统计 QPS 和单请求平均持库时间再按“MaxActive ≈ QPS × 单请求持库时间”来粗略估算并预留 20% 到 30% 的余量。几个核心参数我用表格整理一下方便对照参数含义调优建议initial-size启动时预创建的物理连接数一般和 min-idle 保持一致min-idle池中至少要保留的空闲连接数建议等于 initial-size 或略低max-active最大活跃连接数先压测再设置别随手写个大数max-wait获取连接的最大等待毫秒数建议 60000 左右设为 0 表示无限等待time-between-eviction-runs-millis空闲连接检测周期默认 60000一般不用动min-evictable-idle-time-millis连接最多空闲多久会被回收建议低于数据库 wait_timeout 的一半validation-query连接有效性校验 SQLMySQL 用 SELECT 1Oracle 用 SELECT 1 FROM DUALmin-idle和max-active的关系很容易理解一个是平峰水位一个是高峰天花板。平峰请求少连接池会自动把空闲连接回收到min-idle附近并发上来后连接池会快速创建新连接直到max-active。你要关心的不只是这两个数字而是数据库服务本身能否扛住这个水位。4.2 test-while-idle、test-on-borrow、test-on-return 怎么选这三个参数经常让人犯迷糊。test-on-borrow表示每次从池子里拿连接时先执行validation-query校验一次这样拿到的连接保证是好的代价是多一次数据库交互高并发下整体吞吐会被拖低。test-while-idle由后台线程在做空闲连接扫描时触发发现连接空闲时间过长才校验一次属于低成本采样。test-on-return则是在连接归还时校验同样增加开销。我的建议是常规业务打开test-while-idle: truetest-on-borrow和test-on-return保持 false。如果网络质量差、数据库经常主动断开空闲连接或者有跨地域访问数据库的场景可以开test-on-borrow换取更高可靠性同时接受额外开销。这里没有绝对正确的配置只有适不适合自己的业务场景。4.3 慢 SQL 统计和 WallFilter 的意义Druid 的filters配置虽然只是几个单词实际是把不同类型的拦截器挂到连接池的 filter 链上。stat负责统计 SQL 执行次数、执行耗时、并发数、错误数最终生成监控页面上的数据wall基于 SQL 语法分析做防火墙能拦截常见的注入攻击log4j2则是用日志驱动的接口输出相关事件。connection-properties里还有一个值得留意的配置druid.stat.slowSqlMillis5000意思是执行时间超过 5000 毫秒的 SQL 会被单独记录到慢 SQL 列表。druid.stat.mergeSqltrue则会把结构相近但参数值不同的 SQL 做聚合避免同一查询因为参数不同就在列表里刷出几十条。我实际用下来的经验是慢 SQL 阈值别定太低比如 200 毫秒否则监控面板里全是正常小查询噪音太多反而掩盖了真问题。5. 把 Druid 监控页面真正用起来5.1 开启 StatViewServlet别漏了登录配置监控面板是 Druid 最有辨识度的能力。引入依赖不等于自动有页面必须显式开启spring: datasource: druid: stat-view-servlet: enabled: true url-pattern: /druid/* reset-enable: false login-username: admin login-password: your_strong_password allow: 127.0.0.1 deny:enabled: true启动监控页。url-pattern: /druid/*页面访问路径用习惯的路径就行。reset-enable: false禁用“重置”按钮防止监控数据被人一键清空。login-username/login-password页面登录凭证千万别用 admin/admin 这种组合。allow允许跳转到页面的客户端 IP多个用逗号分隔。配置完成后启动应用浏览器访问http://localhost:8080/druid/login.html输入账号密码就能看到仪表盘。左侧菜单有数据源、SQL 监控、URI 监控、Session 监控等最常用的是“数据源”和“SQL 监控”两块。生产环境部署时特别注意监控页面默认路径并不复杂非常容易被扫描到只开放给内网或者运维专用跳板机访问是很必要的安全底线。如果项目挂在外网一定要在网关层做访问控制不能仅仅依赖 Druid 自身的allow配置。5.2 数据源页和 SQL 监控页怎么读数据源页显示的字段很多我一般重点看这几个字段关注原因初始连接数是否和配置一致如果为 0说明连接没预创建活跃连接数高峰时是否接近 max-active长期打满要警惕等待线程数长期不为 0 说明池子在排队容量吃紧逻辑连接打开/关闭数两组数应大致相等打开远多于关闭要排查连接泄漏物理连接打开/关闭数差值代表当前实际持有的数据库连接数理解“逻辑连接”和“物理连接”的差别是关键。逻辑连接是业务代码里拿到的连接对象物理连接才是真正和数据库建立的 TCP 连接。逻辑连接打开多、关闭少说明有代码借了连接没还。连接泄漏的早期表现不剧烈往往是过一段时间后突然报超时活跃连接曲线直冲max-active这时去数据库PROCESSLIST里翻大概率能看到一批长时间Sleep的会话。SQL 监控页则提供了每个 SQL 的执行次数、总耗时、最大耗时、错误数。排查性能问题时会先去按“最大耗时”排序把排在前面的几条拿出来分析执行计划。这个页面为空时第一反应应该是检查filters是否包含stat其次再去看WebStatFilter的配置是否正确。6. 实操中常见的几个坑与排错思路6.1 启动后发现用的还是 HikariCP我在前文反复强调spring.datasource.type因为这是新手最高频的陷阱。有人在pom.xml里加了依赖但配置里没有显式写type启动日志里打印出来的DataSource类名还是 Hikari。最直接的排查方式是写一段启动探针SpringBootApplication public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext context SpringApplication.run(DemoApplication.class, args); DataSource ds context.getBean(DataSource.class); System.out.println(DataSource Class ds.getClass().getName()); } }如果输出不是com.alibaba.druid.pool.DruidDataSource说明配置没生效。加一行spring.datasource.type: com.alibaba.druid.pool.DruidDataSource重启再验证一次。建议把它写成单元测试断言防止后续代码合并时有人误改。6.2 SQL 监控为空页面却正常显示页面能打开数据源状态正常但 SQL 监控列表空荡荡。优先检查两点一是filters是否包含stat二是web-stat-filter的 url-pattern 是否覆盖了应用接口路径。配置参考spring: datasource: druid: web-stat-filter: enabled: true url-pattern: /* exclusions: *.js,*.gif,*.jpg,*.css,*.ico,/druid/*这个exclusions的作用是排除静态资源和监控页自身的访问不然监控统计里会混入大量静态资源请求。如果你用 MyBatis 或 JPASQL 最终会经过连接池执行stat能捕获到但如果你绕开了 DruidDataSource比如自己 new 了一个原生连接那部分 SQL 是永远进不了 Druid 统计的。6.3 连接数持续上升最终连服务都起不来这类问题最常见的根因还是连接泄漏。先看监控页的活跃连接数如果长期接近max-active去别处查看数据库会话再看业务代码里有没有用 try-with-resources 保证连接关闭。Druid 提供一个兜底配置spring: datasource: druid: remove-abandoned: true remove-abandoned-timeout: 180 log-abandoned: trueremoveAbandoned会主动回收超过removeAbandonedTimeout秒仍未归还的连接logAbandoned会在回收时打印堆栈线索。需要注意这个机制是“急诊手段”它有副作用比如一个真正跑长事务的连接超过 180 秒就会被强行回收导致事务中断。所以定位到泄漏代码后一定要把removeAbandoned改回 false用规范代码替代强杀方案。这个操作我踩过坑当年为了省事直接开了超时强杀结果把一条正常的报表导出事务给杀了生产工单直接飞过来。6.4 Druid 版本和 Spring Boot 版本之间的小摩擦如果你用的是比较老的 druid starter比如 1.1.x和 Spring Boot 2.5 以上的版本搭配时偶尔会出现自动配置读取不到spring.datasource.druid.*的情况。这种情况不要试图一行行去查源码先把依赖升到 1.2.x 跑一遍回归通常能解决。另外JDK 11 下使用 MySQL 驱动控制台会打印大量关于 SSL、时区的小提示本身不影响功能但会干扰日志排障。按照提示在 URL 上显式加useSSLfalse和serverTimezoneAsia/Shanghai日志就干净了。7. 后续可以继续延伸的玩法Druid 创建 Datasource 只是连接池之旅的起点。后面可以做的扩展我简单列一下多数据源场景结合动态数据源方案可以配置读写分离让DS注解在 Service 层动态切换数据源。基于 AOP 做连接池使用量监控自己定义切面拦截 DAO 层调用把单次请求连接占用时长记录到日志。慢 SQL 治理专项结合 Druid 的慢 SQL 列表逐条分析执行计划从索引、SQL 改写、缓存三个方向优化。数据库参数联动把 Druid 的max-active和数据库max_connections联动评估避免应用扩容时把数据库连接打满。我现在维护的几个项目里Druid 监控页基本是上线后必看的地方。尤其是发布新版本后先看慢 SQL 列表有没有新增项再看活跃连接曲线有没有异常爬升。有一次发布后连接数曲线陡增顺着 Druid 的 SQL 监控找到了一个 Feign 调用里忘记释放连接的历史问题直接避免了凌晨一次线上事故。最后分享一个我自己的小习惯本地开发时我会把max-active故意调小一点比如 5。这样一旦代码里有连接泄漏开发阶段就能很快感受到报错而不是上线之后才被生产环境教育。调大配置数字是很简单的事难的是让问题在最早的时间点暴露出来。这个思路比任何监控参数都实用。