mysql慢查询导致内存不释放的典型表现是执行耗时长、扫描行数多的select或update后,threads_connected不变但bytes_received和key_buffer_size持续偏高,mysqld rss内存居高不下,因中间结果、排序缓冲、临时表等资源未及时归还。

MySQL慢查询导致内存不释放的典型表现
执行完一个耗时长、扫描行数多的 SELECT 或 UPDATE 后,SHOW STATUS LIKE 'Threads_connected' 没变,但 SHOW STATUS LIKE 'Bytes_received' 和 Key_buffer_size 相关指标持续偏高,且 top 里 mysqld 进程 RSS 内存居高不下——这不是连接没断,而是查询中间结果、排序缓冲、临时表等资源没及时归还。
为什么 ORDER BY / GROUP BY 容易卡住内存
这类操作默认走内存临时表(tmp_table_size 和 max_heap_table_size 控制上限),一旦超出就落盘到 /tmp 或 tmpdir;但落盘后 MySQL 不会主动清空已分配的 sort buffer 或 join buffer,尤其在 long_query_time 较长、客户端未及时 fetch 全部结果时,缓冲区会长期挂起。
-
sort_buffer_size是 per-connection 的,每个慢查询都可能独占一份,积少成多 -
read_rnd_buffer_size在回表阶段也可能膨胀,尤其配合ORDER BY+LIMIT时 - 使用
SQL_BUFFER_RESULT强制物化结果集,反而会让内存释放更滞后
不重启也能缓解的实操手段
重点不是“清缓存”,而是让 MySQL 主动交还资源。以下操作可立即生效,无需重启:
- 对当前连接执行
KILL QUERY [thread_id](不是KILL),中断查询但保留连接,已分配的 sort/join buffer 通常会回收 - 调低会话级变量:
SET SESSION sort_buffer_size = 262144;(256K),下次查询就会用新值重新分配 - 检查并清理无用的临时表:
SHOW FULL PROCESSLIST;找出State: Copying to tmp table或Creating sort index的线程,针对性 KILL - 确认
tmpdir挂载点没满,否则磁盘临时表无法清理,也会拖慢内存回收
长期配置要注意的三个硬伤点
很多 DBA 调大 innodb_buffer_pool_size 却忽略其他缓冲区联动,结果是 buffer pool 没压满,sort buffer 却把物理内存吃光:
-
sort_buffer_size默认值(2M)对 OLAP 查询太激进,建议按业务最大并发查询数反推:比如最多 20 个并发慢查,就别设超过 512K -
join_buffer_size在非索引 JOIN 场景下同样 per-connection 分配,和sort_buffer_size一样要严控 -
performance_schema开启后本身也吃内存,如果只用来查慢日志,可考虑关掉无关 instruments:UPDATE performance_schema.setup_instruments SET ENABLED = 'NO' WHERE NAME LIKE 'memory%';
真正难处理的是嵌套子查询 + 多层 GROUP BY 的场景——MySQL 8.0 前无法复用中间结果,每层都独立申请 buffer。这种 SQL 改写比调参更有效。











