已采纳
回答正文
回复
讨论正文
该问题的正文、回答与讨论。
【背景】 一个订单服务,Spring Boot 3.2 + JDK 17 + HikariCP 5.1,K8s 4 副本(4C/8G),入口稳态 800 QPS、峰值 1500。链路:K8s Service → 内网 LB(SLB/ALB,TCP 空闲超时 350s)→ MySQL 8.0.35(wait_timeout=28800s)。HikariCP 默认 maximumPoolSize=10、minIdle=10、maxLifetime=30min(默认 1800s > 350s)、keepaliveTime 未显式设置(HikariCP 5.x 默认 300s,但只作用于 minIdle 内的空闲连接,且是「发 keepalive 探测」而非「强制重建」)。
【现象】 每天 1–2 次 P99 尖峰(单次 200–400ms,持续 1–3s),特征很典型:
SQLTransientConnectionException: Connection is not available, request timed out after 30000ms 或 CommunicationsException: Communications link failure,同一时刻同一 Pod 有 3–8 条失败(不是 1 条);HikariPool-1 - Connection is not available 与 Leaked connection 都没有,说明不是池耗尽,而是单条连接被 LB 静默切断后,池子还在把它当 alive 发放。初步判断:LB 的 350s 空闲超时 < HikariCP maxLifetime=1800s,且空闲期超过 350s 的连接(低峰期 minIdle 里躺着的)被 LB 单向 RST/FIN,但池子不知情;下一次 checkout 拿到这条「死连接」,第一次 query 才报错。批量出现是因为同一批连接在同一分钟窗口内被 LB 一起清掉。
【已尝试的排查与结果】
Threads_connected 抖动变大,LB 侧新建连接 QPS 也上去了,团队担心长期下来对 LB/DB 都是隐性成本;SELECT 1,minIdle=10 时约 3 QPS 的 keepalive 流量,DB 侧 Queries 计数被污染,慢日志 / 审计日志里混入大量 SELECT 1,排障时干扰大;HikariCP 5.x 的 connectionTestQuery 与 isConnectionAlive 路径,确认默认走 Connection.isValid(validationTimeout),只有 checkout 时才校验一次——这就是为什么「死连接」会漏到业务侧。【期望得到的答案形态】
SQLTransientConnectionException),还是应该在连接池层加「checkout 时强制 ping」?两者各自的性能代价和正确性风险(重试放大 / 非幂等 SQL 误重试)怎么权衡?hikari_pool_stale_connection_recreated),而不是混在 connectionTimeout / leakDetection 里?具体怎么埋点?我倾向的方案是「maxLifetime=300s + keepaliveTime=120s + 业务侧对 SQLTransientConnectionException 做一次幂等重试」,但 keepalive 流量和重试放大这两笔账我还没算清楚,想看看社区有没有更省的做法。