今天早上在公司遇到一个磁盘空间相关的问题,比较典型,记录一下,希望对大家有帮助。
业务侧反馈有磁盘报警现象,登陆到服务器上之后,看到下面的场景:
[root@ data1]# df -h /data1 Filesystem Size Used Avail Use% Mounted on /dev/sda8 1.5T 1.3T 140G 91% /data1 [root@ data1]# du -sh * 21M database_exporter 845M dbatemp 16K lost+found 116G mysql3306 6.4G mysql3630 414G mysql5186 10M mysqlbinlog
图中可以得到下面的信息:
1、磁盘的总容量是1.5T,实际占用了1.3T
2、/data1目录下的mysql相关的目录,加起来只有不到600G,而其他目录,几乎不占用磁盘空间
3、df -h看到的占用1.3T容量和du -sh看到的实际600G容量不匹配,似乎有700G磁盘容量消失了
剩余的700G空间去哪里了?
检查了磁盘上的隐藏文件,也没有发现端倪。于是我想到了之前的一个经典案例,就是Linux服务器下,有可能出现这种删除文件之后,磁盘空间不释放的情况,一般是文件句柄不释放,导致的磁盘空间问题。
这里需要简单说一个Linux命令,就是lsof命令:
在Linux操作系统中,一切皆文件,而lsof命令的全程就是(list open files),将所有的打开的文件都展示出来,lsof命令不仅可以查看操作系统打开文件、目录的情况,还可以查看进程监听的端口等信息。常用的lsof命令的参数如下:
-a 指示其它选项之间为与的关系 -c <进程名> 输出指定进程所打开的文件 -d <文件描述符> 列出占用该文件号的进程 +d <目录> 输出目录及目录下被打开的文件和目录(不递归) +D <目录> 递归输出及目录下被打开的文件和目录 -i <条件> 输出符合条件与网络相关的文件 -n 不解析主机名 -p <进程号> 输出指定 PID 的进程所打开的文件 -P 不解析端口号 -t 只输出 PID -u 输出指定用户打开的文件 -U 输出打开的 UNIX domain socket 文件 -h 显示帮助信息 -v 显示版本信息
想到这里,我利用lsof命令查看当前打开的文件内容,并过滤了一下已经被delete的文件,结果如下:
[root@ data1]# lsof -n | grep delete COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF node NAME cupsd 1225 root 9r REG 8,2 1926 1575211 /etc/passwd+ (deleted) mysqld_sa 9014 root 0u CHR 136,0 0t0 3 /dev/pts/0 (deleted) mysqld 10175 my5186 5u REG 8,8 15513 96731233 /data1/mysql5186/ibdpQaPG (deleted) mysqld 10175 my5186 6u REG 8,8 0 96731972 /data1/mysql5186/ibQg9qu6 (deleted) mysqld 10175 my5186 7u REG 8,8 0 96731979 /data1/mysql5186/ibLLGH9v (deleted) mysqld 10175 my5186 8u REG 8,8 0 96731980 /data1/mysql5186/ibYg7Bln (deleted) mysqld 10175 my5186 13u REG 8,8 0 96731981 /data1/mysql5186/ibm22peP (deleted) mysqld 10175 my5186 19w REG 8,8 786372990960 96731986 /data1/mysql5186/slow.log (deleted) mysqld 10175 1400 my5186 5u REG 8,8 15513 96731233 /data1/mysql5186/ibdpQaPG (deleted) mysqld 10175 1400 my5186 6u REG 8,8 0 96731972 /data1/mysql5186/ibQg9qu6 (deleted) mysqld 10175 1400 my5186 7u REG 8,8 0 96731979 /data1/mysql5186/ibLLGH9v (deleted) mysqld 10175 1400 my5186 8u REG 8,8 0 96731980 /data1/mysql5186/ibYg7Bln (deleted) mysqld 10175 1400 my5186 13u REG 8,8 0 96731981 /data1/mysql5186/ibm22peP (deleted) mysqld 10175 1400 my5186 19w REG 8,8 786372847911 96731986 /data1/mysql5186/slow.log (deleted) mysqld 10175 1401 my5186 5u REG 8,8 15513 96731233 /data1/mysql5186/ibdpQaPG (deleted) mysqld 10175 1401 my5186 6u REG 8,8 0 96731972 /data1/mysql5186/ibQg9qu6 (deleted) mysqld 10175 1401 my5186 7u REG 8,8 0 96731979 /data1/mysql5186/ibLLGH9v (deleted) mysqld 10175 1401 my5186 8u REG 8,8 0 96731980 /data1/mysql5186/ibYg7Bln (deleted) mysqld 10175 1401 my5186 13u REG 8,8 0 96731981 /data1/mysql5186/ibm22peP (deleted) mysqld 10175 1401 my5186 19w REG 8,8 786372854597 96731986 /data1/mysql5186/slow.log (deleted) mysqld 10175 1402 my5186 5u REG 8,8 15513 96731233 /data1/mysql5186/ibdpQaPG (deleted) mysqld 10175 1402 my5186 6u REG 8,8 0 96731972 /data1/mysql5186/ibQg9qu6 (deleted) mysqld 10175 1402 my5186 7u REG 8,8 0 96731979 /data1/mysql5186/ibLLGH9v (deleted) mysqld 10175 1402 my5186 8u REG 8,8 0 96731980 /data1/mysql5186/ibYg7Bln (deleted) mysqld 10175 1402 my5186 13u REG 8,8 0 96731981 /data1/mysql5186/ibm22peP (deleted)
上面命令就是过滤那些已经从linux操作系统删除掉,然后还处于打开状态的文件(这意味着他们的文件句柄还没有释放,自然磁盘空间就没有释放)。
结果可以看到,有个MySQL实例持有的slow.log文件已经被删除了,但是句柄还没有释放,红色部分size字段显示的数字是786372854597,这个单位是B,换算成GB,也就是786G,这就能解释为什么我们的磁盘空间被消耗了这么多了,现在的问题就是解决这个句柄占用不释放的问题了。
既然是持有的slowlog文件没有释放,那么我们就重新生成一个新的slowlog文件就行,于是,对这个MySQL实例重新生成slowlog文件,执行命令flush slow logs,如下:
superdba@[(none)] 17:45:11>flush slow logs; Query OK, 0 rows affected (28.83 sec)
可以看到,执行时间还是比较长的,持续了28s,这期间我还是比较担心,担心MySQL实例直接挂掉,好在没有挂掉,一切比较正常。
处理过后,再来看磁盘空间:
[root@mis73243 ~]# df -h /data1 Filesystem Size Used Avail Use% Mounted on /dev/sda8 1.5T 571G 874G 40% /data1
可以看到磁盘空间已经被释放掉了。
至此,问题解决。
总结:
1、Linux中,删除文件,没有释放空间,有可能是文件句柄没有释放,持续占用磁盘空间
2、可以通过lsof命令来查看当前打开的文件情况,如果要过滤已经删除的文件,可以使用lsof -n | grep delete命令
3、MySQL可以通过flush slow logs命令来重新生成slowlog,释放旧的slowlog句柄,如果是其他类型的log,可以使用对应的flush语句,如下:
FLUSH [NO_WRITE_TO_BINLOG | LOCAL] { flush_option [, flush_option] ... | tables_option } flush_option: { BINARY LOGS | ENGINE LOGS | ERROR LOGS | GENERAL LOGS | HOSTS | LOGS | PRIVILEGES | OPTIMIZER_COSTS | RELAY LOGS [FOR CHANNEL channel] | SLOW LOGS | STATUS | USER_RESOURCES } tables_option: { TABLES | TABLES tbl_name [, tbl_name] ... | TABLES WITH READ LOCK | TABLES tbl_name [, tbl_name] ... WITH READ LOCK | TABLES tbl_name [, tbl_name] ... FOR EXPORT }
关于flush命令,更多信息,请参考MySQL官方文档。