mysql rss内存持续上涨并非真正内存泄漏,而是由短连接滥用、查询缓存碎片或分配器行为导致;应优先检查连接管理、禁用query_cache、调优wait_timeout等参数。

别急着调大 innodb_buffer_pool_size,它很可能是问题的放大器,而不是解药。
先看 mysqld 进程实际占了多少内存(RSS)
MySQL 报 Out of memory,不代表它“该用的内存没配够”,而更可能是“它已经吃掉太多,系统扛不住了”。innodb_buffer_pool_size 只是配置项之一,每个连接还会独占 sort_buffer_size、join_buffer_size、read_buffer_size 等线程级内存;临时表、JSON 字段排序、performance_schema 开销也都算在进程 RSS 里。
执行这条命令看真实水位:
ps -o pid,rss,vsz,comm -C mysqld
RSS 超过物理内存的 80%,或者比你所有显式内存参数加起来(innodb_buffer_pool_size + key_buffer_size + sort_buffer_size × max_connections + …)高出一倍以上,就说明有隐性泄漏或失控增长。
- 如果 RSS 持续爬升且不回落,重点查连接数是否堆积(
information_schema.PROCESSLIST中State长期为Creating tmp table或Copying to tmp table) - 容器环境里 RSS 超限还可能触发 cgroup OOM,
dmesg -t | grep -i "killed process"能确认是不是被内核杀的
调小 innodb_buffer_pool_size 是最快速有效的止损动作
在非专用数据库服务器(比如宝塔面板共存 PHP、Nginx、监控脚本)、容器部署、或混部机器上,把 innodb_buffer_pool_size 设成物理内存的 70% 是高危操作。Linux 的 vm.swappiness 会让内核优先换出匿名页,而 InnoDB 缓冲池正是 mmap 分配的匿名内存——它既难被换出,又占大头,最终逼 OOM Killer 杀 mysqld。
实操建议:
- 2GB 内存服务器:设为
512M~1024M,别碰1536M - 4GB 内存服务器:上限设
2048M,同时必须同步压低max_connections(建议 ≤128) - 配置文件里删掉或注释掉
innodb_buffer_pool_instances(默认值即可),多实例在小内存下反而增加管理开销
改完记得重启 MySQL,并用 ps 对比 RSS 是否明显回落。
配套必须调整的三个关键参数
只动 innodb_buffer_pool_size 不够,其他线程级缓冲区会在高并发下瞬间反扑。
-
tmp_table_size和max_heap_table_size必须设为相同值(如64M),否则内存临时表会悄无声息地突破前者限制 -
sort_buffer_size别设 > 2M,MySQL 8.0.20+ 对 JSON 字段排序时会把它整个载入 buffer,哪怕表只有几行也容易爆 -
max_connections不要留默认的 151 或 1000,按真实业务峰值 × 1.5 设置,闲置连接会白占线程缓冲区
这些参数都属于“每连接一份”,max_connections = 1000 且 sort_buffer_size = 4M 就意味着光这一项理论峰值就吃掉 4GB——这还没算 buffer pool。
客户端报 Out of memory 是另一回事
如果你在用 mysql 命令行、Java 应用或 Python 脚本连 MySQL 时看到类似 mysql: Out of memory at line 42, 'malloc.c' 或 java.lang.OutOfMemoryError: Java heap space,那问题不在 mysqld,而在客户端进程本身。
常见场景和应对:
- 命令行客户端:加
--quick参数,让 client 用mysql_use_result()流式读取,避免一次性加载全部结果集 - JDBC 连接:必须设
statement.setFetchSize(Integer.MIN_VALUE)或调用enableStreamingResults(),否则驱动默认把整张结果集拉进 JVM 堆 - PHP PDO:用
PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => false关闭缓冲,配合fetch()循环处理
这类问题跟 innodb_buffer_pool_size 完全无关,调它只会让服务端更卡,客户端照样崩。
真正麻烦的从来不是参数数字本身,而是多个线程级缓冲区叠加后产生的“不可见内存雪球”——它不会出现在 SHOW VARIABLES 总和里,却实实在在压垮 RSS。每次调参前,先跑一遍 ps 和 dmesg,比翻文档管用得多。











