开放问答
下游慢 SQL 穿透超时后,连接池如何在 30 秒内被打满?预算怎么分
该问题的正文、回答与评论。
提问者 林望舒-131 · 状态 open
高并发连接池超时HikariCPMySQL性能调优
背景:我在维护一个订单中台,入口 QPS 约 1500,单次请求平均要串行调用 2~3 个下游(库存服务、价格服务、风控服务),其中价格服务最终会落到 MySQL 做聚合查询。整体链路我做了三层超时:Tomcat/网关 800ms → Spring Cloud 服务调用 500ms → HikariCP 连接获取 200ms + socket 读超时 800ms。连接池 maxSize=40,核心配置没做过激进调整。
现象:上周一次 MySQL 主从切换,从库上有一条聚合 SQL 从平时的 30ms 飙到 1200ms+。结果非常反直觉:从 SQL 变慢开始算,不到 30 秒,HikariCP 连接池被完全打满(active=maxSize,pending 队列里堆了几千个线程),进而导致整个订单接口 P99 直接打到 800ms 超时,甚至开始 5xx。更诡异的是,MySQL 侧 SHOW PROCESSLIST 里活跃连接数并没有超过 40,说明连接是被应用侧占住了没释放,而不是数据库侧真的在跑 40 条慢 SQL。
已尝试的排查:
- 抓了 HikariCP 的 metrics,确认
active在 30 秒内从个位数爬到 40; - dump 线程栈,发现大量业务线程阻塞在
HikariPool.getConnection等待pending; - 对比了 socket 读超时(800ms)和连接获取超时(200ms)——发现 socket 读超时其实没生效,因为 JDBC 驱动用的是
SocketImpl而不是Socket,setSoTimeout被绕过去了; - 试过把连接池从 40 缩到 10,抖动幅度确实小了一些,但 P99 反而更差了,因为等待连接的时间被拉长了。
期望得到的答案:想请教社区里做过类似治理的同学:
- 超时预算到底怎么分?网关 800ms 的总预算,拆到「服务调用 + 连接获取 + 实际 SQL 执行」三段,比例有没有经验值?我现在的 500/200/800 是不是本身就矛盾(服务调用超时 500ms 比 socket 读超时 800ms 还短,等于超时根本兜不住)?
- socket 读超时被 JDBC 驱动绕过这个问题,你们是怎么解决?是换驱动、换连接池,还是在 SQL 层面加
MAX_EXECUTION_TIME兜底? - 连接池 maxSize 到底按什么公式定?我见过「CPU 核数 × 10」的说法,也见过「下游 RT × QPS / 1000」的说法,两者差了一个数量级,实际生产里你们更信哪个?
最好能带上具体的配置片段或线上数据,谢谢。
评论
暂无评论。