已采纳
回答正文
回复
讨论正文
该问题的正文与回答。
背景
我维护一条三层调用链:网关(超时 200ms)→ 聚合服务 B(OkHttp 调 C 超时 150ms)→ 数据服务 C(Spring Boot + HikariCP maximumPoolSize=20,连接 MySQL 8.0.32)。C 的核心接口执行一条聚合 SQL,P50 约 8ms,但受 InnoDB purge 线程和长事务影响,P99 会周期性抖到 180~400ms。
现象
当 C 的 P99 抖到 200ms+ 时:
Statement.executeQuery() 还没返回,线程被阻塞在 SocketInputStream.read() 上;active 从稳态 5 涨到 18~20,pending 从 0 涨到 3~5;已尝试
call.cancel():B 超时后调了 cancel,但实测 C 侧线程仍然阻塞到 SQL 返回——cancel 只关闭了 B→C 的 socket,C 侧的 read() 返回的是 IOException 还是正常返回取决于 TCP 栈实现,不可控;KILL QUERY:能杀,但需要知道 thread_id,且 KILL 本身有开销(~5ms),不适合高频场景;statement_timeout(pg)/ max_execution_time(MySQL hint):MySQL 8.0 的 max_execution_time 只对 SELECT 有效,而且设成 150ms 会把正常 P95 也杀掉;CircuitBreaker + TimeLimiter:TimeLimiter 本质也是 cancel 线程,对阻塞在 socket read 上的线程无效。期望得到的答案形态
Statement.cancel() 对已发出、正在服务端执行的 SQL 到底做了什么?是发 KILL QUERY 还是只关闭客户端 socket?不同驱动(MySQL Connector/J、PostgreSQL JDBC)行为是否一致?有没有驱动支持真正的「服务端取消」?SocketInputStream.read() 上的线程,Java 侧有没有可靠的「中断」手段?Thread.interrupt() 对 socket I/O 阻塞有效吗?还是只能关 socket 让它抛 IOException?StructuredTaskScope 对这个问题有没有实质改善(比如 shutdown 时能真正 interrupt 所有子任务)?已读参考:
这个问题如果解决好了,能直接指导连接池 / 线程池的容量规划和超时链路的防御性设计。