大型GROUP BY查询卡住主因是内存授予不足,应查执行计划中GrantedMemory_KB与UsedMemory_KB是否接近或超限,并通过sys.dm_exec_query_memory_grants监控实时内存等待状态。

大型 GROUP BY 查询卡住,八成是内存没分够——不是看执行时间,而是直接查 GrantedMemory_KB 和 UsedMemory_KB 是否接近或超限。
怎么看当前查询实际拿了多大内存?
别猜,直接从执行计划 XML 里抠数字。运行前加一句:SET STATISTICS XML ON,执行后在 XML 中搜索两个关键字段:
-
GrantedMemory_KB:SQL Server 承诺给这个查询的内存上限 -
UsedMemory_KB:它真正在哈希分组中用掉的内存
如果 UsedMemory_KB ≥ GrantedMemory_KB,说明内存已满,SQL Server 正把哈希桶往 tempdb 刷;若出现 Warning: Memory Grant Warning 节点,基本已降级为磁盘哈希,IO 拉满。
怎么查正在跑的查询有没有在等内存?
用 sys.dm_exec_query_memory_grants 看实时状态,重点关注这几列:
-
grant_time为空 → 还在排队等内存 -
required_memory_kb远大于granted_memory_kb→ 内存严重不足 -
wait_time_ms> 0 且wait_type=RESOURCE_SEMAPHORE→ 典型内存争抢信号
注意:DBCC MEMORYSTATUS 是实例级快照,跟单个查询的内存授予无关,别用它诊断 GROUP BY 卡顿。
为什么改了 max server memory 还没用?
因为单个查询能拿多少内存,不只看总内存,还受三重限制:
- 资源调控器中工作负荷组的
REQUEST_MAX_MEMORY_GRANT_PERCENT(比如 default 组默认 25%,改到 10% 就会压低所有查询上限) - 查询自身的
MIN_GRANT_PERCENT提示(如OPTION (MIN_GRANT_PERCENT = 30)) - 并行度
MAXDOP:设为 0 容易让小查询抢光内存,大查询反而饿死;对 GROUP BY,MAXDOP 2或4往往比 0 更稳
改完记得执行 ALTER RESOURCE GOVERNOR RECONFIGURE 生效,否则只是写进去了没生效。
CTE + GROUP BY 套太多容易爆内存吗?
会。尤其当 CTE 里用了 SELECT * 或没加过滤条件时,中间结果集可能远超预期。比如:
WITH active_users AS ( SELECT user_id FROM users WHERE status = 'active' ), recent_orders AS ( SELECT order_id, user_id FROM orders WHERE created_at > '2025-01-01' ) SELECT COUNT(*) FROM recent_orders ro JOIN active_users au ON ro.user_id = au.user_id;
如果 active_users 返回 500 万行,但最终只用其中 2 万行关联,那这 498 万行就是白占内存。更稳妥的做法是:CTE 里就只选必要字段+加索引提示,或者改用临时表分批拉 ID。
真正难调的不是“怎么加内存”,而是“怎么让 SQL Server 信得过你的数据量估算”——统计信息过期、参数嗅探失准、隐式转换,都会让优化器低估行数,从而分太少内存,触发 spill。所以每次调内存前,先确认执行计划里的 Estimated Rows 和 Actual Rows 是否相差一个数量级。

















