连接池很多人是照抄网上模板或干脆用默认值,没人专门调过。但连接池的参数之间相互关联,改一个会影响其他参数表现,三组参数各自为政迟早出事。这篇文章按容量、超时、生命周期三组参数讲清怎么定,再给排查清单。
连接池大小:不是越大越好
最常见的错是把 maximum-pool-size 设得很大,「设大点总没错」。其实连接池设太大不是浪费资源那么简单。每个数据库连接在 MySQL 端要占内存——thread_stack、read_buffer、sort_buffer 加起来一个连接约 2-4MB,500 个就是 1-2GB。更关键的是连接越多 CPU 锁争用越严重,MySQL 内部用互斥锁保护共享结构,连接越多锁冲突越频繁。有案例把连接池从 200 降到 30 后 QPS 反而提升 40%,就是因为锁冲突大幅减少。
HikariCP 官方推荐公式:
连接池大小 = CPU 核数 × 2 + 磁盘数
适用于 OLTP 短查询高并发场景。8 核单磁盘服务器设 17 左右就够。有大量长查询或存储过程可适当放大到 3-5 倍,但别超 100,超过 100 的连接池 99% 是配错了。
多应用实例要会做除法。一台数据库给 N 个应用实例用,每个实例连接池大小应是 (数据库 max_connections × 0.8) / 实例数,留 20% 余量给运维和意外。比如 max_connections 500、3 个实例,每实例不应超 130;但实际是轻量 OLTP,按公式算 17 够,设 25 留点余量。
获取连接超时:别用默认 30 秒
connectionTimeout 是池里没空闲连接时线程最多等多久。HikariCP 默认 30 秒,这很危险。30 秒里线程是挂起的,不释放也不报错——一次营销活动涌入几百请求,每个等 30 秒,应用服务器的线程池会被这些等待线程占满,新请求连应用层都进不来直接超时,前端重试又送来一批,恶性循环。
获取连接超时不应超过 5 秒。一般业务 1-3 秒,核心链路 1 秒以内,批处理放宽到别超 10 秒。超过 3 秒还拿不到连接说明池子真不够用,再等也等不来,不如快速失败触发熔断,让降级逻辑介入(返回缓存或走降级),比线程干等 30 秒把线程池拖死强得多。
spring.datasource.hikari:
connection-timeout: 3000 # 3秒快速失败
maxLifetime:不设会拿到僵尸连接
maxLifetime 控制连接从创建到强制关闭的时间。设成 0(不限制)听着挺好,是大坑。数据库和应用之间通常有防火墙、负载均衡、NAT 网关,这些中间设备会掐断长时间没数据传输的 TCP 连接——防火墙空闲超时通常 15-30 分钟。应用侧不知道,连接池还以为连接是好的,下次拿出来用才发现断了,报 Communications link failure。
高可用场景更常见:主从切换、VIP 漂移、连接代理重启后老连接全失效。经验值设 30 分钟:
spring.datasource.hikari:
max-lifetime: 1800000 # 30分钟
别太短(连接频繁销毁重建开销大),也别太长(超过防火墙超时就没意义)。不知道防火墙超时先设 30 分钟,看日志里连接错误频率再微调。
一份可用的 HikariCP 配置
spring.datasource.hikari:
maximum-pool-size: 25 # 按公式算留余量
minimum-idle: 5 # 低峰期有连接可用
connection-timeout: 3000 # 3秒快速失败
idle-timeout: 1800000 # 空闲30分钟回收
max-lifetime: 1800000 # 连接30分钟强制换新
connection-test-query: SELECT 1
pool-name: myAppPool # 方便日志排查
leak-detection-threshold: 60000 # 60秒未归还报警
适用于大多数中等规模 OLTP 应用。用 Druid 或 c3p0 参数名不同,调优思路通用。
连接池故障怎么排查
第一步:确认是不是连接池的问题。 看到 Too many connections 或 Connection pool exhausted 先别改配置,看两个数:
SHOW STATUS LIKE 'Threads_connected';
SHOW STATUS LIKE 'Threads_running';
Threads_connected 是当前连接总数,Threads_running 是正在执行 SQL 的连接数。前者高后者低,说明大量连接在发呆,大概率是连接泄漏或空闲回收没生效;两者都高说明慢查询堆积或突发流量。
第二步:定位泄漏代码。 开 leak-detection-threshold: 60000,连接被拿走超 60 秒没还就打印含调用栈的警告日志,直接定位到哪行没关连接。生产建议常开。一次排查半天的泄漏,开了这个参数 5 分钟就定位到——导出功能异常分支没关连接。
第三步:看监控面板。 Prometheus + Grafana 重点盯四个指标:active connections(持续上升不回落=泄漏)、idle connections(长期为 0=池不够或回收失效)、total connections(触达上限=池太小)、pending threads(大于 0 说明池不够)。比看日志直观,建议提前配好。
避坑清单
- 最大连接数设太大:超过一定数值锁争用让性能下降,按公式算别猜。
- 超时用默认值:30 秒会让线程池被等待线程占满,改 3 秒以内。
- 不设 maxLifetime:迟早拿到僵尸连接,中间设备超时比你想的短。
- 多实例不预留余量:每个实例都设满加起来超数据库上限,做除法留 20%。
- 不监控空闲连接回收:只看活跃连接不管 idle,回收失效了也不知道,等发现池已掏空——半夜系统卡死翻日志才发现 idle 已经零了快一周。
连接池配置对了感觉不到它的存在,配错了能让你半夜爬起来救火。花半小时把参数理清楚,比出故障再排查划算。