判断GROUP BY内存是否吃紧的唯一可靠依据是执行计划中的GrantedMemory_KB和UsedMemory_KB:若UsedMemory_KB≥90%的GrantedMemory_KB,说明哈希分组濒临溢出,已出现Memory Grant Warning并降级为磁盘哈希;GrantedMemory_KB为0或极低则可能被资源调控器截断;需结合sys.dm_exec_query_memory_grants等动态视图定位等待内存的查询。

直接看执行计划里的 GrantedMemory_KB 和 UsedMemory_KB,这是判断 GROUP BY 是否内存吃紧的唯一可靠依据——别信缓存命中率、别猜统计信息、更别用 DBCC MEMORYSTATUS 看全局内存。
查单次 GROUP BY 查询的实际内存分配
运行查询前加 SET STATISTICS XML ON,执行后在结果页点开 XML 执行计划,用 Ctrl+F 搜 GrantedMemory_KB 和 UsedMemory_KB:
- 如果
UsedMemory_KB≥ 90% 的GrantedMemory_KB,说明哈希分组快撑满,tempdb 开始刷出临时数据 - 出现
Warning: Memory Grant Warning节点,代表已降级为磁盘哈希,性能断崖式下跌 -
GrantedMemory_KB为 0 或极低(如 1024),大概率是被资源调控器或工作负荷组截断了
查正在运行的 GROUP BY 查询是否卡在等内存
用 sys.dm_exec_query_memory_grants 找出“干等不开工”的查询:
-
grant_time IS NULL:查询已编译但没拿到内存,正在排队 -
required_memory_kb > granted_memory_kb * 2:预估要 200MB,只批了 80MB,大概率后续会 spill -
is_small = 1:SQL Server 认定这是小查询,自动压低内存授予,GROUP BY 却偏偏容易被误判
配合 sys.dm_exec_requests 关联 session_id,能立刻定位到哪条 GROUP BY 在等、等了多久、阻塞源是谁。
区分资源调控器和查询级内存限制
GROUP BY 内存不够,可能不是实例内存少,而是被两层机制主动掐了:
- 若你改过
REQUEST_MAX_MEMORY_GRANT_PERCENT(比如设成 10),那所有走[default]工作负荷组的 GROUP BY 都会被硬性限制——哪怕服务器空闲,它也拿不到超过总内存估算值的 10% - 查询末尾加了
OPTION (MIN_GRANT_PERCENT = 25),但实际授予仍很低,说明资源池总配额(max_memory_kb)已见顶,得查sys.dm_resource_governor_resource_pools - 没配资源调控器?那默认是 25%,但 SQL Server 2016+ 对并行 GROUP BY 会按
MAXDOP折算单线程内存,MAXDOP 8时每线程只分到估算值的 1/8,极易 spill
避免被统计信息误导导致的假内存问题
看起来是内存不够,其实是 SQL Server 估算错了行数,被迫选错执行计划:
- 执行计划里看到
Sort+Stream Aggregate而非Hash Match (Aggregate),八成是统计信息过期,让优化器以为 GROUP BY 只有几千行,结果实际几千万行,全靠排序撑住 - 这种情况下
GrantedMemory_KB很小,UsedMemory_KB却爆高(大量 tempdb 排序),但改内存参数没用,得先UPDATE STATISTICS或加OPTION (RECOMPILE) - 用
sys.dm_exec_query_plan查StatementEstRows和实际输出行数比对,差 10 倍以上基本可确认估算失准
真正难处理的不是内存调小了,而是调大了也没用——当哈希分组退化成磁盘排序,或者资源调控器把上限焊死,再看 GrantedMemory_KB 就只是个安慰数字了。

















