news 2026/4/17 11:59:39

数据库连接池原理与HikariCP调优实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
数据库连接池原理与HikariCP调优实战

前言

刚工作那会儿,遇到过一个诡异的问题:服务刚启动时第一批请求特别慢,好几秒才响应,之后就正常了。

查了半天发现是数据库连接的锅——每次请求都新建连接,TCP握手 + MySQL认证,一套下来几百毫秒。用上连接池后,响应时间从秒级降到毫秒级。

连接池这东西,平时不出问题感觉不到它的存在,一出问题就是大麻烦。这篇文章讲清楚原理和调优,让你以后遇到问题能快速定位。

为什么需要连接池

创建连接的代价

数据库连接不是直接就能用的,要经过:

客户端 数据库 | | |------- SYN ---------> | |<------ SYN-ACK ------ | TCP三次握手 |------- ACK ---------> | | | |---- 认证请求 --------> | |<--- 认证Challenge ---- | MySQL认证 |---- 认证响应 --------> | |<--- 认证OK ---------- | | |

一次连接建立,最少也要几十毫秒(局域网),跨机房可能几百毫秒。

如果每次查询都新建连接:

// 不用连接池(千万别这样写)publicUsergetUser(intid){Connectionconn=DriverManager.getConnection(url,user,password);// 每次新建try{PreparedStatementps=conn.prepareStatement("SELECT * FROM user WHERE id = ?");ps.setInt(1,id);ResultSetrs=ps.executeQuery();// ...}finally{conn.close();// 用完就关}}

假设100 QPS,每个连接建立耗时50ms,光建连接就得5秒。

连接池的作用

连接池预先创建好一批连接,要用的时候借出去,用完还回来:

+--------------------+ | 连接池 | | +----+ +----+ | | |conn| |conn| ... | <- 空闲连接 | +----+ +----+ | +--------------------+ ^ | | v 还回 借出 ^ | | v +--------------------+ | 应用代码 | +--------------------+

好处:

  1. 避免重复建连接:几十毫秒 → 微秒级
  2. 控制连接数量:防止撑爆数据库
  3. 连接复用:一个连接可以被多个请求复用

连接池核心原理

核心数据结构

一个连接池至少要有这些东西:

classSimpleConnectionPool{// 空闲连接队列privateQueue<Connection>idleConnections=newLinkedList<>();// 正在使用的连接privateSet<Connection>activeConnections=newHashSet<>();// 最大连接数privateintmaxPoolSize=10;// 等待获取连接的线程privateObjectlock=newObject();}

获取连接的流程

publicConnectiongetConnection(longtimeout){synchronized(lock){longdeadline=System.currentTimeMillis()+timeout;while(true){// 1. 有空闲连接,直接返回if(!idleConnections.isEmpty()){Connectionconn=idleConnections.poll();activeConnections.add(conn);returnconn;}// 2. 没达到最大数,创建新连接if(activeConnections.size()<maxPoolSize){Connectionconn=createNewConnection();activeConnections.add(conn);returnconn;}// 3. 等待其他线程归还longwaitTime=deadline-System.currentTimeMillis();if(waitTime<=0){thrownewSQLException("获取连接超时");}lock.wait(waitTime);}}}

归还连接

publicvoidreturnConnection(Connectionconn){synchronized(lock){activeConnections.remove(conn);// 检查连接是否还有效if(isValid(conn)){idleConnections.offer(conn);}else{conn.close();// 坏了就丢掉}lock.notifyAll();// 唤醒等待的线程}}

连接有效性检测

连接可能因为各种原因失效:

  • 数据库重启
  • 网络中断
  • 连接超时被踢

所以借出前要检测:

privatebooleanisValid(Connectionconn){try{// 方式1:发送轻量查询Statementstmt=conn.createStatement();stmt.execute("SELECT 1");returntrue;}catch(SQLExceptione){returnfalse;}}

HikariCP:最快的连接池

HikariCP是目前最快的Java连接池,SpringBoot 2.x默认用它。

为什么HikariCP快

  1. 字节码优化:用Javassist动态生成代理类,比反射快
  2. 无锁设计:用CAS代替synchronized,减少线程阻塞
  3. FastList:自定义的List实现,针对连接池场景优化
  4. 精简代码:整个核心代码只有几千行

基本配置

# application.ymlspring:datasource:hikari:# 连接池大小minimum-idle:5# 最小空闲连接maximum-pool-size:20# 最大连接数# 超时设置connection-timeout:30000# 获取连接超时(毫秒)idle-timeout:600000# 空闲连接超时(毫秒)max-lifetime:1800000# 连接最大存活时间(毫秒)# 连接检测connection-test-query:SELECT 1

连接池大小怎么设

这是最常被问的问题。官方有个公式:

连接数 = (核心数 * 2) + 有效磁盘数

但实际情况要复杂得多,建议从小开始逐步调整:

# 一般Web应用 最大连接数 = CPU核数 * 2 ~ 4 # 例如8核服务器 maximum-pool-size: 20 minimum-idle: 5

为什么不是越大越好?

连接数太多: ├── 数据库连接数有限(MySQL默认151) ├── 每个连接都占内存(MySQL每连接约1MB) ├── 更多连接 = 更多上下文切换 └── 锁竞争更激烈

经验法则:宁可排队等连接,不要撑爆数据库

超时参数详解

hikari:# 获取连接最多等30秒connection-timeout:30000# 空闲连接超过10分钟就关闭# 注意:要小于数据库的wait_timeoutidle-timeout:600000# 连接最多存活30分钟,然后强制关闭重建# 防止连接时间太长出问题max-lifetime:1800000

关键点max-lifetime必须比数据库的wait_timeout小几分钟:

-- 查看MySQL的wait_timeoutSHOWVARIABLESLIKE'wait_timeout';-- 默认28800秒(8小时)

如果max-lifetime > wait_timeout,数据库会先把连接断掉,连接池不知道,就会拿到死连接。

监控指标

HikariCP暴露了很多指标,配合Prometheus很好用:

// 开启指标HikariConfigconfig=newHikariConfig();config.setMetricRegistry(newPrometheusMeterRegistry(...));

关键指标:

# 活跃连接数 hikaricp_connections_active # 空闲连接数 hikaricp_connections_idle # 等待获取连接的线程数 hikaricp_connections_pending # 获取连接耗时 hikaricp_connections_acquire_seconds

告警规则

groups:-name:hikari-alertsrules:-alert:ConnectionPoolExhaustedexpr:hikaricp_connections_pending>0for:1mannotations:summary:"连接池耗尽,有线程在等待"-alert:ConnectionAcquireSlowexpr:hikaricp_connections_acquire_seconds_max>1for:5mannotations:summary:"获取连接超过1秒"

常见问题排查

问题1:Connection is not available

HikariPool-1 - Connection is not available, request timed out after 30000ms

原因:连接池满了,30秒内没拿到连接。

排查:

-- 看数据库实际连接数SHOWSTATUSLIKE'Threads_connected';-- 看连接来源SELECT*FROMinformation_schema.processlist;

可能原因:

  1. 连接池太小
  2. 有慢查询占着连接不放
  3. 连接泄漏(借出去没还)

问题2:连接泄漏

连接借出去忘了还,池子里的连接越来越少。

HikariCP有泄漏检测:

hikari:leak-detection-threshold:60000# 连接借出超过60秒就报警

日志会显示借出连接的堆栈,定位泄漏代码:

ProxyLeakTask - Connection leak detection triggered for conn0 at com.example.UserService.getUser(UserService.java:42) at ...

问题3:连接被数据库断开

Communications link failure The last packet successfully received from the server was xxx milliseconds ago

原因:连接闲置太久,被数据库踢了。

解决:

hikari:# 定期发心跳保活keepalive-time:30000# 每30秒发一次心跳# 或者让连接在数据库踢之前主动关闭max-lifetime:1700000# 小于wait_timeout

问题4:启动时连接失败

服务启动时数据库还没好,连接失败:

hikari:# 初始化时允许失败initialization-fail-timeout:-1# 慢慢重试connection-timeout:30000

或者用优雅启动,等数据库好了再接入流量。

多数据源配置

实际项目经常要连多个库:

@ConfigurationpublicclassDataSourceConfig{@Bean@Primary@ConfigurationProperties("spring.datasource.primary.hikari")publicDataSourceprimaryDataSource(){returnDataSourceBuilder.create().type(HikariDataSource.class).build();}@Bean@ConfigurationProperties("spring.datasource.secondary.hikari")publicDataSourcesecondaryDataSource(){returnDataSourceBuilder.create().type(HikariDataSource.class).build();}}
spring:datasource:primary:hikari:jdbc-url:jdbc:mysql://master:3306/dbmaximum-pool-size:20secondary:hikari:jdbc-url:jdbc:mysql://slave:3306/dbmaximum-pool-size:10

读写分离

publicclassRoutingDataSourceextendsAbstractRoutingDataSource{@OverrideprotectedObjectdetermineCurrentLookupKey(){returnTransactionSynchronizationManager.isCurrentTransactionReadOnly()?"slave":"master";}}

生产经验

经验1:监控先行

上线前先把监控加上。连接池问题往往是间歇性的,没监控数据很难定位。

经验2:压测确定参数

不同业务对连接池需求不同。用压测工具(JMeter、wrk)在真实负载下调整参数。

经验3:分环境配置

# 开发环境spring:profiles:devdatasource:hikari:maximum-pool-size:5# 生产环境spring:profiles:proddatasource:hikari:maximum-pool-size:30

经验4:多机房注意网络

我们有跨机房数据库访问的场景,连接建立延迟高。这种情况下:

  1. 适当增大connection-timeout
  2. 用更激进的预热策略
  3. 我们用星空组网把两边网络打通后,延迟稳定很多

总结

连接池的核心就三件事:

功能配置项建议值
池大小maximum-pool-sizeCPU核数 * 2 ~ 4
超时控制connection-timeout30秒
连接存活max-lifetime< 数据库wait_timeout

记住几个原则:

  1. 连接数不是越多越好,够用就行
  2. 监控比调参重要,先能看到问题
  3. 连接泄漏是大忌,用框架自动管理
  4. 超时要协调,连接池和数据库要配合

连接池配好了是透明的,配不好就是定时炸弹。希望这篇文章能帮你理解原理,遇到问题时知道往哪个方向排查。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 15:48:55

Adobe下载工具终极方案:macOS用户效率翻倍的秘密武器

Adobe下载工具终极方案&#xff1a;macOS用户效率翻倍的秘密武器 【免费下载链接】Adobe-Downloader macOS Adobe apps download & installer 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-Downloader 还在为Adobe软件下载的种种困扰而烦恼吗&#xff1f;每次…

作者头像 李华
网站建设 2026/4/17 7:50:54

Transformer模型可视化终极指南:浏览器里运行GPT-2的完整教程

Transformer模型可视化终极指南&#xff1a;浏览器里运行GPT-2的完整教程 【免费下载链接】transformer-explainer Transformer Explained Visually: Learn How LLM Transformer Models Work with Interactive Visualization 项目地址: https://gitcode.com/gh_mirrors/tr/tr…

作者头像 李华
网站建设 2026/4/16 2:00:41

Open-AutoGLM手机集成全攻略(从零到上线的5个关键步骤)

第一章&#xff1a;Open-AutoGLM手机集成全攻略概述 Open-AutoGLM 是一款基于 AutoGLM 架构优化的开源框架&#xff0c;专为移动端设备设计&#xff0c;支持在 Android 和 iOS 平台上高效运行大语言模型。该框架通过轻量化推理引擎、动态算子融合与内存压缩技术&#xff0c;显著…

作者头像 李华
网站建设 2026/4/7 2:50:42

Proxmark3 RFID安全实战:从零到精通的完整指南

Proxmark3 RFID安全实战&#xff1a;从零到精通的完整指南 【免费下载链接】proxmark3 Proxmark 3 项目地址: https://gitcode.com/gh_mirrors/pro/proxmark3 作为RFID安全研究领域的标杆工具&#xff0c;Proxmark3 RFID安全工具以其强大的射频信号处理能力和丰富的功能…

作者头像 李华
网站建设 2026/4/15 15:04:45

Windows系统文件win32k.sys 免费下载方法

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

作者头像 李华
网站建设 2026/4/8 14:46:33

Windows系统文件win32u.dll丢失损坏问题 下载修复

在使用电脑系统时经常会出现丢失找不到某些文件的情况&#xff0c;由于很多常用软件都是采用 Microsoft Visual Studio 编写的&#xff0c;所以这类软件的运行需要依赖微软Visual C运行库&#xff0c;比如像 QQ、迅雷、Adobe 软件等等&#xff0c;如果没有安装VC运行库或者安装…

作者头像 李华