3. 数据库连接池优化:连接池大小设置、连接复用策略、超时配置
连接池这东西,说白了就是个复用器。每次业务请求都要新建数据库连接,那开销可太大了——TCP 三次握手、认证、会话建立,一套下来几十毫秒就没了。我早年接手过一个审计系统,每天晚上跑对账脚本时数据库连接数飙到 800+,结果应用服务器先挂了。
3.1 连接池大小设置——不是越大越好
很多人的第一反应是:连接数不够就加呗。我见过把 HikariCP 的 maximumPoolSize 设成 200 的,结果数据库 CPU 直接打满,性能反而下降。为什么?因为数据库的连接数是有限资源,每个连接都要占用内存、锁、I/O 线程。
有个经典公式可以参考:
连接数 = ((核心数 × 2) + 有效磁盘数)
比如你服务器是 8 核,磁盘组 RAID 10 算 4 块盘,那建议的连接数就是 (8×2)+4 = 20。不过这只是起算点。实际项目中,我习惯先压测,观察数据库的活跃连接数和等待队列长度。如果队列长度一直大于 0,说明池子小了;如果活跃连接数长期低于 80%,池子就可能偏大。
| 场景 | 推荐 maximumPoolSize | 常见陷阱 |
|---|---|---|
| OLTP 高并发写入 | CPU 核数 × 2 ~ 3 | 设太大导致锁争用 |
| 审计报表查询(慢查询多) | CPU 核数 × 1 ~ 2 | 连接被慢 SQL 占满 |
| 批量导出/导入 | 根据磁盘 IOPS 估算 | 连接池配置和业务线程数不匹配 |
| 微服务调用链复杂 | 建议单独测试 | 过度复用连接影响隔离性 |
3.2 连接复用策略——让一条连接多干活
连接池的核心价值就是复用。一条连接从池子里拿出来,用完还回去,下个请求接着用。但“复用”不是无脑地一直用,你得考虑几个问题:
- 空闲扫描:连接空闲超过若干秒,是否需要提前关闭?我习惯设置 idleTimeout 为 30 秒,避免连接在池子里躺尸,白白消耗数据库资源。
- 最大生命周期:连接不能永生。数据库连接如果被长期占用,可能被防火墙或数据库自身 kill。我建议 maximumLifetime 设为 30 分钟,比数据库层的超时短一点就好。
- 连接泄漏检测:程序里忘记关闭连接,连接池永远不归还。HikariCP 有 leakDetectionThreshold 参数,我设成 5000 毫秒,超过这个时间还没归还就在日志里打印堆栈,抓泄漏特别好使。
你想想看,如果一条连接跑了半小时,数据库那边的事务隔离级别、预编译语句缓存都可能出问题。尤其是审计系统,经常做长事务,一条连接里塞好几个查询,复用不当会导致数据不一致。
3.3 超时配置——不给系统留隐患
超时要是没设好,连接池就会成为“僵尸池”。我见过一个案例:业务高峰时应用报错“Connection is not available”,但数据库明明没满负荷。后来发现是 connectionTimeout 设成了默认 30 秒,所有请求都堵在等待队列里,最终超时。实际上连接早就被业务线程占满了,连接池还傻等着。
常用的超时参数:
| 参数名 | 说明 | 推荐值 |
|---|---|---|
| connectionTimeout | 从池中获取连接的超时时间 | 1000~3000 ms(取决于接口响应要求) |
| idleTimeout | 连接空闲多长时间后关闭 | 30000~60000 ms |
| maximumLifetime | 连接的最大存活时间 | 1800000 ms(30分钟) |
| validationTimeout | 连接健康检查的超时 | 500 ms |
另外,连接池的超时配置要和数据库侧的超时配合。比如 MySQL 的 wait_timeout 是 8 小时,你池子里 maximumLifetime 设 4 小时就够了。否则数据库把连接关掉了,连接池还以为是可用的,下个请求拿过来就是一串异常。