背景介绍
MySQL实例在宕机时,会产生Core File。它保存了宕机时的现场,以便于后期的故障排查。在以前mysqld在写core file时,会将buffer pool的内容也一并dump出去,常常会写造成core file时间长,占用大量磁盘等问题。根据MySQL 8.0的新特性,TenDB引入了Core Dump时忽略Buffer Pool内容的功能。并发现此功能在MySQL 8.0中存在的两个Bugs。
什么是Core File?
Core File/Core Dump是记录一个进程的内存信息的镜像,包括一些寄存器信息,进程状态等。程序挂掉以后,可以根据Core File的信息做debug。
为什么要忽略Buffer Pool?
实际使用时,为了减少刷盘频率,通常会设置一个很大的Buffer Pool值。在Core Dump时就会产生一个很大的Core File,这样的话可能会带来三个问题。
1. 可能磁盘空间不够造成新的问题
2. 可能需要很长的时间写core file
3. 可能会有信息泄露的危险,毕竟Buffer Pool里面存放数据和索引。
新功能速览
新加了global参数
@@innodb_buffer_pool_in_core_file
ON表示记录buffer pool信息到core file中,OFF表示不记录,默认为ON。用户可以选择是否将buffer_pool dump到core file中。
如果需要不dump buffer pool,需要同时满足下面三个条件。
1. @@innodb_buffer_pool_in_core_file 是 OFF
2. @@core_file 是 ON
3. 操作系统需要支持系统调用madvise(ptr,size,MADV_DONTDUMP)。这个调用可以使得在产生Core Dump时忽略某些信息,Linux Kernel 3.4以上的版本可以支持。
测试记录
目前的配置如下表
Variable_name | Value | Value Explain |
innodb_buffer_pool_size | 2147483648 | 2G |
innodb_buffer_pool_instances | 8 | 8个 |
innodb_buffer_pool_chunk_size | 134217728 | 128M |
不忽略Buffer Pool时:
1. 启动mysqld;
./bin/mysqld --datadir=$datadir \--innodb-buffer-pool-in-core-file \--core-file --user=mysql &
2. 模拟杀死mysqld,让它产生core file;
kill -s SIGABRT $mysql_pid
3. 在data目录中看core file的大小,这里是2.8G。
忽略Buffer Pool时:
1. 启动mysqld;
./bin/mysqld --datadir=$datadir \--skip-innodb-buffer-pool-in-core-file \--core-file --user=mysql &
2. 模拟杀死mysqld,让它产生Core File;
kill -s SIGABRT $mysql_pid
3. 同样的配置,看一下结果,只有652M了。
实现原理
实现参照了MySQL 8.0中由Facebook提交的一个patch,并对TenDB做了适配和优化。
原来buffer_pool在分配/释放大片内存时,会用到以下的两个函数。
pointer allocate_large(...);void deallocate_large(...);
现在需要做的功能是,通过一些参数判断,决定core dump时,是否需要dump出这些内存。
于是在初始化buffer control block时添加了
static bool buf_pool_should_madvise = false;
这个参数用于表示当前的参数是否需要把buffer pool dump到core file中,对这个参数的改变是需要通过新加的互斥锁chunk_mutex保护的。这样可以防止
1. 初始化时chunk时,@@innodb_buffer_pool_in_core_file改变 (这种情况很少)
2. 运行时,@@innodb_buffer_pool_size改变
对每个chunk,实现了以下两个函数,最后落地是否dump
/* 主要是通过调用 madvise(mem, mem_size(), MADV_DODUMP) */bool buf_chunk_t::madvise_dump(); /* 主要是通过调用 madvise(mem, mem_size(), MADV_DONTDUMP) */bool buf_chunk_t::madvise_dont_dump();
因为新加了状态buf_pool_should_madvise,原来的分配方式是没有should_dump状态的,所以新增了两个分配/释放函数,在其中包装了原来的分配功能。(相当于一个wrapper)
// 其中实现了allocator.allocate_large()// 并维护buf_pool_should_madvise状态bool buf_pool_t::allocate_chunk(...)// 其中实现了deallocator.allocate_large()// 并维护buf_pool_should_madvise状态void buf_pool_t::deallocate_chunk(...);
为了解决运行时更新参数@@innodb_buffer_pool_in_core_file的问题,写了以下函数来更新buf_pool_should_madvise的状态,注意,这里必须对所有chunk加互斥锁实现。
void buf_pool_update_madvise();
最后在调用方面,实现了两个函数,对所有的chunks进行遍历,进行dump/dontdump,这里不进行详述。
Bug说明
在测试过程中,发现Facebook提交的这些代码有两个Bug。
Bug1: mysql-test找不到core file (Fixed)
在MySQL 8.0提供的mysql-test中,会出现找不到core file的问题
Bug出现的原因
MySQL 8.0是通过在mysql-test的data目录下寻找core file的,通常这个目录是mysql-test/var/mysqld.1/data/。而实际上,core file不一定在这个目录中。
/proc/sys/kernel/core_pattern这个文件的内容决定了core file的路径和命名方式。
cat /proc/sys/kernel/core_pattern
如果结果是core,那么表示是一个相对路径,命名方式为core.$pid
而路径也可以不是一个在data下的路径,比如某机器机上为/data/corefile/core_%e_%t,这个表示core file产生的位置是/data/corefile,命名方式为core_$进程名_$dump时间.$pid,例如core_mysq-test_1577090676.28888。
所以,原mysql-test的寻找策略是有问题的。
解决方案
在TenDB中,我们通过修改mysql-test寻找core file的策略,分析系统的core_pattern,在系统指定的路径下,寻找core file。从而可以解决非data目录下的core file无法找到的问题。
Bug2: 无法判断内核是否支持MADV_DONTDUMP
可以知道,整个功能最依赖的就是madvise()这个函数,以及它支不支持MADV_DONTDUMP。
Facebook的做法是,通过在configure.cmake中加入了以下的语句
include(CheckSymbolExists)CHECK_SYMBOL_EXISTS(MADV_DONTDUMP "sys/mman.h" HAVE_MADV_DONTDUMP)# 下面代码用于测试输出IF (HAVE_MADV_DONTDUMP)MESSAGE(STATUS, "Platform supports MADV_DONTDUMP")ELSE()MESSAGE(FATAL_ERROR, "Platform does not support MADV_DONTDUMP")ENDIF()
根据 Linux Manual所述“MADV_DONTDUMP (since Linux 3.4)”,即这个参数是 Linux 3.4 之后才有的。
但是实际测试时,发现在Linux 2.6的机器中也输出了Platform supports MADV_DONTDUMP。
Bug出现的原因
其实
CHECK_SYMBOL_EXISTS(MADV_DONTDUMP "sys/mman.h" HAVE_MADV_DONTDUMP)
只是check 的glibc-headers的头文件,并不是系统内核的头文件。可以用rpm -ql glibc-headers查看glibc headers文件列表。
结果发现在一个glibc的头文件中有对这个宏的定义
#define MADV_DONTDUMP 16
而glibc是编译时的行为,不能实际反映kernel是否支MADV_DONTDUMP
这个Bug会在以下两个情况中发生
1. 在高版本的Linux机器编译,低版本机器中运行;
2. 机器的glibc处于较高版本,而机器内核版本较低。
如何解决这个Bug
在Linux内核中,检测参数是否存在的函数是madvise_behavior_valid();可惜这个函数是static的,意味着外部无法调用。
于是,如果想要解决这个问题,只能够在运行时测试一组合法数据的dump/dontdump,看结果是否出错,来确定系统是否支持这两个参数。但是由于madvise可能出错的原因很多,并且这个解决方案并不优雅,所以暂时没有采用这样的解决方案。