数据库连接池天花板之争:HikariCP 与 Druid 底层原理 + 高并发调优全拆解

数据库连接池天花板之争:HikariCP 与 Druid 底层原理 + 高并发调优全拆解 一、数据库连接池的核心本质与通用设计模型1.1 为什么必须用数据库连接池数据库连接的创建与销毁是典型的重量级操作一次完整的JDBC连接创建需要经历TCP三次握手、MySQL服务端身份认证、会话上下文初始化、权限校验等多个环节单次耗时通常在几十到几百毫秒。而在高并发场景下每秒数千次的连接创建销毁会直接耗尽系统CPU与网络资源导致数据库响应延迟飙升甚至服务不可用。连接池的核心价值是通过池化思想实现连接的复用将连接的生命周期交由池容器统一管理应用启动时预先创建一定数量的连接业务请求直接从池中获取空闲连接使用完毕后归还给池而非直接销毁从根本上消除频繁创建销毁连接的开销同时通过连接数上限控制数据库的并发访问压力。1.2 连接池的核心设计组件所有成熟的数据库连接池都围绕以下核心组件构建底层能力连接池核心容器存储所有数据库连接的内存结构是并发控制的核心载体直接决定连接获取与释放的性能连接生命周期管理负责连接的创建、校验、回收、销毁保证连接的有效性与生命周期可控空闲连接回收机制定时清理长时间空闲的无效连接平衡资源占用与请求响应速度连接泄露检测识别并回收长时间被占用未归还的连接避免连接池耗尽并发控制机制保证多线程环境下连接获取与释放的线程安全同时最大化降低锁竞争带来的性能损耗监控与扩展体系采集连接池运行状态、SQL执行指标提供可扩展的功能插件体系1.3 连接池的核心性能瓶颈连接池的性能天花板本质上由两个核心因素决定连接获取与释放的锁竞争强度传统连接池大多使用阻塞队列存储空闲连接多线程并发获取连接时会产生激烈的锁竞争导致线程阻塞与上下文切换这是最核心的性能损耗点连接有效性校验的开销无效连接的处理逻辑直接决定业务请求的成功率与性能不合理的校验机制会带来额外的数据库交互开销二、HikariCP底层原理极致性能的核心密码2.1 HikariCP的核心定位与优势HikariCP是一款专注于极致性能与稳定性的轻量级数据库连接池自SpringBoot 2.0起成为官方默认连接池凭借极简的设计与极致的优化在各类性能基准测试中常年稳居榜首。它的设计理念是「专注于连接池的核心职责剔除所有非必要的功能损耗」核心优势集中在极致的性能、极低的资源占用与极高的稳定性。2.2 核心数据结构ConcurrentBag无锁设计HikariCP性能远超同类产品的核心是自研的ConcurrentBag无锁并发容器彻底替代了传统连接池使用的BlockingQueue从根本上解决了多线程场景下的锁竞争问题。ConcurrentBag的核心设计围绕「线程局部优先、CAS无锁修改、弱一致性」三大原则构建核心结构包括ThreadLocal threadList每个线程私有的本地缓存存储当前线程使用过的连接线程获取连接时优先从本地缓存中读取完全无锁CopyOnWriteArrayList sharedList存储连接池内所有的连接对象全局共享保证数据的最终一致性同步队列用于存储等待连接的线程当池内无可用连接时线程进入队列阻塞等待连接状态机每个PoolEntry连接对象维护一个volatile修饰的state状态字段通过CAS操作实现状态的无锁修改状态包括STATE_NOT_IN_USE空闲可用STATE_IN_USE已被占用STATE_RESERVED已被预留STATE_REMOVED已被移除ConcurrentBag的核心优化逻辑绝大多数场景下线程会从自己的ThreadLocal中获取到连接全程无锁、无阻塞性能达到极致当本地缓存无可用连接时才会遍历全局共享列表通过CAS操作抢占空闲连接仅在CAS失败时才会重试避免了锁竞争采用弱一致性设计允许短暂的连接状态不一致换取更高的并发性能最终通过状态机保证数据正确性2.3 极致性能的其他核心优化FastList替代ArrayListHikariCP内部使用自研的FastList替代JDK的ArrayList存储连接的Statement对象核心优化点去掉了ArrayList的get方法范围校验因为连接池内部完全可控不会出现数组越界重写了remove方法从数组尾部向前遍历查找元素因为Statement的关闭顺序与创建顺序相反尾部查找命中率更高时间复杂度从O(n)降低到接近O(1)字节码动态代理优化HikariCP使用Javassist动态生成Connection、Statement等JDBC接口的代理类替代JDK原生的动态代理。相比JDK动态代理的反射调用动态生成的字节码代理类直接实现了接口方法消除了反射带来的性能损耗同时精简了代理逻辑只保留连接池必要的拦截逻辑。精简的代码体量HikariCP的核心代码量极小剔除了所有非必要的功能与冗余逻辑不仅降低了CPU的指令缓存失效概率也极大减少了潜在的BUG风险保证了极高的稳定性。2.4 连接生命周期管理机制HikariCP对连接的生命周期做了精细化的管理核心逻辑连接创建后会被加入sharedList同时记录创建时间用于计算最大生命周期连接被获取时通过CAS修改状态为IN_USE记录获取时间用于连接泄露检测连接释放时通过CAS修改状态为NOT_IN_USE归还到池中等待下次复用后台定时线程会定期扫描所有连接销毁超过最大生命周期的连接同时补充空闲连接到最小空闲数连接有效性校验默认使用JDBC4规范的Connection.isValid()方法该方法由数据库驱动原生实现通过TCP ping包校验连接有效性无需执行SQL语句性能远高于传统的SELECT 1校验方式三、Druid底层原理企业级全功能连接池的架构设计3.1 Druid的核心定位与优势Druid是阿里开源的企业级数据库连接池是国内使用最广泛的连接池产品核心设计理念是「一站式企业级数据库访问解决方案」在保证连接池核心能力的同时内置了完善的监控体系、SQL防火墙、扩展插件等企业级功能深度适配国内的业务场景与运维需求。3.2 核心架构与连接池底层实现Druid的核心架构围绕可扩展的责任链模式构建底层连接池通过锁与条件队列实现并发控制核心结构如下Druid连接池的底层核心实现DruidConnectionHolder数组存储连接池内所有的连接对象包括活跃连接与空闲连接是连接池的核心存储结构ReentrantLock可重入锁全局唯一的锁对象保证连接获取、释放、创建、销毁等操作的线程安全支持公平锁与非公平锁两种模式Condition条件队列包含notEmpty与notFull两个条件对象实现线程的等待与唤醒机制当池内无空闲连接时获取连接的线程进入notEmpty队列阻塞等待当池内连接数达到上限时创建连接的线程进入notFull队列阻塞等待活跃连接集合维护当前被业务线程占用的连接对象用于连接泄露检测与监控统计后台销毁线程定时运行清理超过空闲时间的连接、超过最大生命周期的无效连接回收泄露的连接3.3 灵魂设计Filter-Chain扩展机制Filter-Chain责任链模式是Druid最核心的设计也是它能够实现丰富功能的核心支撑。Druid将数据库连接的所有操作连接创建、连接关闭、SQL执行、事务提交/回滚等都抽象为责任链上的节点所有的附加功能都通过Filter插件实现无需修改核心代码具备极强的扩展性。Filter-Chain的核心执行逻辑业务线程的每一次JDBC操作都会先经过Filter链的前置处理依次执行链上所有Filter的拦截逻辑最终调用原生JDBC的方法执行操作操作执行完成后反向执行Filter链的后置处理完成指标统计、日志记录、结果校验等操作这种设计的优势在于功能模块完全解耦用户可以根据业务需求自由组合Filter比如只开启监控功能就只加载StatFilter需要SQL防火墙就加载WallFilter需要自定义日志逻辑就实现自定义Filter加入链中灵活性极高。3.4 企业级核心特性详解全维度监控体系Druid内置了业界最完善的监控能力覆盖连接池运行状态、SQL执行全链路、事务执行、Web请求等多个维度连接池监控实时展示当前活跃连接数、空闲连接数、等待连接的线程数、连接创建销毁次数等核心指标SQL执行监控统计所有SQL的执行次数、执行时间、最大耗时、最小耗时、出错次数支持按耗时排序定位慢SQL慢SQL记录可配置慢SQL阈值自动记录执行超时的SQL语句、执行参数、堆栈信息方便定位性能问题事务监控统计事务的提交、回滚次数识别长事务与异常事务内置Web监控页面无需额外开发通过简单配置即可访问可视化监控界面查看所有监控指标SQL防火墙WallFilterWallFilter是Druid内置的SQL安全防护组件基于SQL语法解析实现核心能力包括SQL注入防护识别并拦截恶意的SQL注入语句比如永真条件、注释绕过、多语句注入等攻击方式语法校验拦截不符合SQL规范的语句避免执行报错权限控制禁止执行DROP、ALTER、TRUNCATE等高危DDL语句禁止无WHERE条件的UPDATE、DELETE语句性能防护禁止执行全表扫描的大查询避免数据库性能被拖垮连接泄露检测与回收Druid提供了完善的连接泄露检测能力可配置开启泄露检测当连接被业务线程获取后超过指定时间未归还会被强制回收同时打印连接的堆栈信息帮助开发人员快速定位泄露的代码位置。数据库兼容性Druid完美适配MySQL、Oracle、SQL Server、PostgreSQL等所有主流数据库针对不同数据库的特性做了专门的优化与适配无需额外修改配置即可无缝切换。四、HikariCP与Druid核心维度对比对比维度HikariCPDruid核心定位极致性能的轻量级连接池一站式企业级数据库访问解决方案性能表现极高无锁设计带来的性能优势明显良好因Filter链与监控逻辑有轻微性能损耗功能丰富度极简仅保留连接池核心功能极丰富内置监控、SQL防火墙、扩展插件等企业级功能资源占用极低代码量小内存占用低中等功能丰富带来一定的内存与CPU占用监控能力基础仅提供核心JMX指标需额外集成监控系统完善内置全维度监控与可视化Web界面开箱即用扩展能力弱几乎无扩展点极强基于Filter-Chain可实现任意自定义扩展适用场景追求极致性能的微服务、高并发接口、云原生场景企业级业务系统、需要全链路监控与安全防护的场景、传统IT系统社区支持全球范围活跃SpringBoot官方默认持续迭代国内社区活跃阿里内部大规模使用稳定版持续维护五、高并发场景核心调优策略5.1 连接池调优的第一性原理连接池调优最核心的误区是认为「最大连接数设置越大性能越高」。实际上数据库的处理能力是有限的当连接数超过数据库的最优处理阈值后性能会急剧下降。数据库的最优连接数核心由两个硬件因素决定CPU核心数与磁盘IO能力。业界公认的最优连接数计算公式来自Oracle官方性能优化白皮书最优连接数 (CPU核心数 * 2) 有效磁盘数举个例子一台8核CPU、单SSD磁盘的MySQL服务器最优连接数约为 8*2 1 17。即使是16核的服务器最优连接数也不会超过40。从业务视角补充计算逻辑如果单条SQL的平均执行时间是10ms单个连接每秒可以处理100个请求那么10个连接就可以支撑1000QPS的业务流量。绝大多数业务场景下连接池的最大连接数不需要超过50只有存在大量长事务的场景才需要适当上调。5.2 通用核心参数调优指南核心连接数参数最大连接数控制连接池能创建的连接上限需根据压测结果设置通常不超过50必须小于MySQL的max_connections配置默认151预留足够的连接给管理员与其他应用最小空闲连接数控制连接池长期保持的空闲连接数高并发场景下推荐设置与最大连接数相等将连接池变为固定大小避免高并发下动态创建连接带来的响应延迟初始化连接数应用启动时预先创建的连接数推荐设置与最小空闲连接数相等避免首次请求时需要创建连接带来的延迟连接生命周期参数连接最大生命周期连接从创建到被强制销毁的最大时间必须比数据库的wait_timeoutMySQL默认8小时小30秒以上避免拿到被数据库服务端主动断开的无效连接空闲连接超时时间连接空闲超过该时间会被回收仅当最小空闲连接数小于最大连接数时生效必须小于连接最大生命周期连接获取超时时间线程从池中获取连接的最大等待时间超过该时间未获取到连接则抛出异常高并发场景下推荐设置为3000ms避免线程长时间阻塞实现快速失败保护系统连接有效性校验参数获取连接时校验每次获取连接时都执行校验性能损耗极大高并发场景下必须关闭空闲时校验连接空闲超过指定时间后在下次获取时执行校验性能损耗极小推荐开启配合后台回收线程使用校验语句JDBC4及以上驱动优先使用驱动原生的isValid()方法无需设置校验语句仅老旧驱动需要设置SELECT 1作为校验语句5.3 HikariCP专属调优最佳实践参数名核心含义默认值高并发场景最佳实践maximum-pool-size连接池最大连接数10根据压测结果设置通常10-50不超过数据库最优连接数minimum-idle最小空闲连接数10设置与maximum-pool-size相等固定连接池大小max-lifetime连接最大生命周期1800000ms30分钟设置为1500000ms25分钟小于MySQL的wait_timeoutidle-timeout空闲连接超时时间600000ms10分钟当minimum-idle与max相等时该参数不生效无需调整connection-timeout连接获取超时时间30000ms30秒设置为3000ms高并发下快速失败leak-detection-threshold连接泄露检测阈值0关闭设置为60000ms60秒大于业务最长事务执行时间开启泄露检测validation-timeout连接校验超时时间5000ms设置为1000ms避免校验超时阻塞业务5.4 Druid专属调优最佳实践参数名核心含义默认值高并发场景最佳实践max-active最大活跃连接数8根据压测结果设置通常10-50不超过数据库最优连接数min-idle最小空闲连接数0设置与max-active相等固定连接池大小initial-size初始化连接数0设置与min-idle相等启动时预创建连接max-wait连接获取最大等待时间-1无限等待设置为3000ms高并发下快速失败time-between-eviction-runs-millis后台回收线程运行间隔60000ms60秒设置为30000ms30秒更频繁的清理无效连接min-evictable-idle-time-millis连接最小空闲时间1800000ms30分钟设置为1500000ms25分钟小于MySQL的wait_timeouttest-on-borrow获取连接时校验false必须保持关闭避免性能损耗test-while-idle空闲时校验true保持开启配合回收线程实现低开销校验remove-abandoned开启连接泄露回收false高并发场景下开启防止连接泄露耗尽池资源remove-abandoned-timeout泄露连接回收超时时间300秒设置为60秒大于业务最长事务执行时间log-abandoned泄露连接日志打印false开启打印泄露连接的堆栈信息方便定位问题filters加载的Filter链无基础场景配置stat,wall,slf4j高并发下无需加载非必要Filter5.5 高并发调优落地步骤基准压测通过压测工具获取业务的核心指标包括平均QPS、单请求SQL执行时间、最长事务执行时间、数据库服务器的CPU与IO负载初始参数设置根据最优连接数公式设置初始的最大连接数将最小空闲连接数与最大连接数设置为相等配置合理的连接生命周期与超时参数梯度压测调整逐步提升压测流量观察连接池的等待线程数、活跃连接数、数据库的CPU负载与响应时间微调最大连接数找到性能最优的阈值开启防护配置开启连接泄露检测、慢SQL记录配置合理的超时时间避免异常场景下连接池耗尽持续监控优化通过监控系统采集连接池的核心指标关注等待连接的线程数、连接泄露告警、慢SQL记录持续优化业务代码与连接池配置六、代码示例6.1 项目环境与依赖配置?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.4/version relativePath/ /parent groupIdcom.jam/groupId artifactIddemo-datasource-pool/artifactId version0.0.1-SNAPSHOT/version namedemo-datasource-pool/name properties java.version17/java.version mybatis-plus.version3.5.6/mybatis-plus.version druid.version1.2.23/druid.version fastjson2.version2.0.49/fastjson2.version guava.version32.1.3-jre/guava.version springdoc.version2.5.0/springdoc.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-spring-boot3-starter/artifactId version${mybatis-plus.version}/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-3-starter/artifactId version${druid.version}/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId version1.18.30/version scopeprovided/scope /dependency dependency groupIdcom.alibaba.fastjson2/groupId artifactIdfastjson2-extension-spring6/artifactId version${fastjson2.version}/version /dependency dependency groupIdcom.google.guava/groupId artifactIdguava/artifactId version${guava.version}/version /dependency dependency groupIdorg.springdoc/groupId artifactIdspringdoc-openapi-starter-webmvc-ui/artifactId version${springdoc.version}/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project6.2 数据库表结构CREATE TABLE t_user ( id bigint NOT NULL AUTO_INCREMENT COMMENT 主键ID, username varchar(64) NOT NULL COMMENT 用户名, age int DEFAULT NULL COMMENT 年龄, email varchar(128) DEFAULT NULL COMMENT 邮箱, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 更新时间, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COLLATEutf8mb4_0900_ai_ci COMMENT用户表;6.3 HikariCP整合SpringBoot3MyBatisPlus实战application.yml配置spring: application: name: demo-datasource-pool datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/demo_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: root hikari: maximum-pool-size: 20 minimum-idle: 20 max-lifetime: 1500000 connection-timeout: 3000 leak-detection-threshold: 60000 validation-timeout: 1000 mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.jam.demo.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl springdoc: swagger-ui: path: /swagger-ui.html tags-sorter: alpha api-docs: path: /v3/api-docs packages-to-scan: com.jam.demo.controller6.4 Druid整合SpringBoot3MyBatisPlus实战application.yml配置spring: application: name: demo-datasource-pool datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/demo_db?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: root druid: max-active: 20 min-idle: 20 initial-size: 20 max-wait: 3000 time-between-eviction-runs-millis: 30000 min-evictable-idle-time-millis: 1500000 test-on-borrow: false test-while-idle: true remove-abandoned: true remove-abandoned-timeout: 60 log-abandoned: true filters: stat,wall,slf4j stat-view-servlet: enabled: true url-pattern: /druid/* login-username: admin login-password: admin123 allow: 127.0.0.1 web-stat-filter: enabled: true url-pattern: /* exclusions: *.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/* mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml type-aliases-package: com.jam.demo.entity configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl springdoc: swagger-ui: path: /swagger-ui.html tags-sorter: alpha api-docs: path: /v3/api-docs packages-to-scan: com.jam.demo.controller6.5 核心代码实现实体类package com.jam.demo.entity; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; /** * 用户实体类 * author ken */ Data TableName(t_user) Schema(description 用户实体) public class User { TableId(type IdType.AUTO) Schema(description 主键ID, example 1) private Long id; Schema(description 用户名, example jam) private String username; Schema(description 年龄, example 25) private Integer age; Schema(description 邮箱, example jamexample.com) private String email; Schema(description 创建时间) private LocalDateTime createTime; Schema(description 更新时间) private LocalDateTime updateTime; }Mapper接口package com.jam.demo.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.jam.demo.entity.User; import org.apache.ibatis.annotations.Mapper; /** * 用户Mapper接口 * author ken */ Mapper public interface UserMapper extends BaseMapperUser { }Service接口package com.jam.demo.service; import com.jam.demo.entity.User; import java.util.List; /** * 用户服务接口 * author ken */ public interface UserService { Long addUser(User user); User getUserById(Long id); ListUser getUserList(); Boolean updateUser(User user); Boolean deleteUserById(Long id); }Service实现类package com.jam.demo.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.google.common.collect.Lists; import com.jam.demo.entity.User; import com.jam.demo.mapper.UserMapper; import com.jam.demo.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; import java.util.List; /** * 用户服务实现类 * author ken */ Slf4j Service public class UserServiceImpl implements UserService { private final UserMapper userMapper; private final TransactionTemplate transactionTemplate; public UserServiceImpl(UserMapper userMapper, TransactionTemplate transactionTemplate) { this.userMapper userMapper; this.transactionTemplate transactionTemplate; } /** * 新增用户 * param user 用户实体 * return 新增成功的用户ID */ Override public Long addUser(User user) { if (ObjectUtils.isEmpty(user)) { throw new IllegalArgumentException(用户对象不能为空); } if (!StringUtils.hasText(user.getUsername())) { throw new IllegalArgumentException(用户名不能为空); } return transactionTemplate.execute(status - { try { int result userMapper.insert(user); if (result 0) { status.setRollbackOnly(); throw new RuntimeException(新增用户失败); } log.info(新增用户成功用户ID{}, user.getId()); return user.getId(); } catch (Exception e) { status.setRollbackOnly(); log.error(新增用户异常, e); throw e; } }); } /** * 根据ID查询用户 * param id 用户ID * return 用户实体 */ Override public User getUserById(Long id) { if (ObjectUtils.isEmpty(id)) { throw new IllegalArgumentException(用户ID不能为空); } return userMapper.selectById(id); } /** * 查询用户列表 * return 用户列表 */ Override public ListUser getUserList() { ListUser userList userMapper.selectList(new LambdaQueryWrapperUser() .orderByDesc(User::getCreateTime)); if (CollectionUtils.isEmpty(userList)) { return Lists.newArrayList(); } return userList; } /** * 更新用户信息 * param user 用户实体 * return 更新结果 */ Override public Boolean updateUser(User user) { if (ObjectUtils.isEmpty(user) || ObjectUtils.isEmpty(user.getId())) { throw new IllegalArgumentException(用户信息或ID不能为空); } return transactionTemplate.execute(status - { try { int result userMapper.updateById(user); if (result 0) { status.setRollbackOnly(); log.warn(更新用户失败用户ID{}, user.getId()); return Boolean.FALSE; } log.info(更新用户成功用户ID{}, user.getId()); return Boolean.TRUE; } catch (Exception e) { status.setRollbackOnly(); log.error(更新用户异常, e); throw e; } }); } /** * 根据ID删除用户 * param id 用户ID * return 删除结果 */ Override public Boolean deleteUserById(Long id) { if (ObjectUtils.isEmpty(id)) { throw new IllegalArgumentException(用户ID不能为空); } return transactionTemplate.execute(status - { try { int result userMapper.deleteById(id); if (result 0) { status.setRollbackOnly(); log.warn(删除用户失败用户ID{}, id); return Boolean.FALSE; } log.info(删除用户成功用户ID{}, id); return Boolean.TRUE; } catch (Exception e) { status.setRollbackOnly(); log.error(删除用户异常, e); throw e; } }); } }Controller层package com.jam.demo.controller; import com.jam.demo.entity.User; import com.jam.demo.service.UserService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 用户控制器 * author ken */ RestController RequestMapping(/user) Tag(name 用户管理, description 用户信息增删改查接口) public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService userService; } PostMapping(/add) Operation(summary 新增用户, description 创建新的用户信息) public Long addUser(RequestBody User user) { return userService.addUser(user); } GetMapping(/{id}) Operation(summary 查询用户, description 根据用户ID查询用户详情) public User getUserById(PathVariable Long id) { return userService.getUserById(id); } GetMapping(/list) Operation(summary 用户列表, description 查询所有用户信息列表) public ListUser getUserList() { return userService.getUserList(); } PutMapping(/update) Operation(summary 更新用户, description 更新用户的信息) public Boolean updateUser(RequestBody User user) { return userService.updateUser(user); } DeleteMapping(/{id}) Operation(summary 删除用户, description 根据用户ID删除用户信息) public Boolean deleteUserById(PathVariable Long id) { return userService.deleteUserById(id); } }启动类package com.jam.demo; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * 应用启动类 * author ken */ SpringBootApplication MapperScan(com.jam.demo.mapper) public class DemoDatasourcePoolApplication { public static void main(String[] args) { SpringApplication.run(DemoDatasourcePoolApplication.class, args); } }6.6 自定义Druid Filter扩展示例package com.jam.demo.filter; import com.alibaba.druid.filter.FilterEventAdapter; import com.alibaba.druid.proxy.jdbc.ConnectionProxy; import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy; import lombok.extern.slf4j.Slf4j; import java.sql.SQLException; /** * 自定义SQL执行日志Filter * author ken */ Slf4j public class SqlExecuteLogFilter extends FilterEventAdapter { Override protected void preparedStatement_executeAfter(PreparedStatementProxy statement, boolean firstResult, SQLException error) { super.preparedStatement_executeAfter(statement, firstResult, error); String sql statement.getSql(); long executeTime statement.getLastExecuteTimeNano() / 1000000; if (error ! null) { log.error(SQL执行异常SQL{}异常信息{}, sql, error.getMessage()); return; } log.info(SQL执行完成执行耗时{}msSQL{}, executeTime, sql); } Override public void connection_closeAfter(ConnectionProxy connection, SQLException error) { super.connection_closeAfter(connection, error); log.debug(连接归还到连接池连接ID{}, connection.getId()); } }七、高并发场景常见踩坑与避坑指南连接数设置过大导致数据库性能崩溃这是最常见的踩坑场景很多开发人员为了应对高并发将最大连接数设置为几百甚至上千导致数据库线程数过多CPU大部分时间消耗在线程上下文切换上SQL执行效率急剧下降最终数据库响应超时服务雪崩。 避坑方案严格按照最优连接数公式设置初始值通过压测微调绝大多数场景下最大连接数不超过50同时保证应用连接池的最大连接数总和小于MySQL的max_connections配置。连接最大生命周期超过数据库超时时间数据库会对长时间空闲的连接主动断开比如MySQL的wait_timeout默认是8小时如果连接池的连接最大生命周期设置超过这个值会导致应用拿到已经被数据库断开的无效连接执行SQL时抛出No operations allowed after connection closed异常。 避坑方案将连接池的最大生命周期设置为比数据库的wait_timeout小30秒以上MySQL场景下推荐设置为25分钟。长事务导致连接池耗尽长事务会长期占用数据库连接期间连接无法被其他请求复用当并发量上来后连接池内的连接会被快速耗尽所有新的请求都会阻塞等待连接最终导致服务响应超时。 避坑方案严格控制事务的执行时间避免在事务中执行远程调用、文件IO等耗时操作将大事务拆分为多个小事务同时开启连接泄露检测识别长事务与未释放的连接。开启testOnBorrow导致性能急剧下降testOnBorrow参数会在每次获取连接时都执行一次校验SQL相当于每次业务请求都多了一次数据库交互在高并发场景下会带来极大的性能损耗响应时间成倍增加。 避坑方案高并发场景下必须关闭testOnBorrow使用testWhileIdle配合后台回收线程做连接有效性校验JDBC4及以上驱动使用原生的isValid()方法做校验无需设置校验SQL。连接泄露导致连接池被打满连接泄露是指应用从连接池获取连接后使用完毕没有归还给池比如事务没有提交/回滚、没有在finally中关闭连接、异常场景下未释放连接等最终导致连接池内的空闲连接耗尽服务不可用。 避坑方案开启连接池的连接泄露检测功能设置合理的超时时间打印泄露连接的堆栈信息快速定位问题代码使用try-with-resources语法管理连接确保连接一定会被释放使用编程式事务保证异常场景下事务一定会回滚连接被释放。Druid的WallFilter配置不当导致业务异常WallFilter的SQL防火墙功能如果配置过严会拦截正常的业务SQL比如禁止了多表联查、子查询等业务需要的语法导致业务执行报错如果配置过松又起不到安全防护的作用。 避坑方案根据业务场景配置WallFilter的规则先开启日志模式不直接拦截观察所有业务SQL的执行情况调整规则后再开启拦截模式避免影响正常业务。最小空闲连接数设置过小导致高并发下响应延迟如果最小空闲连接数设置远小于最大连接数当流量突增时连接池需要动态创建新的连接而连接的创建是重量级操作会导致请求响应延迟增加甚至超时。 避坑方案高并发场景下将最小空闲连接数与最大连接数设置为相等将连接池变为固定大小应用启动时就预创建所有连接避免高并发下动态创建连接的开销。八、总结HikariCP与Druid两款连接池分别代表了连接池产品的两个极致方向HikariCP专注于极致的性能与稳定性凭借无锁设计与深度优化成为了SpringBoot官方默认的连接池完美适配追求极致性能的微服务与云原生场景Druid则聚焦于企业级一站式解决方案凭借完善的监控体系、SQL防火墙与灵活的扩展能力成为了国内企业级业务场景的首选。连接池调优的核心从来不是盲目调大参数而是回归本质理解连接池的底层原理结合业务的实际场景与压测数据设置合理的参数同时优化业务代码避免长事务、慢SQL、连接泄露等问题才能充分发挥连接池的性能保证系统在高并发场景下的稳定运行。