SQL Server单次执行耗时最可靠方式是SET STATISTICS TIME ON,关注SQL Server Execution Times中的Elapsed time;多版本对比需结合sys.dm_exec_cached_plans与object_id过滤,并清除计划缓存防参数嗅探干扰。

用 SET STATISTICS TIME ON 捕获真实执行耗时
SQL Server 中最直接、最可靠的单次执行时间度量方式就是开启统计开关,它绕过客户端延迟和网络抖动,只反映 SQL 引擎内部的 CPU 和 Elapsed 时间。关键点在于:必须在每个存储过程调用前重置统计状态,否则累积值会污染对比结果。
- 每次测试前执行
SET STATISTICS TIME ON,结束后立即SET STATISTICS TIME OFF - 避免在 Management Studio 中勾选“包含实际执行计划”——它会显著拖慢执行,且引入额外编译开销,导致基准失真
- 首次执行常含编译开销,应先单独运行一次(不计入数据),再连续执行 3–5 次取
elapsed time平均值 - 注意输出中的
SQL Server Execution Times:行,关注Elapsed time = xxx ms,而非Parse and Compile Time
用 sys.dm_exec_query_stats 提取历史缓存的平均性能指标
当多个版本的存储过程已上线运行,想回溯对比它们在生产环境的真实负载表现,不能靠人工重跑——这时要查动态管理视图。但直接查 sys.dm_exec_query_stats 有陷阱:它按查询语句哈希聚合,而同名存储过程不同版本的底层语句可能完全一样(比如只改了注释或变量名),导致指标混在一起。
- 务必结合
object_name(plan_handle)或sys.dm_exec_sql_text(plan_handle)确认对应的是目标存储过程 - 过滤条件示例:
WHERE text LIKE '%CREATE%PROC%YourProcName%'不可靠;应先用sys.procedures获取object_id,再关联sys.dm_exec_cached_plans的objtype = 'Proc'和cacheobjtype = 'Compiled Plan' - 重点关注
total_elapsed_time / execution_count(平均耗时)、total_logical_reads / execution_count(平均逻辑读)——这两项比 CPU 时间更能反映 I/O 压力差异
避免参数嗅探干扰导致的版本误判
同一存储过程不同版本若使用相同参数值测试,但执行计划被旧版缓存“污染”,新版优化器可能根本没机会生成新计划,结果看似“变慢”,实则是用了过期计划。这是多版本对比中最隐蔽的误差源。
- 每次切换版本后,强制清除该过程的计划缓存:
DBCC FREEPROCCACHE (<code>plan_handle),或更安全地用ALTER PROCEDURE YourProcName WITH RECOMPILE临时加编译提示 - 不要依赖
WITH RECOMPILE长期开启——它虽保证每次新建计划,但会掩盖“计划稳定性”这一关键维度;基准测试阶段可用,生产对比时应测默认行为 - 检查
sys.dm_exec_cached_plans中usecounts是否为 1:如果是,说明计划未复用,参数嗅探影响被放大,此时需补测典型参数组合(如空值、边界值、高频值)
用 sp_WhoIsActive 捕获阻塞与资源等待细节
当两个版本耗时差异不大(比如 120ms vs 145ms),但线上反馈“新版卡顿明显”,大概率是等待类型变了——比如旧版等 ASYNC_NETWORK_IO(客户端拉数据慢),新版却卡在 LCK_M_S(锁等待)。这种差异仅看耗时数字会漏判。
- 在执行存储过程时,并行运行
sp_WhoIsActive @get_task_info = 2, @get_outer_command = 1,捕获活动会话的wait_info和blocking_session_id - 特别留意新版是否新增了
WRITELOG(日志写入瓶颈)或PAGEIOLATCH_SH(磁盘读压力)——这指向索引缺失或统计信息陈旧,而非代码本身问题 - 如果新版触发了更多自增锁(
KEY: 2:72057594038321152 (b100e8a0c6d8)类似格式),说明存在热点页争用,需检查聚集索引设计或考虑NEWSEQUENTIALID()
UPDATE STATISTICS,就可能让新版计划走嵌套循环而旧版走哈希匹配,结论全错。


















