参数敏感计划(PSP)不优化JOIN算法本身,而是为不同参数值缓存多个执行计划,以应对数据分布不均导致的计划质量差异;它仅响应WHERE或ON中的参数化过滤谓词,不干预优化器对嵌套循环、哈希或合并连接的选择逻辑。

参数敏感计划(PSP)不直接优化JOIN算法
SQL Server 2022 的参数敏感计划(PSP)优化本身不改变JOIN的物理实现(比如不让HASH JOIN变MERGE),它只解决“同一个参数化查询,不同参数值导致执行计划质量天差地别”的问题。也就是说:如果某条含JOIN的语句,在传入 @status = 'A' 时走的是高效索引查找+LOOP JOIN,但传入 @status = 'Z' 时因预估行数爆炸而退化成全表扫描+HASH JOIN——PSP 就可能为这两个参数范围分别缓存两个计划。它不干预JOIN类型选择逻辑,只让“选对计划”这件事更自动。
哪些JOIN场景可能触发PSP优化
PSP是否启用取决于谓词是否满足非均匀分布、基数跨度大、且被优化器判定为“高风险”。对JOIN语句来说,真正起作用的是WHERE或ON中的**参数化过滤谓词**,而非JOIN本身。以下条件越明显,越可能激活PSP:
- JOIN字段本身不带参数,但
WHERE里有像WHERE o.order_date > @from_date这类范围查询,且该列直方图显示严重倾斜(例如95%订单集中在最近7天) - 关联后立即用参数过滤右表,如
LEFT JOIN orders o ON c.id = o.customer_id WHERE o.status = @status,而status列只有3个值但分布极不均('pending'占0.1%,'shipped'占99.8%) - 使用
OPTIMIZE FOR UNKNOWN或未指定OPTIMIZE FOR,且数据库兼容级别为160、PARAMETER_SENSITIVE_PLAN_OPTIMIZATION = ON
注意:LEFT JOIN中若@status用于左表过滤(如WHERE c.type = @type),则PSP更可能作用于左表驱动逻辑,间接影响JOIN输入大小——这才是它影响JOIN性能的真实路径。
如何确认PSP是否在你的JOIN语句中生效
不能只看执行计划图形界面。必须抓取实际执行后的XML,搜索关键节点:
- 找
<ParameterSensitivePlan>1</ParameterSensitivePlan>—— 表示该计划已识别为PSP调度计划 - 找
<ParameterSensitivePlanVariant>块 —— 每个块对应一个参数范围生成的子计划(即查询变体) - 对比不同参数值下
sys.dm_exec_query_stats中同一query_id是否对应多个plan_id,且is_forced_plan为0、last_execution_time错开
如果执行计划里只有<Adaptive>1</Adaptive>而没有ParameterSensitivePlan,说明自适应连接(Adaptive Joins)在起作用,和PSP无关——这两者独立开关、独立触发。
PSP对JOIN的副作用与规避点
PSP本身是“好意”,但和JOIN共存时容易暴露底层脆弱性:
- 它会为不同参数生成多个计划变体,每个变体都可能选择不同JOIN类型(LOOP/HASH/MERGE),若某变体因统计信息滞后选了HASH JOIN,又碰上内存预估偏差,仍会触发
ERROR 701 - 若JOIN涉及跨库或OPENQUERY,PSP无法穿透远程数据源统计信息,调度表达式失去依据,常被跳过(XE事件
psp_skipped_reason_enum中UnsupportedComparisonType或RemoteQueryNotSupported) - 显式指定
OPTION (LOOP JOIN)或OPTION (USE HINT('DISABLE_BATCH_MODE_ADAPTIVE_JOINS'))会压制PSP调度逻辑——优化器认为“你已接管计划控制权”,不再生成变体
最易被忽略的一点:PSP只基于**单个谓词**的统计直方图做分桶,如果你的JOIN语句同时依赖@date和@region两个参数过滤,而两者相关性极强(比如华东区订单集中在Q3),PSP默认最多只挑前三个谓词,很可能只选了日期没选区域,导致变体划分失效。这时手动拆分或加OPTIMIZE FOR反而更稳。

















