MySQL 8.0 RSS内存持续上涨并非真正内存泄漏,而是glibc malloc分配器碎片导致空闲页未归还OS;换用tcmalloc/jemalloc并调优wait_timeout、sort_buffer_size等参数可有效缓解。

MySQL 8.0 本身没有传统意义上的 C 堆内存泄漏,但 mysqld 进程的 RSS 内存持续上涨、不随负载下降而回落,确实是常见且棘手的问题——根源往往不在 MySQL 代码,而在底层内存分配器(如 glibc malloc)的行为。
为什么 glibc malloc 会让 MySQL 看起来像在泄漏内存
MySQL 启动后会频繁申请/释放小块内存(比如 per-connection buffers、解析树节点、临时表行等),glibc 的默认 malloc 在高频率分配/释放后容易产生内存碎片,且不会主动将空闲页归还给 OS。这导致 ps aux 看到的 RSS 持续增长,但 performance_schema.memory_summary_global_by_event_name 显示的已用内存却稳定甚至下降。
- 现象典型:Threads_connected 稳定,
Threads_created却每小时新增数百上千 - 验证方法:运行
cat /proc/$(pgrep mysqld)/smaps | awk '/^MMU:/ {sum+=$2} END {print sum}'(实际物理映射页)对比malloc_stats()输出(需 gdb 或启用 tcmalloc) - 关键区别:RSS 上涨 ≠ MySQL 内部未 free;而是 malloc 保留了虚拟地址空间和部分物理页
换用 tcmalloc 或 jemalloc 是最直接有效的缓解手段
这两个分配器比 glibc malloc 更激进地回收空闲内存页,尤其适合 MySQL 这类高并发、高频分配/释放的场景。实测中,切换后 RSS 增长曲线常从“只升不降”变为“锯齿状波动”,夜间低峰期明显回落。
- 启用 tcmalloc:启动 mysqld 前设置
LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4"(路径依系统而异) - 启用 jemalloc:同样用
LD_PRELOAD,推荐版本 >=5.2.1(避免早期版本的 thread cache bug) - 验证是否生效:连接 MySQL 后执行
SELECT @@version_compile_os;并检查 error log 是否含 “using jemalloc” 或类似提示 - 注意:不要同时加载多个 allocators;升级分配器库后务必重启 mysqld
配合分配器切换必须调优的连接与缓冲参数
即使换了分配器,若应用层仍滥用短连接或线程缓冲过大,仍会加剧内存压力。这些参数直接影响每次连接的内存开销:
-
wait_timeout和interactive_timeout建议设为 60–180 秒(非 28800 默认值),避免空闲连接长期驻留 -
sort_buffer_size、read_buffer_size、join_buffer_size不要全局设高(如 >2M),应依赖 session 级动态调整,或用max_sort_length控制排序阈值 - 禁用已废弃的
query_cache_type(MySQL 8.0 默认关闭,但配置文件中残留会导致启动警告甚至干扰) - 确认客户端连接池行为:PHP 的
mysqlnd.max_persistent=0、Java 的 HikariCPmaximumPoolSize与idleTimeout必须匹配业务节奏
别忽略 InnoDB 缓冲池外的“隐形大户”
很多人盯着 innodb_buffer_pool_size,却漏掉几个常被低估的内存消耗点:
-
tmp_table_size和max_heap_table_size共同限制内存临时表大小,设得过高(如 512M)会使单个复杂 JOIN 轻易吃掉百 MB;建议设为 64M–128M,并监控Created_tmp_disk_tables指标 - Performance Schema 自身开销:若启用了大量 instruments(如
memory/%),其内存占用可能达百 MB;按需关闭:UPDATE performance_schema.setup_instruments SET ENABLED = 'NO' WHERE NAME LIKE 'memory/%'; - 复制相关:如果启用了 Group Replication,
group_replication_message_cache_size默认 1G,且不随流量下降自动收缩,需根据集群规模调低
真正难处理的不是“找到哪个表占内存”,而是区分“RSS 上涨是 OS 层分配器滞留”还是“MySQL 内部某模块真没释放”。前者靠换 allocator + 参数收紧就能大幅改善;后者才需要深挖 memory_summary_by_thread_by_event_name 和堆栈分析。多数线上 case,属于前者。


















