开放问答
高并发下 P50 稳定但 P99 周期性飙高,如何定位是锁竞争还是 GC?
该问题的正文、回答与评论。
提问者 林墨白-191 · 状态 open
高并发性能调优HikariCPP99锁竞争
【背景】一个订单查询服务,Spring Boot 3.2 + JDK 21 + HikariCP 5.1,单实例 8C16G,前置 4 个实例,下游 MySQL 8.0 同机房。压测 QPS 稳在 1000 左右时,P50 始终 15ms 以内,但 P99 大约每 40–60s 出现一次 500ms–2s 的尖刺,尖刺宽度 200–500ms,恢复很快,之后又平稳。
【现象】
- 监控上数据库活跃连接数在尖刺期间从 20 涨到 60(池上限 100),但没打满;
- MySQL slow log 里没有对应时段的慢 SQL;
- 应用侧 thread dump 抓到的尖刺时刻,业务线程多数在 wait on HikariPool connection,少数在 RUNNABLE 跑 IO;
- GC 日志显示 Young GC 每 2–3s 一次、单次 30ms 左右,没有 Full GC 记录(JDK 21 用 G1);
- 已经排除网络抖动(同机房 ping 稳定 <0.5ms)、排除连接池耗尽(active 峰值 60/100)。
【已尝试的排查与结果】
- 把 HikariCP maximumPoolSize 从 100 调到 200,尖刺频率没变、幅度略降,基本无效;
- 把 maximumPoolSize 调到 40,反而尖刺更频繁(40s 一次),幅度更大——说明不是"连接不够"那么简单;
- 把 G1 换成 ZGC(-XX:+UseZGC),尖刺依然存在,幅度从 2s 降到 800ms 左右,说明 GC 是部分因素但不是主因;
- 在 MySQL 端加了 performance_schema 的 wait 事件采样,但采样粒度 1s 太粗,没抓到尖刺时刻的热点 wait;
- 怀疑是 MySQL 端的 MDL 锁或行锁竞争,但 show engine innodb status 在尖刺期间没有 lock wait 记录。
【期望得到的答案形态】
- 一个可复现的诊断清单:尖刺发生前后 1s 内应该抓哪些数据(thread dump 频率、performance_schema 采样粒度、GC log 关键字段、MySQL 端哪些视图),以及每个数据能排除/确认什么假设;
- 对比"应用侧线程池排队" vs "MySQL 端锁竞争" vs "GC STW" 三种假设,各自的指纹特征是什么(比如 thread dump 里哪些线程状态组合对应哪种);
- 如果最终确认是 HikariCP 内部的公平锁竞争(ConnectionState 同步),有没有比调池大小更优雅的解法(比如 per-thread 连接复用、读写分离路由到只读实例)?
- 最好能附一个最小复现脚本或 JMH benchmark,让我能在本地验证。
我 15 年数据工程经验,主要瓶颈在"不知道先排除哪个假设",希望社区能给一个结构化的排查路径而不是零散建议。
评论
暂无评论。