
MySQL 内存占用高,先看 innodb_buffer_pool_size 是否过大
这是最常见也最容易被误配的内存项。InnoDB 缓冲池默认可能占物理内存的 75%(尤其在 Docker 或云主机上自动调优后),但若你的实例只跑几个小表,或并发查询极少,这个值就是浪费——它会常驻内存,且不轻易释放。
- 查当前值:
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';
- 合理范围:一般设为物理内存的 50%~70%,但必须结合实际数据量。比如总数据文件(
ibdata1+*.ibd)才 2GB,却配了 16GB 缓冲池,基本就是冗余 - 动态调整(8.0+ 支持在线 resize):
SET GLOBAL innodb_buffer_pool_size = 4294967296; -- 4GB
,但需确保innodb_buffer_pool_instances能整除新大小(如设为 4GB,建议instances≥ 4)
连接数暴增导致 thread_stack 和 sort_buffer_size 累积耗尽内存
每个连接都会分配独立的线程栈和临时排序/join缓冲区。当 max_connections 设得过高(如 1000+),而应用又没及时断开空闲连接,大量 sleeping 连接就会把内存悄悄吃光。
- 查活跃连接:
SHOW PROCESSLIST;
,重点关注Command为Sleep且Time> 300 的连接 - 限制单连接资源:
SET SESSION sort_buffer_size = 262144; -- 256KB,而非默认 2MB
,配合应用层使用连接池(如 HikariCP)控制最大活跃数 - 缩短空闲超时:
SET GLOBAL wait_timeout = 60; SET GLOBAL interactive_timeout = 60;
,避免连接“挂着不走”
tmp_table_size 和 max_heap_table_size 不匹配引发磁盘临时表误判
MySQL 用内存临时表加速 GROUP BY、ORDER BY 等操作,但这两个参数必须相等,否则即使内存充足,也会因 max_heap_table_size 更小而强制落盘——落盘本身不耗内存,但后续磁盘 I/O 堵塞会导致连接堆积,间接推高内存占用。
- 检查是否一致:
SHOW VARIABLES LIKE 'tmp_table_size';
SHOW VARIABLES LIKE 'max_heap_table_size'; - 统一设为合理值(如 64MB):
SET GLOBAL tmp_table_size = 67108864;
SET GLOBAL max_heap_table_size = 67108864; - 验证效果:执行大结果集排序后,查
Created_tmp_disk_tables是否明显下降(SHOW STATUS LIKE 'Created_tmp%';
)
排查隐藏内存大户:查询缓存(Query Cache)已废弃但残留配置
MySQL 8.0 已彻底移除 query cache,但如果你是从 5.7 升级上来,且配置文件里还留着 query_cache_type = 1 和 query_cache_size = 268435456,MySQL 会静默忽略这些参数——可问题在于,某些旧版监控脚本或 DBA 习惯性盯着 Qcache_ 状态变量,反而掩盖了真正的问题源。
- 确认是否启用(5.7 及以下):
SHOW VARIABLES LIKE 'query_cache_%';
,若query_cache_size> 0,且业务无大量重复只读查询,建议关掉:SET GLOBAL query_cache_type = 0;
- 8.0+ 用户请直接删掉配置文件中的所有
query_cache_*行,避免干扰理解 - 真正该盯的是
Innodb_buffer_pool_read_requests与Innodb_buffer_pool_reads比值,低于 99% 才说明缓冲池不够用,而不是盲目加内存
内存问题很少是单一参数惹的祸,往往是缓冲池配大了 + 连接池没管住 + 临时表阈值没对齐,三者叠加才让 top 里的 mysqld RSS 居高不下。动手前务必先
SHOW ENGINE INNODB STATUS\G看 buffer pool 和 transaction section 的实时水位,比看配置文件更准。










