SQL Server 2019 并发编译队列问题是高并发下COMPILE锁争用和计划缓存不稳的表征,根源在于参数嗅探、临时表、动态SQL及内存配置不当;应优先固化执行计划、减少隐式重编译、优化tempdb与MAXDOP配置。

SQL Server 2019 存储过程并发编译队列不是可直接“调优”的对象,而是高并发场景下查询编译争用的表征;真正要动的是编译锁(COMPILE)竞争和计划缓存策略。
为什么会出现“并发编译队列”问题
当大量会话同时首次执行同一个存储过程(尤其带参数化、临时表或动态SQL),SQL Server 会为每个会话尝试生成执行计划。此时多个线程会排队等待获取 COMPILE 锁,表现为 CMEMTHREAD 或 RESOURCE_SEMAPHORE 等待类型升高,sys.dm_exec_requests 中出现大量 wait_type = 'CXPACKET' 或 'SOS_SCHEDULER_YIELD' 伴随高 CPU,但实际工作线程却卡在编译阶段。
这不是存储过程写得不好,而是 SQL Server 的计划生成机制在争抢内存和同步资源。
- 参数嗅探 + 多个不同参数值 → 触发多次编译,而非复用
- 存储过程中含
CREATE TABLE #t→ 每次执行都触发重编译(RECOMPILE行为) - 使用了
EXEC(@sql)且未加WITH RECOMPILE→ 计划缓存混乱,反而加剧编译抖动 - 服务器配置了过小的
max server memory→ 缓存压力大,计划频繁被驱逐,被迫重编译
优先检查并固化执行计划
避免重复编译最有效的方式,是让 SQL Server 少“思考”,多“复用”。重点不是禁止编译,而是让编译结果稳定落地:
- 对关键存储过程,用
sp_create_plan_guide绑定一个已验证高效的执行计划,绕过运行时优化器决策 - 确认是否启用了
QUERY_OPTIMIZER_HOTFIXES数据库选项(SQL Server 2019 默认关闭),某些补丁级优化可能影响编译路径稳定性 - 检查是否误开了
optimize for ad hoc workloads:该选项对单次执行查询友好,但对高频存储过程反而增加编译开销,建议设为OFF - 用
sys.dm_exec_query_stats查看目标存储过程的execution_count和plan_generation_num:若后者远大于前者,说明频繁重编译,需定位原因(如临时表、SET选项变更等)
从代码层减少隐式重编译触发点
很多重编译不是你写的 WITH RECOMPILE,而是语句结构自带的“编译敏感性”:
- 把
SELECT ... INTO #t改成CREATE TABLE #t+INSERT INTO #t:前者强制每次重编译,后者只在首次解析时编译一次 - 避免在存储过程中修改 SET 选项(如
SET ANSI_NULLS OFF),哪怕只改一次,也会导致计划失效 - 用
OPTION (KEEPFIXED PLAN)替代盲目加WITH RECOMPILE—— 后者是“每次都编”,前者是“永远别换这个计划” - 如果必须用动态SQL,统一用
sp_executesql并显式传参,而非拼字符串 +EXEC();否则参数无法参数化,每次都是新计划
最后才考虑实例级资源调控
当以上都收敛后仍有编译排队,说明硬件/配置成了瓶颈:
- 确认
max degree of parallelism(MAXDOP)没设成 1:编译本身是单线程操作,但低MAXDOP会拖慢整个编译流程,尤其在高逻辑 CPU 机器上 - 检查
tempdb文件数是否匹配逻辑 CPU 数(一般 ≤8),tempdb竞争会间接拖慢编译(因编译过程大量使用tempdb) - 不要轻易关超线程(
Hyper-Threading):SQL Server 2019 对 HT 优化较好,除非实测Context Switches/sec > 5000且关 HT 后编译延迟下降明显,否则不建议动 -
query_store必须开启并设为READ_WRITE:它能捕获编译失败/超时事件,比盲查 DMV 更早暴露编译链路瓶颈
真正难处理的,从来不是“怎么让编译快一点”,而是“怎么让大多数请求根本不用编译”。计划稳定性比编译速度重要十倍——一旦某个存储过程在高峰期每秒被调用 200 次,而每次编译耗时 80ms,那光编译就吃掉 16 个核心秒,这比任何索引优化都致命。










