确认是否启用psp需检查五项条件:统计直方图非均匀分布、最多前三谓词、完全优化计划、查询存储启用且read_write、兼容级别≥160;失效主因包括参数类型不一致、混用字面量、统计过期或兼容级不足。

SQL Server 2022 中参数嗅探不是要“消灭”,而是用参数敏感计划(PSP)自动分流——但必须满足条件,否则仍得手动干预。
怎么确认你的存储过程是否启用了参数敏感计划(PSP)?
PSP 不是所有带参数的查询都能自动获得多个执行计划,它有明确触发门槛:
- 列统计信息直方图必须显示非均匀分布(比如某字段 95% 值是 'A',5% 是 'B'~'Z')
- 优化器最多只对前三个谓词做 PSP 评估(
WHERE中多个@param = col时,只挑最敏感的三个) - 必须是完全优化的计划(
StatementOptmLevel="FULL"),带OPTION (RECOMPILE)或分布式查询不参与 - 查询需启用查询存储(
QUERY_STORE ON)且处于READ_WRITE模式 - 数据库兼容级别 ≥ 160(SQL Server 2022 对应版本号)
验证方式:执行 DBCC SHOW_STATISTICS('YourTable', 'YourIndex'),检查 Histogram 步骤数是否 ≥ 200,且 AVG_RANGE_ROWS 差异明显(比如从 1 跳到 10000)。
为什么加了 PSP 还是单计划?常见失效场景
PSP 不是开关一开就生效,很多情况下它根本不会被激活:
-
@param类型和长度每次调用不一致(比如一次传nvarchar(10),一次传nvarchar(50)),导致计划缓存分裂,PSP 失去比较基础 -
WHERE条件里混用参数和字面量:WHERE Company = @Company AND Status = 'Active'—— 后者破坏谓词独立性,PSP 不识别 - 统计信息过期或缺失直方图(
UPDATE STATISTICS没跑,或采样率太低),优化器无法判断“哪些值范围会导出大结果集” - 数据库兼容级别低于 160,PSP 功能不可用
局部变量怎么写才真正屏蔽参数嗅探?
局部变量仍是目前最稳、最可控的兜底方案,但写错等于没写:
- 声明和赋值必须在同一行:
DECLARE @LocalCompany NVARCHAR(100) = @Company;—— 分两行(DECLARE+SET)可能被优化器绕过 - 类型长度必须严格一致:
@Company是NVARCHAR(50),就不能声明成NVARCHAR(20),否则隐式转换让索引失效 - 只屏蔽关键参数:如果
WHERE有@Company和@Region,两个都得用局部变量,漏一个就可能被穿透嗅探
PSP 仍可生效:局部变量写法不阻止 PSP,只是把“参数值不确定性”转移到变量上,PSP 依然能基于 @LocalCompany 的分布做判断。
OPTION (RECOMPILE) 加在哪、什么时候加才有效?
只加在具体慢查询语句末尾,不是整个存储过程头上。它让这一句每次执行都重编译,其他语句仍可复用计划,开销可控:
- 适用场景:
OFFSET从 10 到 1000000 的分页查询、每天几次的报表、财务核对类关键路径 - 错误用法:
CREATE PROCEDURE ... WITH RECOMPILE—— 整个过程每次调都重编译,CPU 压力陡增 - 错误用法:
EXEC YourSP WITH RECOMPILE—— 只影响本次,不解决缓存污染问题 - 错误用法:加在
INSERT/UPDATE的子查询里却漏掉主语句
示例:SELECT * FROM SalesTable WHERE Company = @Company OPTION (RECOMPILE);
复杂点在于:PSP 和局部变量不是二选一,而是可以共存;但 PSP 生效前提非常具体,稍有偏差就退化为传统参数嗅探——而局部变量一旦写错类型或拆成两步,就彻底失效。这两个机制的边界,才是实际调优中最容易被忽略的地方。










