直接结论:别先调work_mem或tmp_table_size,应优先通过执行计划识别HashAggregate溢出、Using temporary或GrantedMemory_KB远小于RequiredMemory_KB等内存瓶颈信号;再以强制SortAggregate、前置过滤精简字段、大文本哈希化为三步实操路径优化。

直接结论:别先调 work_mem 或 tmp_table_size,先看执行计划里是不是 HashAggregate 溢出、Using temporary 出现,或者 GrantedMemory_KB 远小于 RequiredMemory_KB——这些才是内存真卡点。
怎么一眼识别 GROUP BY 正在吃光内存?
不是等 OOM 才发现,得从执行计划里提前抓信号:
-
EXPLAIN (ANALYZE)(PostgreSQL)里出现HashAggregate+Spill to disk或temp file - MySQL 的
Extra列写着Using temporary,且type是ALL或index - SQL Server 中
sys.dm_exec_query_memory_grants显示granted_memory_kb比required_memory_kb小 30% 以上 - 日志里反复出现
could not resize shared memory segment(PG)或resource_semaphore等待(SQL Server)
为什么加内存参数常失效?
因为数据库不是“内存不够才慢”,而是算法本身决定了内存消耗模式:
- PostgreSQL 的
work_mem是每个操作符独占的,一个查询含多个GROUP BY或嵌套JOIN,会各自申请一份,50 个并发 × 128MB = 6.4GB,很容易触发 swap - MySQL 的
sort_buffer_size对GROUP BY完全不生效,真正起作用的是tmp_table_size和max_heap_table_size,且取二者较小值 - SQL Server 不靠全局内存配置,而依赖单次查询的内存授予(query memory grant),
max server memory调再高也没用 - MySQL 8.0.22+ 默认启用
hash_join,此时join_buffer_size彻底失效
真正有效的三步实操路径
核心是让数据库少缓存、少比较、少读行:
-
强制走 SortAggregate:加
ORDER BY且必须与GROUP BY完全一致(列名、顺序、方向),例如GROUP BY user_id, status ORDER BY user_id, status;有索引支撑时,排序可免,仅流式聚合 -
前置过滤 + 精简字段:把
WHERE写在GROUP BY前,且只选分组字段和聚合字段;避免SELECT *或带TEXT/JSON字段;状态字段优先用TINYINT替代VARCHAR(20) -
大文本字段必须哈希化:对
VARCHAR(2000)或JSON_EXTRACT(data, '$.email'),改用GROUP BY SHA2(email, 256)或MD5(email),并建函数索引(MySQL:CREATE INDEX idx_hash ON t (SHA2(email, 256));PG:CREATE INDEX idx_hash ON t ((md5(email))))
容易被忽略的隐性陷阱
很多优化失败,不是方法不对,而是细节没控住:
- 字符串分组字段类型不一致:MySQL 中
VARCHAR(50)和VARCHAR(100)关联,会触发隐式转换,索引失效 - 复合索引顺序错位:
GROUP BY a, b配合WHERE c = ?,索引应建为(c, a, b),不是(a, b, c) - JSON 字段提取后没建函数索引:
GROUP BY JSON_EXTRACT(data, '$.user_id')在 MySQL 8.0+ 之前根本无法走索引 - 物化汇总表没设定时刷新:每天跑一次
INSERT INTO daily_summary SELECT ... GROUP BY ...,但忘了加ON CONFLICT DO UPDATE或去重逻辑,导致数据错乱

















