sql存储过程执行超时并非由单一机制触发,而是客户端commandtimeout(仅控首行返回)、sql server端remote query timeout、网络设备空闲超时三者中最小值决定实际断连;真正可控的是存储过程开头的set lock_timeout,且需配合防事务悬空与结果集消费。

SQL 存储过程执行时间过长本身不会直接“触发连接超时”,但会暴露并激活多层独立的超时机制,最终导致连接被中断——关键在于这些机制彼此不协同,最小值决定实际断连时机。
CommandTimeout 只管“发命令到第一行”的那段时间
这是 ADO.NET 或 JDBC 客户端层面的计时器,单位秒,默认常为 30。它只监控从 execute() 调用发出、到数据库返回**第一行结果**之间的等待时间。
- 后续几百 MB 结果集的读取、网络传输、客户端解析,全都不在它的管辖范围内
- 存储过程中写
WAITFOR DELAY '00:05:00'或循环处理 10 万条记录,只要首行没出来,倒计时就一直跑 - 设
CommandTimeout = 600,但 LB 在 90 秒切断 TCP 连接,客户端收到的是A transport-level error has occurred,根本等不到超时异常
真正掐断连接的往往是网络或服务端策略
客户端设置只是其中一环,实际断连由三者中**最先到期的那个**触发:
- SQL Server 的
remote query timeout(默认 600 秒,用sp_configure 'remote query timeout'查) - 负载均衡器 / 防火墙的空闲超时(常见 90 秒,TCP 连接静默即断)
- 连接池的
connection lifetime或idle timeout(如 HikariCP 的maxLifetime)
它们互不感知:SQL Server 可能还在执行第 3 条 UPDATE,客户端已收不到任何字节,连接物理消失。
存储过程内没有“总耗时限制”语法,但有唯一可控的锁刹车
MySQL 没有 max_execution_time 对 CALL 生效;SQL Server 也没有 EXEC ... TIMEOUT。唯一能在过程内主动干预的,是语句级锁等待控制:
- 必须显式写
SET LOCK_TIMEOUT 5000(单位毫秒),且放在存储过程开头 - 它只对锁冲突生效(如
SELECT ... FOR UPDATE卡在 KEY LOCK 上),对 CPU 密集型操作或 I/O 瓶颈无效 - 出错抛
error 1222,不是 1205,TRY CATCH里得按号区分 - 配合局部变量 +
WAITFOR DELAY '00:00:00.1'可做最多 3 次轻量重试,再多易雪崩
最容易被忽略的“假性超时”:事务悬空与结果集未消费
很多“超时”现象其实和时间无关,而是资源卡死:
- 存储过程开了
BEGIN TRANSACTION,但在异常分支里漏了ROLLBACK→ 事务长期挂起,连接被锁死,SHOW FULL PROCESSLIST里看到Command = 'Sleep'但Time持续增长 - 过程返回多个
SELECT,JDBC 客户端只取了第一个ResultSet就关连接 → 后续结果集没消费完,连接卡在等待状态,连接池里该连接永远无法复用 - 应用层用
CallableStatement却只关ResultSet,没关CallableStatement本身 → 物理连接未释放,leakDetectionThreshold日志会报堆栈
这类问题不会报超时错误,但表现就是连接池耗尽、新请求排队、监控里活跃连接数持续攀升——比超时更难定位。










