Msg 701错误源于执行阶段内存预估失败而非编译问题,首次执行耗时一分钟报错(尝试分配内存),后续秒错(复用失败计划);根本原因是排序、哈希连接等操作在resource pool 'internal'中无法获得预估内存,需通过分批处理、抑制并行、更新统计信息及OPTION(RECOMPILE)等手段优化执行路径。

SQL Server 存储过程中报 Msg 701,不是缺硬盘空间,是执行计划运行时拿不到足够内存 —— 这类问题不能靠加物理内存或调大 pagefile.sys 解决,得从查询逻辑和资源调度入手。
为什么 DBCC freeproccache 后首次执行慢、后续秒错
这说明问题不在编译阶段,而在执行阶段的内存申请环节。SQL Server 在生成执行计划时已预估所需内存(如排序、哈希连接、大型临时结果集),但运行时发现 resource pool 'internal' 无法满足该预估值。典型表现是:GetAllRevisions_Monthly 第一次跑一分钟才报错(正在尝试分配+等待),第二次立刻失败(直接复用失败计划,不重试)。此时清缓存没用,必须干预执行路径本身。
- 执行计划中存在未显式限制的
SORT或HASH JOIN操作,数据量稍大就触发内存暴涨 - 存储过程内用了未分页的
SELECT INTO #temp或CTE嵌套过深,中间结果集全驻内存 - 统计信息陈旧,导致优化器误判数据分布,为“本该小”的操作预留了“巨大”内存
用内存优化表(MEMORY_OPTIMIZED)替代传统临时表是否有效
基本无效,还可能让问题更隐蔽。内存优化表适用于高并发、短事务的 OLTP 场景,其内存占用是常驻且不可被 SQL Server 内存管理器动态回收的。对 Msg 701 这种“执行时瞬时内存峰值超限”问题,它既不降低峰值,也不参与 internal 资源池的配额协商。反而可能因缺乏统计信息导致更差的执行计划。
- 仅当临时表读写极频繁、且生命周期严格可控(如单次过程内创建→填充→查询→DROP)时,才考虑
MEMORY_OPTIMIZED - 必须配套使用
SCHEMA_ONLY选项,否则数据持久化到CHKPT文件,又占磁盘 - 注意:SQL Server 2008 不支持内存优化表,该方案仅适用于 2014+ 版本
分段处理的核心是控制单次内存申请上限
把一个大查询拆成多个小批次,本质是让每次执行只申请自己那部分所需的内存,避开全局资源池的硬性阈值。关键不在“怎么分”,而在“分在哪”——优先切在最耗内存的操作之前。
- 对含
ORDER BY+OFFSET/FETCH的分页逻辑,改用基于主键/时间戳的游标式分页(如WHERE id > @last_id ORDER BY id ASC TOP 1000),避免每次重排全量数据 - 对大表
JOIN,先用WHERE条件缩小驱动表范围,再分批JOIN,而不是JOIN完再WHERE - 用
OPTION (MAXDOP 1)抑制并行,防止多线程同时争抢内存;必要时加OPTION (QUERYTRACEON 8649)(慎用,需 DBA 授权)强制走串行计划 - 在存储过程开头加
SET STATISTICS XML ON,捕获实际执行计划,重点看EstimateRows和EstimatedAvailableMemory是否严重偏离
最容易被忽略的点:统计信息 + 参数嗅探
Msg 701 经常和参数嗅探绑定出现。比如 GetAllRevisions_Monthly @year = 2025 首次编译时传入的是空值或极小范围,优化器生成了轻量计划;但真实执行时传入的是全年数据,却仍复用那个轻量计划,导致内存预估严重不足。此时即使分段,只要没解决计划复用逻辑,问题照旧。
- 在存储过程内对关键变量加
OPTION (RECOMPILE),让每次执行都生成适配当前参数的计划 - 定期手动更新统计信息:
UPDATE STATISTICS [schema].[table] WITH FULLSCAN, NORECOMPUTE - 避免在 WHERE 中对字段做函数操作(如
WHERE YEAR(RevDate) = @year),这会让统计信息失效

















