单纯调高CommandTimeout基本没用,因其仅控制客户端从发命令到首行返回的等待时间,不涵盖结果读取、锁等待等环节;实际断连由SQL Server端remote query timeout、网络设备空闲超时中的最小值决定,且SET LOCK_TIMEOUT才是存储过程中唯一可控的锁等待“刹车”机制。

单纯调高 CommandTimeout 基本没用,它只管“发命令到第一行返回”那段时间,后面读几百MB结果、锁住等5分钟,它完全不管。
为什么 CommandTimeout 经常设了也白设
它只是 ADO.NET 或 JDBC 客户端的计时器,一旦 SQL Server 开始执行(哪怕刚进存储过程第一行),这个倒计时就启动了。但真正断连的往往不是它:
- SQL Server 自己的
remote query timeout(默认 600 秒),用sp_configure 'remote query timeout'查 - 负载均衡器或防火墙的空闲超时(常见 90 秒),TCP 连接直接被掐断,客户端报
A transport-level error has occurred - Entity Framework 中
DbContext.Database.CommandTimeout必须在FromSqlRaw()调用前设置,AsNoTracking()之后再设就失效 - JDBC 用户注意:
socketTimeout(TCP 层)和queryTimeout(Statement 级)是两套机制,混用会互相干扰
存储过程里真正能控住“卡死”的开关是 SET LOCK_TIMEOUT
这是 SQL Server 唯一能在语句级干预锁等待的原生机制,不是优化,而是防雪崩:
- 必须写在存储过程开头,比如
SET LOCK_TIMEOUT 5000(单位毫秒) - 设为
0表示锁冲突立即失败;设为-1(默认)就是无限等 - 只对锁等待生效(如 SELECT 等 KEY LOCK、UPDATE 等 PAGE LOCK),对大排序、递归 CTE、I/O 瓶颈完全无效
- 出错时抛
error 1222(锁超时),不是1205(死锁),TRY...CATCH里得按号区分处理 - 配合
WAITFOR DELAY '00:00:00.1'+ 局部变量可做轻量重试,但最多 3 次,否则可能把并发拖垮
查执行计划比调超时更管用
很多“超时”根本不是时间不够,而是执行计划选错了,比如参数嗅探导致全表扫描卡在锁上:
- 在 SSMS 中右键存储过程 → “显示估计的执行计划”,重点看有没有
聚集索引扫描、表扫描或黄色警告图标 - 用
DBCC SHOW_STATISTICS('table_name', 'index_name')看统计信息是否陈旧(rows sampled远小于rows就要更新) - 临时加
OPTION (RECOMPILE)可破参数嗅探,但别滥用,编译开销本身也耗时 - 大结果集别硬扛:拆成多次小查询,或先写入临时表再关联,避免单次 RPC 长期占用连接
真正容易被忽略的是:SQL Server Agent Job 或 SSIS 调用存储过程时,CommandTimeout 完全不生效,得去作业步骤里改“超时(秒)”配置项;而 WAITFOR DELAY 在存储过程中不会暂停任何一层的超时计时器——它只让当前会话歇会儿,客户端和服务器的倒计时照跑。

















