MYSQL的内存管理方法

【MYSQL的默认内存管理方法---- GLIBC(ptmalloc) 内存分配器的简述】

    一般情况下, MySQL 使用 glibc 作为默认的内存分配器,而 Linux 下内存管理也是由默认的 glic 库来与内核交互,以下是一个进程虚拟内存结构图,其中有两块区域涉及到我们内存分配和释放,堆和内存映射区域,而 malloc() /mmap() 就是在该区域动态进行内存分配:

 

malloc() glibc 提供的内存分配函数,对应到系统调用上,有 brk() mmap() 两种实现方式。在使用 malloc 之前, brk 的值等于 start_brk ,也就是说 heap 大小为 0 ptmalloc 在开始时,若请求的空间小于 mmap 分配阈值( mmap threshold ,默认值为 128KB )时,主分配区会调用 sbrk() 增加一块大小为 (128 KB + chunk_size) align 4KB 的空间作为 heap 。非主分配区会调用 mmap 映射一块大小为 HEAP_MAX_SIZE 32 位系统上默认为 1MB 64 位系统上默认为 64MB )的空间作为 sub-heap 。这就是前面所说的 ptmalloc 所维护的分配空间,当用户请求内存分配时,首先会在这个区域内找一块合适的 chunk 给用户。当用户释放了 heap 中的 chunk 时, ptmalloc 又会使用 fast bins bins 来组织空闲 chunk 。以备用户的下一次分配。若需要分配的 chunk 大小小于 mmap 分配阈值,而 heap 空间又不够,则此时主分配区会通过 sbrk() 调用来增加 heap 大小,非主分配区会调用 mmap 映射一块新的 sub-heap ,也就是增加 top chunk 的大小,每次 heap 增加的值都会对齐到 4KB

当用户的请求超过 mmap 分配阈值,并且主分配区使用 sbrk() 分配失败的时候,或是非主分配区在 top chunk 中不能分配到需要的内存时, ptmalloc 会尝试使用 mmap() 直接映射一块内存到进程内存空间。使用 mmap() 直接映射的 chunk 在释放时直接解除映射,而不再属于进程的内存空间。任何对该内存的访问都会产生段错误。

一般的, 内存变得破碎时,大块的内存分解成非连续的小块。 这些小块就会成为孤儿样,当内存被释放时并不能被合并在一起。 GLIBC 被要求分配一个更大的内存块时,如果不能满足这个要求,就要要求内核提供更多的内存,这也就是为什么我们当前存在空闲内存实际上内存的使用还在上涨的原因(碎片问题)。 Allocate() brk(2) free() fragment 的循环一直持续到进程的内存耗尽,被内核的 OOM Killer 线程杀死,或者 OOM Killer 最终导致系统崩溃。

 

【glibc(ptmalloc)内存分配器的不足】

1、   ptmalloc 不适合用于管理长生命周期的内存,特别是持续不定期分配和释放长生命周期的内存,这将导致 ptmalloc 内存暴增。如果要用 ptmalloc 分配长周期内存,在 32 位系统上,分配的内存块最好大于 1MB 64 位系统上,分配的内存块大小大于 32MB 。这是由于 ptmalloc 默认开启 mmap 分配阈值动态调整功能, 1MB 32 位系统 mmap 分配阈值的最大值, 32MB 64 位系统 mmap 分配阈值的最大值,这样可以保证 ptmalloc 分配的内存一定是从 mmap 映射区域分配的,当 free 时, ptmalloc 会直接把该内存返回给操作系统,避免了被 ptmalloc 缓存。

2、   多线程分阶段执行的程序不适合用 ptmalloc ,这种程序的内存更适合用内存池管理,就像 Appach 那样,每个连接请求处理分为多个阶段,每个阶段都有自己的内存池,每个阶段完成后,将相关的内存就返回给相关的内存池。。 Google 的许多应用也是分阶段执行的,他们在使用 ptmalloc 也遇到了内存暴增的相关问题,于是他们实现了 TCMalloc 来代替 ptmalloc TCMalloc 具有内存池的优点,又有垃圾回收的机制,并最大限度优化了锁的争用,并且空间利用率也高于 ptmalloc 。(这块也是我们选择是否继续使用 glibc 的参考

 



【MYSQL服务器内存暴涨的原因】

1.          内存碎片,由于在运行过程中产生了许多不连续的空小内存堆,  GLIBC 内存分配器可能受到堆碎片的影响,从而导致过度的内存分配,第三方内存分配器如 jemalloc tcmalloc 似乎可以解决这个碎片问题。目前来看,像 jemalloc tcmalloc 这样的内存分配器可能能提供一些额外的效率和性能,特别是在高并发和高负载的情况下。由于 MySQL  没有构建 OS 内存分配器,所以采用默认的 glibc(ptmalloc) 内存分配器, MySQL 申请和释放内存通过该分配器,最终的内存占用还是由 glibc 的特征决定。如果内存变得破碎时,大块的内存分解成非连续的小块。  这些小块就会成为孤儿样,当内存被释放时并不能被合并在一起。  GLIBC 被要求分配一个更大的内存块或更多申请者时,如果不能满足这个要求,就要要求内核提供更多的内存。

2.          另外一个原因是内存泄漏, oracle 官方有相关的内存泄漏 Bug List(Internal - Known MySQL Memory (or Resource) Leak Bugs (Doc ID 1587704.1)) 以及判断内存泄漏的方法。但实际上,大多数情况下并没有一个绝对的标准,也就是某些场景下,我们无法绝对的区别是内存泄漏还是内存碎片。不过官方给出了一个说明,如下: MySQL 维护着内部许多缓冲区,这些缓冲区不断增长,直到达到配置的最大尺寸。这些缓冲区中最大的通常是 Innodb Buffer Pool 。在一个有许多表或分区表的繁忙系统上,这个缓冲区可以相当快地增长到配置的大小,然而在一些数据量很小的系统上,缓冲池有可能从未完全初始化。在这种情况下,可能会出现 MySQL 有内存泄漏的情况,但事实上,缓冲池在较长的时间内持续增长,以满足配置的大小。只有当所有缓冲区都被完全分配后,才能评估是否有内存泄漏。内存泄漏看起来就像一个较长时期内的非周期性的内存使用模式;周期通常从一周重复到一周,因为每天的查询负载形成了一个模式。如果内存使用量只在几周内增长,而且你的缓冲区都被完全分配了,那么你可能有一个内存泄漏。

mysql 很多 bug 文档上大部分情况都表示其不是 mysql bug ,而是内存分配器的问题,例如

In   short, several memory tables of observable size are created and dropped.   Although performance_schema schema shows that malloc and free in heap is   balanced, but not all memory is released from mysqld due to implementation of   glibc.

  By   changing malloc-lib to jemalloc, memory is released after table drop.

 

 

What you describe is a typical behaviour of the GNU malloc library. We are not in charge of maintaining that library. Also, on Linux and some other operating systems, you can easily change to the malloc library of your choice.
Even with GNU malloc library, you can free memory by writing a short C program which would only called calloc() with the parameter that is slightly larger then the available memory on the system.
In short, this is not our bug.
 

 

 

3.          当堆碎片不是原因时,通常可能是某些线程级别参数设置不合理等导致了内存的增加。 我们知道一旦内存被GLIBC分配到堆中,它就不会返回给内核,所以内存使用的峰值将导致进程内存使用量推高到一个高水位,直到它被重新启动。

4.          Innodb Buffer Pool 设置不合理,这是一个可能原因。

 




【可以考虑使用更换成谷歌的内存分配器tcmalloc】

MOS 文档描述 :

Finally, high performance memory allocators like jemalloc and tcmalloc may provide you some additional efficiency and performance, especially with high core counts and heavily threaded workloads. A the time of writing TCMalloc 2.5 is the latest version so you may want to experiment with that and jemalloc to see which works best for your workload and system.

jemalloc and tcmalloc BSD google 优化后的内存分配器。

 

请参考以下替换方案:

 
----    Install TCMALLOC
     
1.unpack
     
tar   zxvf  gperftools-2.9.1.tar.gz
     
./configure    --enable-frame-pointers --prefix=/usr/lib --libdir=/usr/lib
     
make    && make install 
     
   
IF:
     
configure:    WARNING: No frame pointers and no libunwind. Using experimental   backtrace  capturing via l
     
 
     
workaround:
     
     
or
     
yum -y    install libunwind
     
 
     
2.change    option
     
vi my.cnf
     
[mysqld_safe]
     
malloc-lib=/usr/lib/libtcmalloc.so                //   As  of MySQL 5.7.15, the option value must be one of the directories   /usr/lib,  /usr/lib64, /usr/lib/i386-linux-gnu, or   /usr/lib/x86_64-linux-gnu
     
以上  这种方式使用mysqld_safe启动才生效
如果使用service起:
export   LD_PRELOAD=/usr/lib/libtcmalloc.so 
or
vi   /etc/sysconfig/mysql
LD_PRELOAD=/usr/lib/libtcmalloc.so 
 
测试有效:
/usr/lib/systemd/system
vi   /etc/systemd/system/mysqld3307.service
[Service]下添加:
Environment=LD_PRELOAD=/usr/lib/libtcmalloc.so《《《《《《
 
systemctl   restart mysqld3307 
 lsof   -n | grep tcmalloc
 
     
3.restart    mysql server
     
 
     
4.check 
     
lsof -n   |  grep tcmalloc
     
 
     
mysqld       19277          root     mem          REG                 252,0   2638408   18678278 /usr/lib/libtcmalloc.so.4.5.9
     
mysqld       19277 19278    root     mem          REG                 252,0   2638408   18678278 /usr/lib/libtcmalloc.so.4.5.9
     
mysqld       19277 19279    root     mem         REG                 252,0   2638408   18678278 /usr/lib/libtcmalloc.so.4.5.9
     
......


 

【参考文档】

https://dev.mysql.com/doc/refman/5.7/en/mysqld-safe.html#option_mysqld_safe_malloc-lib

     Overview of  GLIBC  Malloc

glibc 内存管理 ptmalloc 源代码分析 .pdf

深入理解计算机系统 .pdf

How to inspect and diagnose MySQL heap memory fragmentation and excessive memory use (Doc ID 2798321.1)

How do I know if MySQL has a memory leak? (Doc ID 1962842.1)

Recommended Settings for MySQL 5.6, 5.7, 8.0 Server for Online Transaction Processing (OLTP) and Benchmarking (Doc ID 1531329.1)


请使用浏览器的分享功能分享到微信等