如何避免SQL聚合查询占用过多内存

P粉602998670

P粉602998670

2026-07-24

403人浏览

原创

group by大字段直接撑爆哈希表内存,因数据库将分组键(如varchar(2000)、text)与聚合值全量缓存于内存哈希表,单键几kb×千万级分组即达数十gb;根本解法是用sha2/md5哈希或substring_index等确定性截断替代原始大字段,并避免select*和where索引失效。

如何避免sql聚合查询占用过多内存

GROUP BY 大字段直接撑爆哈希表内存

数据库执行 GROUP BY 时,会把分组键和聚合中间值全量缓存在内存哈希表里。如果分组字段是 VARCHAR(2000)TEXT 或 JSON 字段,单个键几 KB,一千万不同值就轻松吃掉几十 GB 内存——根本没机会走到聚合计算那步。

常见错误现象:EXPLAIN 里看不到警告,但 Extra 出现 Using temporary 就已是危险信号;查询卡住、OOM killer 杀进程、或日志里反复报 could not resize shared memory segment(PostgreSQL)或 resource_semaphore 等待(SQL Server)。

  • 别用 GROUP BY LEFT(long_text, 100):既不减内存,又让索引失效
  • 避免 GROUP BY JSON_EXTRACT(data, '$.body') 这类原值提取:JSON 字段本身体积大,且无法走函数索引(除非显式建函数索引)
  • MySQL 8.0+ / PostgreSQL 12+ 才支持函数索引,SUBSTRING_INDEX(url, '/', 3) 这类表达式必须配合函数索引才有效

用确定性哈希或前缀替代原始大字段

真正可落地的解法,是让分组键变小、变稳定,而不是调大 tmp_table_sizework_mem —— 后者只是延缓崩溃,不是解决。

对文本字段,改用 GROUP BY SHA2(big_text, 256)(固定 64 字节)或 MD5(big_text)(固定 32 字节),冲突概率在业务可控范围内;对 JSON 字段,先提取关键路径再哈希,例如 GROUP BY SHA2(JSON_EXTRACT(data, '$.user_id'), 256)

  • SHA2MD5 更安全,但计算开销略高;若只做去重/分组,MD5 足够
  • 哈希值需加索引:MySQL 中建函数索引 CREATE INDEX idx_hash ON t (SHA2(text_col, 256));PostgreSQL 中用 CREATE INDEX idx_hash ON t ((md5(text_col)))
  • 别在哈希字段上 ORDER BY 原始值——哈希后顺序已丢失,如需排序,得额外关联原表

SELECT * 和 WHERE 条件错位放大内存压力

你以为只 GROUP BY 大字段有问题?其实 SELECT * 和不当的 WHERE 条件会让哈希表体积翻倍甚至指数级增长。

左脉梦幻师
左脉梦幻师

一款基于AI大模型的创意内容生成工具

下载

SELECT * 导致回表 + 全行加载进临时表;WHERE 条件写在聚合之后(如 HAVING)、或用了非 SARGable 表达式(如 WHERE YEAR(create_time) = 2025),都会迫使数据库先扫描全量数据再过滤。

  • 永远只 SELECT 真正需要的字段,尤其是避免带大字段(TEXTJSON
  • WHERE 条件必须放在 GROUP BY 之前,且尽可能用索引列的裸值比较,例如 WHERE create_time >= '2025-01-01',而非 WHERE DATE(create_time) = '2025-01-01'
  • 复合索引顺序很重要:WHERE status = 1 GROUP BY user_id 应建 (status, user_id),而不是反过来

work_mem / tmp_table_size 不是万能解药

调大 work_mem(PostgreSQL)或 tmp_table_size(MySQL)只能缓解,不能根治。本质问题在于哈希表内存占用 = 分组数 ×(分组键体积 + 聚合值体积)。

MySQL 中 tmp_table_sizemax_heap_table_size 取较小值生效;PostgreSQL 的 work_mem 是单查询上限,高并发下多个查询争抢,反而触发整体 OOM。

  • PostgreSQL:单次查询设 work_mem = '512MB' 可能没问题,但 20 个并发就吃掉 10GB,远超物理内存
  • MySQL:设 tmp_table_size = 1G,但 max_heap_table_size = 64M,实际仍按 64MB 限制
  • 即使 fallback 到磁盘(如 PostgreSQL 的 SORTAGG),排序阶段仍需加载全部分组键进内存,不是“自动落盘”

最易被忽略的点:哈希值虽小,但一旦涉及 HAVINGORDER BY 原始字段,就得回查原表——这时候大字段又回来了。所以哈希分组必须和业务逻辑对齐,不能只图 SQL 看着短。

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
数据分析工具有哪些
数据分析工具有哪些

数据分析工具有Excel、SQL、Python、R、Tableau、Power BI、SAS、SPSS和MATLAB等。详细介绍:1、Excel,具有强大的计算和数据处理功能;2、SQL,可以进行数据查询、过滤、排序、聚合等操作;3、Python,拥有丰富的数据分析库;4、R,拥有丰富的统计分析库和图形库;5、Tableau,提供了直观易用的用户界面等等。

2023.10.12

2425

8

SQL中distinct的用法
SQL中distinct的用法

SQL中distinct的语法是“SELECT DISTINCT column1, column2,...,FROM table_name;”。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2023.10.27

447

4

SQL中months_between使用方法
SQL中months_between使用方法

在SQL中,MONTHS_BETWEEN 是一个常见的函数,用于计算两个日期之间的月份差。想了解更多SQL的相关内容,可以阅读本专题下面的文章。

2024.02.23

612

5

SQL出现5120错误解决方法
SQL出现5120错误解决方法

SQL Server错误5120是由于没有足够的权限来访问或操作指定的数据库或文件引起的。想了解更多sql错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

3923

10

sql procedure语法错误解决方法
sql procedure语法错误解决方法

sql procedure语法错误解决办法:1、仔细检查错误消息;2、检查语法规则;3、检查括号和引号;4、检查变量和参数;5、检查关键字和函数;6、逐步调试;7、参考文档和示例。想了解更多语法错误的相关内容,可以阅读本专题下面的文章。

2024.03.06

1302

4

oracle数据库运行sql方法
oracle数据库运行sql方法

运行sql步骤包括:打开sql plus工具并连接到数据库。在提示符下输入sql语句。按enter键运行该语句。查看结果,错误消息或退出sql plus。想了解更多oracle数据库的相关内容,可以阅读本专题下面的文章。

2024.04.07

3539

11

sql中where的含义
sql中where的含义

sql中where子句用于从表中过滤数据,它基于指定条件选择特定的行。想了解更多where的相关内容,可以阅读本专题下面的文章。

2024.04.29

3416

6

sql中删除表的语句是什么
sql中删除表的语句是什么

sql中用于删除表的语句是drop table。语法为drop table table_name;该语句将永久删除指定表的表和数据。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

639

5

sql中删除一列的命令是什么
sql中删除一列的命令是什么

在sql中,使用alter table语句可以删除一列,语法为:alter table table_name drop column column_name。想了解更多sql的相关内容,可以阅读本专题下面的文章。

2024.04.29

525

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
热门推荐
/
最新课程
phpStudy极速入门视频教程
phpStudy极速入门视频教程

共6课时 | 54.4万人学习

独孤九贱(4)_PHP视频教程
独孤九贱(4)_PHP视频教程

共89课时 | 131.8万人学习