mysql存储过程支持返回多个结果集,但需启用multiplestatements=true、用callablestatement.execute()启动,并循环调用getmoreresults()和getresultset()逐个消费,否则连接会卡在sleep状态。

连接调用存储过程后无法继续查询,大概率不是连接“断了”,而是连接被卡在未释放状态——最常见原因是没消费完所有结果集,或事务没提交。
存储过程返回多个 ResultSet,但只取了第一个
MySQL 存储过程允许用多个 SELECT 语句返回多个结果集。JDBC 要求客户端**逐个获取并完全消费**,否则连接会卡在 Sleep 状态,看似空闲实则被占用。
- 现象:调用
cs.execute()后再执行conn.createStatement().executeQuery("SELECT 1")报超时或阻塞 - 验证方式:执行
SHOW FULL PROCESSLIST,看对应线程的State是Sleep且Info为空(说明正等下一个结果集) - 解决办法:必须循环调用
cs.getMoreResults()+cs.getResultSet(),直到返回false - 示例片段:
try (CallableStatement cs = conn.prepareCall("{CALL proc_multi_select()}")) {
cs.execute();
do {
try (ResultSet rs = cs.getResultSet()) {
if (rs != null) {
while (rs.next()) { /* 消费掉 */ }
}
}
} while (cs.getMoreResults());
}
CallableStatement 或 ResultSet 未关闭
即使用了连接池,CallableStatement 不关,底层物理连接就不会归还;漏关 ResultSet 同样会导致连接挂起。
- 常见写法错误:
cs.close()前没调cs.getResultSet().close(),或只关了ResultSet忘关CallableStatement - 安全做法:一律用
try-with-resources,确保CallableStatement和所有衍生ResultSet都自动关闭 - 注意:不能只对
ResultSet用try,必须把CallableStatement包进去,否则资源仍泄漏
存储过程内事务未提交或回滚
过程里有 START TRANSACTION 却没配对的 COMMIT 或 ROLLBACK,尤其在异常分支里遗漏,会导致连接长期持有事务锁,后续查询被阻塞。
- 检查点:过程定义中是否有显式
BEGIN ... START TRANSACTION?有没有IF ... THEN ... ROLLBACK分支? - 风险操作:过程里含
SELECT ... FOR UPDATE、大表UPDATE、或嵌套循环中反复查写,都可能让事务拖长 - 建议:过程内尽量避免长事务;如必须,确保每个出口路径(包括
EXIT HANDLER)都有明确的COMMIT或ROLLBACK
连接池提前回收了“活着但卡住”的连接
连接池(如 HikariCP)按自身规则判断连接是否可用,它不关心你是不是还在等第二个 ResultSet——只要超过 leakDetectionThreshold 就标记为泄露,甚至主动 close 掉。
- 典型表现:日志出现
Connection leak detection triggered,堆栈指向某次CALL调用后没再操作 - 这不是数据库问题,是应用层资源管理失当:连接池认为你“拿了不用”,就收走了
- 临时缓解:调大
leakDetectionThreshold,但治标不治本;根本解法仍是上面三条——消费完结果、关干净语句、事务收尾干净
真正难排查的,往往不是语法错或权限错,而是那些“看起来执行完了,其实还没完”的隐性卡点:多结果集没扫完、getResultSet() 返回 null 就以为结束了、EXIT HANDLER 里忘了 ROLLBACK……这些地方一漏,连接就静默卡死,连 SHOW PROCESSLIST 都只显示 Sleep,不像报错那么醒目。











