一句话总结
连接池通过复用数据库连接避免频繁创建/销毁连接的开销。HikariCP 是 Spring Boot 2.x 默认连接池,之所以快是因为:1)字节码精简(类少、方法短);2)ConcurrentBag 无锁并发集合;3)FastList 代替 ArrayList。核心参数:maximumPoolSize(最大连接数)、minimumIdle(最小空闲)、connectionTimeout(等待超时)、idleTimeout(空闲超时)、maxLifetime(最大存活时间)。
初级理解
为什么需要连接池?
数据库连接是重量级资源:TCP 三次握手 + MySQL 认证 + 线程创建,每次创建约 10~50ms。如果每次请求都新建连接,高并发下性能极差。连接池预先创建一批连接,请求时借出,用完归还,避免频繁创建销毁。
HikariCP 核心参数
# Spring Boot 配置(application.yml)
spring:
datasource:
hikari:
# 最大连接数(默认 10)
maximum-pool-size: 20
# 最小空闲连接数(默认等于 maximumPoolSize)
minimum-idle: 10
# 等待连接超时(默认 30 秒)
connection-timeout: 30000
# 空闲连接超时(默认 10 分钟)
idle-timeout: 600000
# 连接最大存活时间(默认 30 分钟)
max-lifetime: 1800000
# 连接测试查询
connection-test-query: SELECT 1
连接池大小公式
# 经验公式(Oracle 官方推荐)
# 连接数 = ((核心数 * 2) + 有效磁盘数)
# 例如:4 核 CPU + 1 块 SSD
# 连接数 = (4 * 2) + 1 = 9
# 注意:
# 1. 连接数不是越大越好,太多连接会导致上下文切换开销
# 2. 同时要考虑 MySQL max_connections 限制
# 3. 多个服务实例共享同一个 MySQL,要合理分配
一句话总结:连接池 = 预创建 + 复用 + 管理,避免频繁创建连接的开销。
中级深入
HikariCP 为什么快?
| 优化点 | 说明 |
| 字节码精简 | 类少、方法短,JIT 编译更高效,生成的机器码更优 |
| ConcurrentBag | 无锁并发集合,通过 ThreadLocal + CopyOnWriteArrayList 实现高效并发 |
| FastList | 代替 ArrayList,去掉 rangeCheck,remove 时不从后往前拷贝 |
| 优化代理 | 生成的 ProxyConnection 字节码更少 |
连接池工作流程
# 获取连接流程
# 1. 从 ConcurrentBag 的 ThreadLocal 列表中取(最快)
# 2. 没有则从共享列表中取
# 3. 还没有则创建新连接(不超过 maximumPoolSize)
# 4. 达到上限则等待(不超过 connectionTimeout)
# 5. 超时抛出 SQLException
# 归还连接流程
# 1. 检查连接是否可用(SELECT 1)
# 2. 放回 ConcurrentBag
# 3. 如果空闲连接超过 minimumIdle,关闭多余连接
连接泄漏排查
# HikariCP 连接泄漏检测
spring:
datasource:
hikari:
leak-detection-threshold: 10000 # 连接持有超过 10 秒打印警告
# 日志输出示例:
# [HikariPool-1] Connection leak detection triggered
# for conn1, stack trace follows:
# java.lang.Exception: Apparent connection leak detected
# at com.example.UserService.getUser(UserService.java:25)
# ...
# 常见泄漏原因:
# 1. 获取连接后没有 close()(没有用 try-with-resources)
# 2. 事务时间过长
# 3. 循环中获取连接
中级要点:HikariCP 快在字节码精简 + ConcurrentBag 无锁 + FastList;连接泄漏用 leak-detection-threshold 排查。
高级拓展
Druid vs HikariCP
| 对比维度 | HikariCP | Druid |
| 性能 | 极快(字节码级优化) | 较快 |
| 监控 | 基础(需 Prometheus + Actuator) | 内置强大监控页面 |
| SQL 防火墙 | 不支持 | 支持(防 SQL 注入) |
| 适用场景 | 追求极致性能 | 需要监控和诊断 |
# Druid 配置(application.yml)
spring:
datasource:
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
# 监控配置
stat-view-servlet:
enabled: true
url-pattern: /druid/*
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 1000
wall:
enabled: true # SQL 防火墙
连接池常见问题
1. 连接超时:连接池满了,新请求等待超时 → 增大 maximumPoolSize 或优化慢查询
2. 连接被 MySQL 断开:MySQL wait_timeout 默认 8 小时 → maxLifetime 应小于 wait_timeout
3. 连接泄漏:获取连接后未归还 → 开启 leak-detection-threshold
实战场景
场景:线上连接池耗尽排查
# 1. 查看连接池状态(Actuator + HikariCP)
# GET /actuator/health
{
"hikari": {
"active": 20, # 活跃连接(全在用!)
"max": 20, # 最大连接
"pending": 15, # 等待获取连接的请求
"timeout": 30000
}
}
# 2. 查看 MySQL 连接
SHOW PROCESSLIST;
# 看到大量 Sleep 状态的连接 → 连接泄漏
# 3. 临时解决:增大连接池
# 4. 根本解决:找到未关闭连接的代码
# 开启 leak-detection-threshold 定位泄漏点
面试模拟
面试官:HikariCP 为什么这么快?
你:1)字节码级优化——类少方法短,JIT 编译更高效;2)ConcurrentBag——无锁并发集合,通过 ThreadLocal 避免竞争;3)FastList——去掉 ArrayList 的 rangeCheck 和不必要的数组拷贝;4)代理优化——生成的 ProxyConnection 字节码更精简。
面试官:连接池大小怎么设置?
你:公式:连接数 = (核心数 * 2) + 有效磁盘数。但不是越大越好,太多连接会导致上下文切换开销。实际中还要考虑:1)MySQL max_connections 限制;2)多个服务实例共享;3)压测验证。一般 20~50 足够大多数场景。