mysql 8.0 默认使用glibc malloc,高并发下易现内存碎片与线程争用;切换jemalloc可稳控内存、降oom风险、防缓冲池“虚胖”,但须通过lsof验证加载、规避ld_preload失效陷阱,并经48小时真实流量观察方可确认生效。

直接上结论:MySQL 8.0 默认用 glibc 的 malloc,高并发下容易产生内存碎片、线程争用严重;换成 jemalloc 后,内存占用更稳、OOM 风险下降、InnoDB 缓冲池长期运行不“虚胖”——但不是装上就生效,必须确认加载路径、规避动态库冲突、且不能跳过验证步骤。
怎么确认 MySQL 当前用的是哪个内存分配器
别猜,用 lsof 看进程实际加载的动态库:
lsof -p $(pgrep mysqld) | grep -E 'lib(c|jemalloc)' | head -5
如果只看到 libc-*.so,没 libjemalloc.so,说明没生效;如果看到两者共存,大概率是环境变量或启动方式冲突,后续会出问题。
常见误判点:
- 以为改了
LD_PRELOAD就一定成功——其实 mysqld 启动脚本(如 systemd service)可能用PrivateTmp=yes或ProtectSystem=full隔离了环境变量 - 在容器里跑 MySQL,却在宿主机装了 jemalloc——容器内没这个 so 文件,
LD_PRELOAD指向无效路径 - 装了 jemalloc 但没运行
ldconfig,系统找不到libjemalloc.so,LD_PRELOAD失败静默降级回glibc
三种能真正生效的加载方式(按推荐顺序)
核心原则:让 mysqld 进程在 main() 执行前 就绑定到 libjemalloc.so,且不被覆盖。
-
方式一(最稳):用
LD_PRELOAD启动 mysqld
编辑 MySQL 启动脚本或 systemd service 文件,在ExecStart前加环境变量:Environment="LD_PRELOAD=/usr/local/lib/libjemalloc.so.2"
注意路径要和find /usr -name "libjemalloc.so*"结果一致;.so.2是常见后缀,不同版本可能为.so.1或无后缀 -
方式二:编译安装时指定
仅适用于源码编译 MySQL:cmake -DWITH_MALLOC=jemalloc ...
这会让 jemalloc 成为内置依赖,无需运行时加载,但重编译成本高,升级麻烦 -
方式三:替换 mysqld 启动包装器(慎用)
创建 wrapper 脚本:#!/bin/bash<br>export LD_PRELOAD="/usr/local/lib/libjemalloc.so.2"<br>exec /usr/local/mysql/bin/mysqld "$@"
然后把 service 的ExecStart指向该脚本;缺点是监控/日志路径可能错位,调试困难
为什么配了 jemalloc 还有内存碎片?几个关键漏点
jemalloc 本身不自动“清理历史碎片”,它只是让后续分配更高效。以下情况仍会导致表象上的内存增长:
-
innodb_buffer_pool_size设得过大,且未满足 chunk 对齐 → MySQL 启动时自动向下取整,但已 mmap 的内存不会释放,OS 层仍计入 RSS;查cat /proc/$(pgrep mysqld)/status | grep VmRSS和SHOW VARIABLES LIKE 'innodb_buffer_pool_size'是否接近 - 大量短连接 + 长事务未提交 →
jemalloc的 tcache 不会立即归还内存给 arena,需等线程退出或触发mallctl("thread.tcache.flush", ...);可通过SET GLOBAL innodb_max_purge_lag = 10000加速 purge,间接减少活跃事务内存占用 - 未启用 jemalloc 统计功能,无法判断是否真有泄漏:
启动时加export MALLOC_CONF="stats_print:true,lg_chunk:21"(打印启动统计),或运行中执行:echo stats | sudo socat - /tmp/jemalloc_stats.sock(需提前配置stats_interval和 socket 路径)
上线前必须做的三件事
跳过任意一步,都可能在高峰期突然 OOM 或性能倒退:
- 用
mysqlslap或真实业务流量压测 30 分钟,观察top中 mysqld 的%MEM是否平缓上升,对比切换前后Innodb_buffer_pool_reads每秒值是否下降 - 检查错误日志有没有
jemalloc: Error in munmap或arena_reset相关 warn —— 多见于 ARM64 系统未打补丁或 jemalloc 版本太旧(建议用 5.3.0+) - 确认
jemalloc不和TokuDB(已废弃)、MyRocks等第三方引擎冲突;若用了它们,必须统一内存分配器,否则 arena 管理混乱
最易被忽略的一点:jemalloc 的优势在“长周期运行+高并发连接”场景才明显,单次压测 5 分钟看不出差别;上线后至少盯 48 小时 RSS 趋势,看凌晨低峰期内存能否回落。











