centos下使用jemalloc解决Mysql内存泄漏问题

文章讲述了jemalloc在MySQL优化中的使用,比较了jemalloc、tcmalloc和ptmalloc的性能,发现jemalloc在ARM64架构上由于默认hugepages大小导致内存使用激增。通过修改hugepages设置和内核配置,解决了内存占用问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考: MySQL bug:https://bugs.mysql.com/bug.php?id=83047&tdsourcetag=s_pcqq_aiomsg 

    https://github.com/jemalloc/jemalloc/blob/dev/INSTALL.md

(1)ptmalloc 是glibc的内存分配管理

(2)tcmalloc 是google的内存分配管理模块

(3)jemalloc 是BSD的提供的内存分配管理 (可以使用jemalloc优化Nginx)

三者jemalloc和tcmalloc的性能不分伯仲,而ptmalloc则要低一些

1、下载最新版jemalloc

git clone https://github.com/jemalloc/jemalloc

cd jemalloc

[root@bogon jemalloc]# cat VERSION
5.1.0-108-gc4063ce439523d382f2dfbbc5bf6da657e6badb0

2、安装步骤:

./autogen.sh

./configure 

make 

make install

[root@bogon jemalloc]# make install

/usr/bin/install -c -d /usr/bin
/usr/bin/install -c -m 755 bin/jemalloc-config /usr/bin
/usr/bin/install -c -m 755 bin/jemalloc.sh /usr/bin
/usr/bin/install -c -m 755 bin/jeprof /usr/bin
/usr/bin/install -c -d /usr/include/jemalloc
/usr/bin/install -c -m 644 include/jemalloc/jemalloc.h /usr/include/jemalloc
/usr/bin/install -c -d /usr/lib
/usr/bin/install -c -m 755 lib/libjemalloc.so.2 /usr/lib
ln -sf libjemalloc.so.2 /usr/lib/libjemalloc.so
/usr/bin/install -c -d /usr/lib
/usr/bin/install -c -m 755 lib/libjemalloc.a /usr/lib
/usr/bin/install -c -m 755 lib/libjemalloc_pic.a /usr/lib
/usr/bin/install -c -d /usr/lib/pkgconfig
/usr/bin/install -c -m 644 jemalloc.pc /usr/lib/pkgconfig
/usr/bin/install -c -d /usr/share/doc/jemalloc
/usr/bin/install -c -m 644 doc/jemalloc.html /usr/share/doc/jemalloc
/usr/bin/install: cannot stat ‘doc/jemalloc.html’: No such file or directory
make: *** [install_doc_html] Error 1
[root@bogon jemalloc]#

默认安装目录:

PREFIX             : /usr/local
BINDIR             : /usr/local/bin
DATADIR            : /usr/local/share
INCLUDEDIR         : /usr/local/include
LIBDIR             : /usr/local/lib
MANDIR             : /usr/local/share/man

3、配置mysqld使用jemalloc,需要将配置写到[mysqld_safe] section

[mysqld_safe]
malloc-lib=/usr/local/lib/libjemalloc.so

4. 如果直接使用sqld启动,

export LD_PRELOAD=/usr/local/lib/libjemalloc.so

然后启动sqld

5、查看jemalloc是否生效

lsof -n |grep jemalloc

[root@localhost mysql3306]# lsof -n |grep jemalloc
bash      11223                 root  cwd       DIR              253,0      4096      35178 /software/jemalloc
mysqld    17475                mysql  mem       REG              253,0   4291512   18480172 /usr/lib/libjemalloc.so.2
mysqld    17475 17482          mysql  mem       REG              253,0   4291512   18480172 /usr/lib/libjemalloc.so.2
mysqld    17475 17483          mysql  mem       REG              253,0   4291512   18480172 /usr/lib/libjemalloc.so.2
mysqld    17475 17484          mysql  mem       REG              253,0   4291512   18480172 /usr/lib/libjemalloc.so.2

显示以上信息说明mysql已经成功加载jemalloc

或者

使用jemalloc(或tcmalloc)优化MYSQL(安装步骤)

malloc

  1. wget 
    tar xjf jemalloc-3.4.0.tar.bz2
    cd jemalloc-3.4.0
    ./configure --prefix=/usr/local/jemalloc --libdir=/usr/local/lib make && make install
    echo '/usr/local/lib' > /etc/ld.so.conf.d/local.conf
    ldconfig
    [root@host-192-168-1-56 mysql]# cp /usr/local/lib/libjemalloc.so /usr/lib64/mysql/libjemalloc.so

mysql使用jemalloc

  1. 修改配置文件
  2. [mysqld_safe]
    malloc-lib=/usr/lib64/mysql/libjemalloc.so             ###指定libjemalloc.so 即可

重启mysql检查是否生效

  1. [root@host-192-168-1-56 mysql]# lsof -n | grep jemalloc
    mysqld     6032     mysql  mem       REG              252,1   4781206     320288 /usr/lib64/mysql/libjemalloc.so

MySQL使用Jemalloc

鉴于jemalloc的诸多优点,计划使用jemalloc作为内存管理器来优化MySQL,下面是测试环境。

测试环境

CPU: ARM64
Memory: 512GB
OS: CentOS Linux release 8.3.2011
Kernel: 4.18.0-193.28.1.el8_2.aarch64
MySQL: 8.0.25
Test Tool: SysBench 1.0.20
Jemalloc: 5.2.1

jemalloc的安装和使用

# wget https://github.com/jemalloc/jemalloc/archive/refs/tags/5.2.1.tar.gz -O jemalloc-5.2.1.tar.gz
# tar xzvf jemalloc-5.2.1.tar.gz
# cd jemalloc-5.2.1
# ./autogen.sh
//安装到指定目录
# ./configure --prefix=/home/test-user/jemalloc-5.2.1-install
//编译并安装
# make; make install
//配置环境变量
# export LD_PRELOAD=/home/test-user/jemalloc-5.2.1-install/lib/libjemalloc.so

安装好MySQL后,通过如下命令检查jemalloc是否被正常使用(MySQL的安装请参考官方步骤,这里不再赘述)

# lsof -n |grep jemalloc

下图显示MySQL已经正常使用jemalloc

title=

测试用例

sysbench启动80个线程对MySQL进行读写压测。

异常问题

压测过程中发现内存使用“异常”:MySQL进程占用的物理内存超过了100GB。
 

title=


不使用jemalloc切换回默认的glibc后,内存占用降低到了7GB,和以往的测试结果一致。
 

title=


从测试结果看,使用jemalloc作为内存管理器时内存使用量激增,需要进一步分析原因。内存使用量是否合理?是否和架构相关?

回到顶部

问题分析

第一阶段分析

1. 复现“问题”

首先需要确定该“问题”是否在x86架构上也存在,是否和操作系统或内核版本相关。

为了快速验证以上疑问,在AWS上分别创建x86实例(m5)和arm64实例(m6g)进行测试, 并没有复现“问题”。这两个实例默认的操作系统是Amazon Linux 2,而本地测试时使用的是CentOS8,在m6g上安装CentOS8重新测试,“问题”复现。测试结果如下:

title=

2. 对比分析

对比测试环境,分析它们不同点,我们发现该“问题”只有在内核page size是64KB时才会出现。

另外,根据前文介绍,jemalloc中extent会基于page size分配内存。而且,深入分析jemalloc代码后还发现有多个数据结构的内存分配都涉及到page size,比如size_class, bin, extents, arena等等。

页表在操作系统中作为最基础的内存分配结构,ARM64支持4K、16K、64K不同大小的页表,x86只支持4KB。而本地测试使用的ARM64 CentOS的默认page size就是64KB,所以初步判断该“问题”和page size的配置相关。

3. 解决方法

即然ARM64架构支持多种page size,而page size为4KB时没有出现问题,那么可以修改ARM64 CentOS8的内核默认的page size来解决该"问题"。

修改page size方法

由于内核当前页表大小只支持静态配置,不支持动态修改,所以需要重新编译内核。
修改方法如下:

  • 在 https://www.kernel.org/ 获取需要的内核版本
  • 解压并修改内核配置参数

    # tar xf linux-x.x.x.tar.xz
    # cd linux-x.x.x
    # cp /boot/config-xxx .config
    # make menuconfig

    在图形菜单中找到“Kernel Features-> Page size”,选择4KB并保存配置

    title=

  • 编译并安装新的内核

    # make -j
    # make modules_install
    # make install
  • 重启进入新的内核,参看page size是否修改成功

    # getconf PAGE_SIZE
    4096
4. 验证

修改page size为4KB后重新测试,jemalloc内存使用量和glibc接近。测试结果如下:

title=

5. 潜在问题

至此该“问题”似乎可以通过修改page size来解决。但是,如果用户仍然需要使用64KB的页表,该方法将不再适用。

实际上,jemalloc本身支持编译参数“--with-lg-page=16”,该参数可以使jemalloc在page size为4KB时复用多个页面来达到使用64KB页面的效果。

尝试在4KB page size的系统下加入该编译参数,并没有出现内存使用量激增的现象。

这说明除了page size,还有其他因素影响了jemalloc的内存分配,仍然需要进一步分析。

第二阶段分析

1. micro-benchmark

通过以上测试发现该“问题”和MySQL并没有直接关系。为了简化分析和复现过程,单独开发了一个micro-benchmark https://github.com/machuang1983/jemalloc_micro_benchmark

该程序用于建立多个线程,每个线程分配一定内存,程序运行过程中实时打印进程的内存使用情况。

通过micro-benchmark可以快速复现问题。测试结果显示,每新建一个线程就会消耗1GB左右的内存。测试结果如下:

image.png

再次简化测试,直接运行单线程程序,如sleep 100,进程就会占用1GB内存。
 

title=


由此看见,jemalloc针对一个线程进行内存初始化分配时就会分配1GB内存。需要深入分析jemalloc具体的分配机制。

2. 深入分析jemalloc代码

按前文所述,jemalloc的内存分配涉及到多个数据结构,我们结合gdb单步执行来分析jemalloc代码,同时实时查看内存占用的变化,由此定位到关键代码。

调试过程中发现,base会基于默认的hugepage size分配内存,分配之后监控到内存使用量突然增大,具体代码在 https://github.com/jemalloc/jemalloc/blob/dev/src/base.c#L46-L49
 

title=


继续搜索hugepage size相关代码,还发现另一处使用它来分配内存,代码在https://github.com/jemalloc/jemalloc/blob/master/src/arena.c#L2052

title=

由此可见除了page size,hugepage size对jemalloc的内存分配也有影响。通常hugepage size比page size大得多,所以hugepage size的影响会更大。

3. hugepage

内存管理采用"分页机制",但是当运行内存需求量较大时,默认page大小的页面会导致较多的TLB miss和缺页中断,从而大大影响应用程序性能。所以,有些场景希望可以使用更大的内存页作为映射单位,因此引入了hugepage。

不同架构支持的hugepage size不同,见下表:

title=

4. 解决方法

ARM64 CentOS在page size=64KB时,默认hugepage size是512MB,jemalloc的base会以512MB来分配内存,而当page size=4KB时,默认hugepage size是2MB。所以回顾前面的测试,修改page size后问题消失的主要原因是默认的hugepage size改变导致的。

默认hugepage size修改方法
  1. 修改启动参数“default_hugepagesz=2M”
    ARM64支持多种hugepage size,可以使用hugepagesz启动参数进行调整,无需重新编译内核。

    • 永久修改

      • Centos: Set default_hugepagesz=2M in /boot/grub2/grubenv file
      • Ubuntu: Set default_hugepagesz=2M to GRUB_CMDLINE_LINUX in /etc/default/grub file, then run “update-grub”
    • 临时修改

      • 内核启动时,输入"e"进入修改启动选项界面,加入参数“default_hugepagesz=2M”,然后输入"ctrl+x"启动内核。
  2. jemalloc编译参数"--with-lg-hugepage=21"
    jemalloc支持编译参数"--with-lg-hugepage=21",替代系统的默认的hugepage size为2MB。建议使用该方法。
5. 验证

修改默认hugepage size后测试结果(sysbench使用256线程压测)如下:
 

title=


测试结果显示,将hugepage size改为2MB以后,jemalloc的内存使用情况和glibc接近。

回到顶部

总结

该"问题"和架构无关,jemalloc作为内存管理器,如果默认hugepage size较大,会导致软件占用较大的内存,jemalloc提供了编译参数"--with-lg-hugepage=21"来降低这个影响。

由于ARM64支持更多类型的page size和hugepage size,用以提升软件的性能。所以用户在ARM64系统上使用jemalloc时,需要关注默认的page size和hugepage size,并根据具体需求做出相应的调整。

<think>好的,用户现在的情况是不能在生产环境中重启数据库,但需要监控MySQL的内存分配。之前已经给过使用Valgrind和jemalloc的方法,但现在用户明确不能重启,可能需要调整方案。 首先,用户提到的Valgrind和jemalloc都需要在启动时加载,尤其是jemalloc需要修改启动配置并重启MySQL。而Valgrind运行需要停止现有服务,用Valgrind启动,显然不符合用户不能重启的需求。所以必须找替代方案。 接下来,我需要思考有哪些工具或方法可以在不重启MySQL的情况下监控内存。可能的选项包括: 1. **内置MySQL命令和状态变量**:比如SHOW ENGINE INNODB STATUS,SHOW GLOBAL STATUS,或者information_schema中的表来查看内存使用情况。这种方法不需要任何外部工具,但可能不够详细。 2. **系统级工具**:如pmap、ps、top等,可以查看进程的内存使用情况,但无法深入到具体的内存分配细节。 3. **动态追踪工具**:比如gdb(但不安全在生产环境使用),或者perf,eBPF工具如bpftrace,这些可以在不重启的情况下附加到进程,但需要一定的权限和专业知识,可能对性能有影响。 4. **第三方分析工具**:如pt-mysql-summary,或者通过Performance Schema来收集内存数据,但可能需要MySQL的特定版本支持。 然后需要评估这些方法的可行性。用户需要的是监控内存分配,可能关注泄漏或异常分配。在不重启的情况下,Performance Schema可能是一个选项,特别是如果MySQL版本在5.7及以上,因为Performance Schema中有内存监控的表。 接下来,需要详细检查Performance Schema的相关配置。例如,是否启用了内存监控,相关instruments和consumers是否开启。可能需要指导用户如何启用这些功能,但注意启用后可能会有性能开销,需要权衡。 另外,如果用户之前没有启用Performance Schema的内存监控,可能需要动态开启,这取决于MySQL版本是否支持动态设置。例如,MySQL 5.7及以上允许部分参数的动态调整,而无需重启。 还有,像sys schema中的视图可能已经聚合了一些内存使用信息,可以简化查询。 同时,系统工具如pmap可以定期检查进程的内存映射,查看是否有异常增长。虽然不如Valgrind详细,但可以在不干扰服务的情况下进行。 另外,jemalloc如果已经作为默认内存分配器加载,可能通过mallctl等工具来获取统计信息,但需要确认jemalloc是否已经被使用,或者是否可以在运行时加载,但通常LD_PRELOAD需要在启动时指定,所以用户如果之前没有配置jemalloc,可能无法在不重启的情况下使用。 综上,可能的解决方案包括: 1. 使用MySQL的Performance Schema和sys schema来监控内存使用情况。 2. 利用系统工具如pmap、smem等定期检查内存变化。 3. 检查现有的jemalloc是否已经启用,并尝试通过mallctl获取统计信息(如果可行)。 4. 考虑使用动态追踪工具如bpftrace进行实时监控,但需要确保生产环境允许,且具备相关技术能力。 需要逐步引导用户通过这些方法,并注意可能的影响和限制。此外,需要提醒用户如果确实需要更详细的内存分配分析,可能还是需要在测试环境中复现问题使用Valgrind等工具,因为生产环境不重启的限制会导致某些工具不可用。</think>--- ### 若无法重启数据库,可通过以下方法监控MySQL内存分配(无需重启): --- ### 一、使用 **MySQL内置监控工具** #### 1. **Performance Schema 内存统计(需MySQL 5.7+)** - **步骤1:确认是否启用内存监控** ```sql -- 查看内存监控是否激活 SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'memory/%'; ``` 若`ENABLED`列为`NO`,则需动态开启(无需重启): ```sql -- 启用所有内存监控点 UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'memory/%'; -- 启用内存事件消费者 UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'memory_summary_global_by_event_name'; ``` - **步骤2:查询内存分配详情** ```sql -- 按模块统计内存使用 SELECT EVENT_NAME, SUM_NUMBER_OF_BYTES_ALLOCATED FROM performance_schema.memory_summary_global_by_event_name ORDER BY SUM_NUMBER_OF_BYTES_ALLOCATED DESC LIMIT 10; ``` #### 2. **`sys` Schema 简化查询** 若已安装`sys` Schema(MySQL官方工具库): ```sql -- 查看内存分配Top10模块 SELECT * FROM sys.memory_global_by_current_bytes WHERE event_name LIKE 'memory/%' LIMIT 10; ``` --- ### 二、操作系统级监控(无需MySQL配合) #### 1. **`pmap` 查看进程内存映射** ```bash # 实时查看MySQL进程内存分布(替换实际PID) pmap -x $(pidof mysqld) | less # 定期记录内存变化(每10秒) while true; do pmap -x $(pidof mysqld) > /tmp/mysql_mem_$(date +%s).log sleep 10 done ``` #### 2. **`smem` 分析内存占用** ```bash # 安装smem sudo apt-get install smem # Ubuntu/Debian sudo yum install smem # CentOS/RHEL # 按进程树统计内存 smem -t -k -P mysqld ``` #### 3. **`gdb` 临时采样(谨慎使用)** ```bash # 生成内存分配快照(可能短暂阻塞进程) gdb -p $(pidof mysqld) -ex "malloc_info 0" -ex "detach" -ex "quit" > /tmp/mysql_malloc.xml ``` --- ### 三、低侵入式动态追踪(需root权限) #### 1. **`bpftrace` 实时监控内存分配** ```bash # 安装bpftrace sudo apt-get install bpftrace # Ubuntu/Debian sudo yum install bpftrace # CentOS/RHEL # 追踪malloc调用(按大小统计) sudo bpftrace -e 'uprobe:/usr/lib/x86_64-linux-gnu/libc.so.6:malloc { @bytes = hist(arg0); }' ``` #### 2. **`stap`(SystemTap)跟踪内存行为** ```bash # 统计malloc调用次数(需调试符号) sudo stap -e 'probe process("/usr/sbin/mysqld").function("malloc") { @counts[ubacktrace()] <<< 1; }' ``` --- ### 四、替代方案:间接诊断内存问题 #### 1. **监控Swap使用** ```bash # 观察Swap活动(频繁写入可能表明内存不足) vmstat 1 5 | awk '{print $8,$9}' ``` #### 2. **InnoDB缓冲池状态** ```sql -- 检查缓冲池碎片率 SHOW ENGINE INNODB STATUS\G -- 查看"BUFFER POOL AND MEMORY"部分 ``` --- ### 注意事项 1. **性能影响** - `bpftrace`/`stap`可能增加5%~10%的CPU负载,建议短时间采样。 - 避免在生产高峰执行高负载操作。 2. **数据解读** - `pmap`中`[anon]`段增长可能预示内存泄漏。 - Performance Schema中`memory/sql/TABLE`过高可能需优化临时表。 3. **应急措施** 若发现内存持续增长,可尝试: ```sql -- 释放查询缓存(若启用) RESET QUERY CACHE; -- 终止异常会话 KILL [SESSION_ID]; ``` --- ### 总结方案 | 场景 | 工具 | 粒度 | 侵入性 | |----------------------|--------------------------|-------------|---------| | 快速检查内存分布 | `pmap` / `smem` | 进程级 | 低 | | 定位MySQL模块泄漏 | Performance Schema | 模块级 | 中 | | 实时追踪内存分配 | `bpftrace` | 函数调用级 | 中 | | 长期趋势分析 | 定期`pmap`日志 + 可视化 | 进程级 | 低 | --- 通过以上方法,可在不重启数据库的前提下实现内存监控。如需深入分析内存泄漏,仍需在测试环境中复现问题使用Valgrind。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值