MySQL内存占用高且不释放主因是innodb_buffer_pool_size超可用内存且未预留OS余量,需以free -h的available值为准下调至available−2GB以内,并同步调优tmp_table_size/max_heap_table_size、max_connections及per-connection参数。

MySQL安装后物理内存占用高且不释放,大概率不是“泄露”,而是参数配置脱离实际负载,尤其是innodb_buffer_pool_size被设得过大或未预留系统余量。
检查innodb_buffer_pool_size是否远超可用内存
这个参数是MySQL启动时就向OS预分配的固定内存块,不会随负载收缩。哪怕你只查一条记录,它也占着——Linux的OOM Killer就爱杀这种“虚胖”进程。
- 执行
SHOW VARIABLES LIKE 'innodb_buffer_pool_size';,再对比free -h输出里的available列(不是free) - 若服务器总内存64GB、
available常驻仅8GB,却配了48GB buffer pool,必须下调;保守建议:不超过available - 2GB - MySQL 8.0+支持在线调整,但新值需满足:
innodb_buffer_pool_size % (innodb_buffer_pool_chunk_size × innodb_buffer_pool_instances) == 0,否则会被向下取整(执行后务必再查一遍确认生效值)
排查tmp_table_size和max_heap_table_size不一致导致的隐性开销
这两个值必须完全相等。若tmp_table_size=16M而max_heap_table_size=64M,MySQL会按更小的那个(16M)判定内存临时表上限,一超就落盘——磁盘临时表本身不耗内存,但后续I/O堵塞会让连接堆积,线程数飙升,间接推高sort_buffer_size等per-connection内存总量。
- 执行
SHOW VARIABLES LIKE 'tmp_table_size';和SHOW VARIABLES LIKE 'max_heap_table_size';核对 - 统一设为合理值,如
SET GLOBAL tmp_table_size = 67108864;+SET GLOBAL max_heap_table_size = 67108864;(64MB) - 同时检查
Created_tmp_disk_tables状态变量:若该值持续增长,说明SQL没走索引、GROUP BY/ORDER BY触发了大量磁盘临时表
盯住连接数与per-connection缓冲区的实际占用
每个活跃连接都会独占sort_buffer_size、join_buffer_size、thread_stack等内存。设sort_buffer_size=4M、max_connections=1000,光排序缓冲就可能吃掉4GB——哪怕99%连接处于Sleep状态。
- 执行
SHOW STATUS LIKE 'Threads_connected';,对比max_connections;若长期>70%且SHOW PROCESSLIST里大量Sleep且Time > 300,说明应用层连接没复用或泄漏 - 降低会话级缓冲:例如
SET SESSION sort_buffer_size = 262144;(256KB),比默认2MB更安全 - 缩短空闲超时:
SET GLOBAL wait_timeout = 60;,避免连接挂着不走
别忽略performance_schema和碎片化内存的隐性成本
MySQL 5.7+默认开启performance_schema,若table_definition_cache或performance_schema_max_table_instances设得过高(比如VPS上还按服务器规格配400),它自己就能吃掉几百MB;而glibc内存分配器在频繁申请/释放小块内存后易产生碎片,表现为RES持续上涨但sys.memory_global_by_current_bytes统计不上来。
- 对小内存机器(≤2GB),可考虑禁用:
performance_schema = OFF(需重启),或调低performance_schema_max_table_instances=200 - 若确认是glibc碎片(
/proc/PID/status中VmRSS远高于sys.memory_global_by_current_bytes总和),MySQL 8.0.28+支持SET GLOBAL performance_schema_memory_dump = ON;辅助诊断,但无法强制回收——此时更应聚焦于减少短生命周期连接和临时表
真正难处理的从来不是某个参数调大调小,而是innodb_buffer_pool_size和max_connections之间那种相互掩盖的关系:buffer pool看着合理,但连接数暴增后per-connection内存把余量全吞了;反过来,连接数压下去了,又发现buffer pool其实根本用不完——得进performance_schema.memory_summary_global_by_event_name里看实时分布,而不是只盯着top的RES。


















