SQL Server参数嗅探是指优化器基于首次参数值生成并缓存执行计划,导致后续不同参数值时性能骤降;可通过查询计划差异、DMV统计波动识别,推荐用OPTION(RECOMPILE)、局部变量或OPTIMIZE FOR缓解。

SQL Server 的参数嗅探(Parameter Sniffing)是指查询优化器在首次编译存储过程或参数化查询时,基于传入的实际参数值生成执行计划,并将该计划缓存复用。问题在于:如果首参值具有特殊分布(如极低选择性、空值、边界值),生成的执行计划可能对其他参数值严重低效——这就是“首执行计划陷阱”。
为什么首参会“带偏”整个计划
优化器不会为每个参数值重新编译,而是复用缓存计划。例如:
- 首参是 @status = 'Cancelled'(仅占0.1%数据),优化器选索引查找+嵌套循环;
- 后续调用 @status = 'Active'(占95%数据),同样走查找+循环,导致数万次随机IO,性能暴跌。
本质是统计信息与参数值耦合过紧,而计划缓存缺乏上下文感知能力。
快速识别是否中招
不用猜,直接查缓存和实际执行差异:
- 用 sys.dm_exec_query_stats + sys.dm_exec_sql_text 找到对应查询,看 execution_count 高但 avg_logical_reads 或 max_elapsed_time 波动剧烈;
- 对比不同参数值下 SET STATISTICS XML ON 输出的执行计划——若形状/运算符/预估行数明显不同,大概率是参数嗅探;
- 检查 sys.dm_exec_cached_plans 中该计划的 usecounts 和 objtype,确认是否被多参数复用。
实用缓解方案(按推荐顺序)
不追求“根治”,重在可控、低侵入、见效快:
- OPTION (RECOMPILE):加在语句末尾,强制每次编译。适合执行频次低、参数组合少、或关键路径需绝对稳定性的场景;
- 局部变量绕过嗅探:在存储过程中把参数赋给同类型局部变量,再在WHERE中使用该变量(如 DECLARE @s VARCHAR(20) = @status;),让优化器失去参数值线索,转而按统计密度估算;
- OPTIMIZE FOR 语义明确化:用 OPTIMIZE FOR (@p = '典型值') 或 OPTIMIZE FOR UNKNOWN,前者适配常见负载,后者退回到平均分布估算;
- 避免 sp_executesql 中混用字面量和参数——字面量会固化计划分支,加剧偏差。
长期治理建议
参数嗅探不是Bug,是设计权衡。真正要做的,是让系统对参数变化更鲁棒:
- 定期更新关键表的统计信息(尤其数据倾斜列),WITH FULLSCAN 比默认采样更准;
- 对高频多态查询,考虑拆分为多个专用子过程(如 GetActiveOrders / GetCancelledOrders),计划各管各;
- 监控 sys.dm_exec_procedure_stats 中的 cached_time 与 last_execution_time 差距,异常长缓存可能意味着计划已脱节;
- SQL Server 2016+ 可开启 Query Store,自动捕获不同参数下的计划并支持强制策略,是可观测性基石。










