sql server 2022 通过并发 gam/sgam 更新缓解 tempdb 系统页争用,使原有存储过程无需改代码即可减少 pagelatch 等待;参数敏感计划(psp)自动适配不同参数值选择更优执行计划,但需避免 optimize for unknown 或强制 recompile。

SQL Server 2022 对并发控制的改进不是靠新增语法糖,而是通过底层机制缓解争用——尤其是 tempdb 和系统页锁存器这类“隐形瓶颈”。直接在存储过程中写 WITH (UPDLOCK) 或 SET TRANSACTION ISOLATION LEVEL 并不会自动受益,必须配合新版本的运行时行为调整。
为什么旧存储过程在2022上仍卡在tempdb?
很多存储过程频繁创建本地临时表(#tmp)、表变量(@table)或触发行版本(如启用了 READ_COMMITTED_SNAPSHOT),这些操作全挤在单个 tempdb 上。2022 之前,GAM/SGAM 页争用严重,多个会话同时分配空间时互相阻塞——现象是 Page latch contention on tempdb,但错误日志里不报错,只表现为存储过程执行延迟抖动。
2022 引入了并发 GAM 和 SGAM 更新,本质是把原来串行更新的分配位图拆成多段并行处理。效果不是“变快”,而是“不排队”。
- 无需改代码:只要升级到
SQL Server 2022 (16.0.1000.6+)并重启实例,所有现有存储过程自动受益 - 验证方式:运行
SELECT * FROM sys.dm_os_wait_stats WHERE wait_type LIKE 'PAGE%LATCH%',对比升级前后PAGEIOLATCH_UP和PAGELATCH_UP的累计等待时间 - 注意:若仍用旧版
tempdb文件配置(如单文件、未按逻辑 CPU 数设置文件数),新机制无法充分发挥
参数敏感计划(PSP)如何避免存储过程执行计划“一刀切”?
传统存储过程常因参数嗅探导致“第一次编译的计划,后续全套用”,比如 @CustomerID = 'A001' 返回 10 行走索引查找,而 @CustomerID = 'ALL' 应该走扫描——但旧版可能对两个参数都复用查找计划,造成慢查询。
SQL Server 2022 的 Parameter Sensitive Plan 不是开关选项,而是优化器自动启用的能力。它会在运行时根据实际参数值,从缓存中选择更匹配的计划(前提是统计信息足够新)。
- 必须关闭
OPTIMIZE FOR UNKNOWN:这个提示会禁用 PSP,让优化器放弃参数感知 - 避免在存储过程中用
RECOMPILE:虽然强制重编译能绕过缓存问题,但会放大 CPU 压力,PSP 是更轻量的替代方案 - 检查是否生效:执行
SELECT p.*, q.query_plan FROM sys.dm_exec_procedure_stats p CROSS APPLY sys.dm_exec_query_plan(p.plan_handle) q WHERE object_id = OBJECT_ID('YourProc'),观察同一存储过程是否出现多个不同plan_handle
乐观并发控制在2022中要避开哪些陷阱?
很多人用时间戳或版本号做乐观锁,比如:
UPDATE Orders SET Status = 'Shipped', Version = Version + 1 WHERE OrderID = @id AND Version = @oldVersion;
这在 2022 上依然有效,但容易忽略两个现实问题:
-
row_version列(timestamp类型)在 2022 中已标记为 deprecated,建议改用rowversion(同义词,但语义更清晰) - 如果存储过程里先
SELECT再UPDATE,中间间隙仍可能被其他事务修改——2022 没提供“原子读-校验-更新”新语法,必须靠应用层重试逻辑兜底 - 启用
READ_COMMITTED_SNAPSHOT后,SELECT不加锁,但UPDATE仍需获取排他锁;若并发高,锁等待时间反而比悲观锁更难预测
真正需要动手改的地方,往往不在 SQL 语法本身,而在 tempdb 配置、统计信息更新频率、以及是否无意中关闭了 2022 默认启用的优化路径。一个没动过一行代码的存储过程,在 2022 上跑得更稳,通常是因为它终于不再被 tempdb 的 GAM 页堵住入口。











