MySQL为什么lsof会看到这么多临时文件

作者:高鹏(网名八怪),《深入理解MySQL主从原理32讲》系列的作者。系列链接:https://www.jianshu.com/nb/43148932
版本:5.7.29
一、问题提出一般情况下我们会使用lsof命令来查看MySQL当前使用的临时文件的使用,这是因为这些临时文件使用ls命令并不能显示,在5.7中其建立方式是使用Liunx api mkstemp进行的建立,这种文件是进程专用的,返回文件描述符后会使用api unlink进行删除。
源码主要就是这一段,其包含在create_temp_file函数中。
org_file=mkstemp(to);if(mode&O_TEMPORARY)(void)my_delete(to,MYF(MY_WME));
在5.7中如下常见的一些功能会用到这种临时文件,比如:
执行计划 filesort 文件名字:MY开头
lsof|grepdelete如:/tmp/MYdRH1GW(deleted)
大事务binary log缓存文件名字:ML开头
lsof|grepdelete如:/tmp/MLq9INFu(deleted)
online DDL 涉及排序比如add key,一般是 ib开头
altertabletestsortaddkey(id);lsof|grepdelete如:/tmp/ibCxlYQg(deleted)/tmp/ib51nvZ1(deleted)设置innodb_tmpdir可以将这类文件放到指定的目录
但是当一个MySQLD进程刚刚启动的时候我们去查看这类文件的时候,会发现很多这类文件,如下:
[root@mgr2~]#lsof|grepdelete|grepmysql|wc-l215[root@mgr2~]#
这些文件大小基本都是0,且都以ib开头,曾经有朋友问过我这个问题,这里详细做一下解释。
二、MySQLD初始化后的临时文件种类实际上在5.7中这类文件均由函数create_temp_file建立,那么我们就可以在MySQLD启动的时候将断点放在上面进行分析了。
下面这样一些文件,均使用的是ib开头的临时文件。
srv_monitor_file (srv0start.cc:2022):这是我们通常show engine innodb status命令输出的时候使用到的文件,主要函数为srv_printf_innodb_monitor。栈针:
srv_dict_tmpfile (srv0start.cc:2032):主要和外键创建信息输出有关。
srv_misc_tmpfile (srv0start.cc:2041):主要和外键抛错输出有关。
lock_latest_err_file (lock0lock.cc:465):主要和死锁报错输出有关。
dict_foreign_err_file (dict0dict.cc:1245):主要和外键抛错输出有关。
我们可以发现实际上这些文件都不会占用太大,实际上这5个文件初始化后就已经建立好了。
三、为什么lsof默认的输出会多很多很明显我们上面ib打头的文件只有5个,那么我们lsof(不加任何参数)的时候看到的会多很多呢?
如下是我初始化启动的情况下看到的个数:
[root@mgr2support-files]#lsof|grepdelete|grepmysql|wc-l215[root@mgr2support-files]#lsof|grepdelete|grepmysqlmysqld12916mysql10uREG8,5976517109405/tmp/ibpuzts2(deleted)mysqld12916mysql11uREG8,5023137683/tmp/ibA4q9iX(deleted)mysqld12916mysql12uREG8,5023137684/tmp/ibRdXEl4(deleted)mysqld12916mysql13uREG8,5023137685/tmp/ibe6hfHJ(deleted)mysqld12916mysql19uREG8,5023137686/tmp/ibfb7rps(deleted)mysqld1291612920mysql10uREG8,5976517109405/tmp/ibpuzts2(deleted)mysqld1291612920mysql11uREG8,5023137683/tmp/ibA4q9iX(deleted)mysqld1291612920mysql12uREG8,5023137684/tmp/ibRdXEl4(deleted)mysqld1291612920mysql13uREG8,5023137685/tmp/ibe6hfHJ(deleted)mysqld1291612920mysql19uREG8,5023137686/tmp/ibfb7rps(deleted)mysqld1291612944mysql10uREG8,5976517109405/tmp/ibpuzts2(deleted)mysqld1291612944mysql11uREG8,5023137683/tmp/ibA4q9iX(deleted)mysqld1291612944mysql12uREG8,5023137684/tmp/ibRdXEl4(deleted)mysqld1291612944mysql13uREG8,5023137685/tmp/ibe6hfHJ(deleted)mysqld1291612944mysql19uREG8,5023137686/tmp/ibfb7rps(deleted)mysqld1291612945mysql10uREG8,5976517109405/tmp/ibpuzts2(deleted)mysqld1291612945mysql11uREG8,5023137683/tmp/ibA4q9iX(deleted)mysqld1291612945mysql12uREG8,5023137684/tmp/ibRdXEl4(deleted)此处省略….
我们主要关注以下几列:
PID:is the Process IDentification number of the process.
TID:is the task (thread) IDentification number
FD:is the File Descriptor number of the file
SIZE/OFF:is the size of the file or the file offset in bytes
NAME:is the name of the mount point and file system on which the file resides 更加详细的自行参考man。
从输出中我们可以发现
PID都是12916这正是mysqld的PID,而它们的TID则不同,这些当然就是多线程造成的原因了。
FD和NAME实际上是有规律的重复的,其实只有5个值而已。这代表我们上面说的5个临时文件
SIZE都比较小。
因此我们可以认为虽然输出多达215行,但是实际上整个MySQLD进程只打开了5个文件而已也就是我们前面说的那5个临时文件,这是因为线程间文件描述符(FD)是共享的,每一个线程lsof查看的时候都会显示这5个临时文件,如果我们的MySQL有很多session,那么线程会很多很多,那么lsof看到的临时文件数量就是线程数据量*5。因此我们在检查临时文件使用空间情况的时候不要被过多的输出而吓到,我们只要输出MySQLD进程的临时文件即可,输出如下:
[root@mgr2proc]#lsof|grepdelete|grepmysql|awk'{if($3==”mysql”)print}’mysqld12916mysql10uREG8,5976517109405/tmp/ibpuzts2(deleted)mysqld12916mysql11uREG8,5023137683/tmp/ibA4q9iX(deleted)mysqld12916mysql12uREG8,5023137684/tmp/ibRdXEl4(deleted)mysqld12916mysql13uREG8,5023137685/tmp/ibe6hfHJ(deleted)mysqld12916mysql19uREG8,5023137686/tmp/ibfb7rps(deleted)或者加上lsof加上参数只看进程的临时文件,而不去查看子线程的临时文件[root@mgr2~]#lsof-p12916|grepdeletemysqld12916mysql10uREG8,5976517109405/tmp/ibpuzts2(deleted)mysqld12916mysql11uREG8,5023137683/tmp/ibA4q9iX(deleted)mysqld12916mysql12uREG8,5023137684/tmp/ibRdXEl4(deleted)mysqld12916mysql13uREG8,5023137685/tmp/ibe6hfHJ(deleted)mysqld12916mysql19uREG8,5023137686/tmp/ibfb7rps(deleted)
注意MySQL8.0.21下这些临时文件的名字有一些变化,Liunx下可能直接(O_TMPFILE flag)使用这种方式建立如下:
open(dirname_buf,O_RDWR|O_TMPFILE|O_CLOEXEC,S_IRUSR|S_IWUSR)我测试得到的名字如下:[root@mgr4~]#lsof|grepa.out|grepdeletea.out11703root3uREG8,5024237415/tmp/#24237415(deleted)MySQLD启动后查看的临时文件名如下:[root@mgr4~]#lsof-p11488|grepdeletemysqld11488mysql10uREG8,5024237413/tmp/#24237413(deleted)mysqld11488mysql11uREG8,5024237414/tmp/#24237414(deleted)mysqld11488mysql12uREG8,5024237416/tmp/#24237416(deleted)mysqld11488mysql19uREG8,5024237417/tmp/#24237417(deleted)
虽然临时文件名有所变化,但是基本的用途和5.7差不多
四、线程间的文件描述符(FD)是共享的这一段没什么好解释的借用Linux系统编程的一段说明如下:
五、测试我们可以写一段简单的多线程的代码来测试如下(不做任何错误判断):
#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<string.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<pthread.h>void*u_sleep(void*t){charfile_temp[]=”ibXXXXXX”;intfd=mkstemp(file_temp);unlink(file_temp);inti=0;char*teststr=”aasd”;printf(“theadtmpfile:%sn”,file_temp);for(i=0;i<100000;i++){write(fd,teststr,strlen(teststr));}sleep(100);}intmain(void){intret=0;pthread_ttid[5];inti=0;intfd=0;inttid_num=0;inttmp_file_num=5;charfile_temp[]=”ibXXXXXX”;for(i=0;i<tmp_file_num;i++){strcpy(file_temp,”ibXXXXXX”);mkstemp(file_temp);printf(“%sn”,file_temp);unlink(file_temp);}for(i=0;i<sizeof(tid)/sizeof(pthread_t);i++){pthread_create(tid+tid_num,NULL,u_sleep,NULL);tid_num++;}for(i=0;i<sizeof(tid)/sizeof(pthread_t);i++){ret=pthread_join(*(tid+i),NULL);}}
这段代码实际上就是模拟在主进程中通过mkstemp和unlink建立了5个临时文件且也是以ib打头(模拟MySQL的临时文件),然后建立了5个线程,每个线程再各自建立一个临时文件,写点东西进去,然后线程睡眠一会,主线程会等待线程回收资源,因此可以有足够的时间观察到。
这样来观察是线程之间文件描述符(FD)是否共享,预期我们能够观察到的输出为60个(1个主控线程,5个子线程,主控线程建立了5个临时文件6个线程共享就是30个,然后每个子线程建立了1个临时文件又是5个临时文件,6个线程共享又是30个)如下输出:
[root@mgr2~]#lsof|grepdelete|grepa.out|wc-l60[root@mgr2~]#lsof|grepdelete|grepa.out|awk'{if($3==”root”)print}’a.out13460root3uREG8,5043691141/root/ibpotqYK(deleted)a.out13460root4uREG8,5043691142/root/ibxlgopk(deleted)a.out13460root5uREG8,5043691144/root/ibHOmmQT(deleted)a.out13460root6uREG8,5043691145/root/ibvmykht(deleted)a.out13460root7uREG8,5043691146/root/ib3TViI2(deleted)a.out13460root8uREG8,540000043691147/root/ibEn9hAb(deleted)a.out13460root9uREG8,540000043691148/root/ibkexi9B(deleted)a.out13460root10uREG8,540000043691149/root/ibJJZi1K(deleted)a.out13460root11uREG8,540000043691150/root/ibRRxKtk(deleted)a.out13460root12uREG8,540000043691151/root/ibWuKsWT(deleted)[root@mgr2~]#lsof|grepdelete|grepa.out|morea.out13460root3uREG8,5043691141/root/ibpotqYK(deleted)a.out13460root4uREG8,5043691142/root/ibxlgopk(deleted)a.out13460root5uREG8,5043691144/root/ibHOmmQT(deleted)a.out13460root6uREG8,5043691145/root/ibvmykht(deleted)a.out13460root7uREG8,5043691146/root/ib3TViI2(deleted)a.out13460root8uREG8,540000043691147/root/ibEn9hAb(deleted)a.out13460root9uREG8,540000043691148/root/ibkexi9B(deleted)a.out13460root10uREG8,540000043691149/root/ibJJZi1K(deleted)a.out13460root11uREG8,540000043691150/root/ibRRxKtk(deleted)a.out13460root12uREG8,540000043691151/root/ibWuKsWT(deleted)a.out1346013461root3uREG8,5043691141/root/ibpotqYK(deleted)a.out1346013461root4uREG8,5043691142/root/ibxlgopk(deleted)a.out1346013461root5uREG8,5043691144/root/ibHOmmQT(deleted)a.out1346013461root6uREG8,5043691145/root/ibvmykht(deleted)a.out1346013461root7uREG8,5043691146/root/ib3TViI2(deleted)a.out1346013461root8uREG8,540000043691147/root/ibEn9hAb(deleted)a.out1346013461root9uREG8,540000043691148/root/ibkexi9B(deleted)a.out1346013461root10uREG8,540000043691149/root/ibJJZi1K(deleted)a.out1346013461root11uREG8,540000043691150/root/ibRRxKtk(deleted)a.out1346013461root12uREG8,540000043691151/root/ibWuKsWT(deleted)省略很多…..
因此我们的说法得到证明。
扫码添加作者微信
全文完。
Enjoy MySQL 🙂
叶老师的「MySQL核心优化」大课已升级到MySQL 8.0,扫码开启MySQL 8.0修行之旅吧

版权声明