遇到一个Linux系统文件被删除后仍占用磁盘的问题

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://latelee.blog.csdn.net/article/details/98194258

在使用Linux系统中,有时候发现明明已经删除了大量的文件(特别是日志文件),但用du查看磁盘,空间依然没有减少,这种非常奇怪的问题,最近遇到了。本文描述一下前因后果,并给出一些个人看法。

起因

最近项目准备上线,同事查了服务器磁盘使用情况,发现隔了几天磁盘空间就减少几百兆,叫我查一下。
环境描述如下:
1、nodejs应用服务,使用pm2管理,每天会产生大量日志,并且要保留。(这是必要的,为了与厂家扯皮保留证据)
2、每天半夜,自动压缩日志。(必须的,否则日志太多了)
3、服务器上还有一期遗留的产品,包括源码,日志。已不使用。

经过

首先使用du -h --max-depth=1查看空间。如下:

0       ./dev
0       ./proc
16K     ./lost+found
961M    ./root
337M    ./var
2.5G    ./usr
33M     ./etc
8.0K    ./opt
147G    ./home
9.4M    ./tmp
4.0K    ./media
226M    ./run
103M    ./boot
4.0K    ./srv
298G 

注:有心的朋友如果计算各个目录的空间总和,则会发现其远远小于最后一行显示的空间大小。笔者在最后时刻才意识到。
/home目录是外挂的硬盘,此处暂不提。从上面信息看到,有几个目录可疑:

961M    ./root
337M    ./var
2.5G    ./usr
226M    ./run

发现/usr/local/share/.cache/yarn/v2/var/cache/yum有缓存数据,可以删除。另外发现/root/root/path存在一些旧代码以及不再使用的日志文件、sql文件,/root/.pm2/有pm2产生的日志。

接着,查找最近1天修改过的文件,命令如下:

find ./* -mtime -1

得到的文件体积均不大,可能不是此方面原因。

到网上搜索,发现有文章介绍相关的内容,使用lsof |grep delete来查询已经删除的文件。结果出现大量文件列表,数量很多:

lsof |grep delete | wc -l
7344

示例如下:

node  786  root 15w   REG  202,64     1941641 5354776 /root/logs/log39.2019-07-26.txt (deleted)
node  786  root 16w   REG  202,64   437345392 5354830 /root/logs/logtf.2019-07-26.txt (deleted)
node  786  root 17w   REG  202,64      471811 5354836 /root/logs/logtt.2019-07-26.txt (deleted)
node  786  root 18w   REG  202,64  1189231954 5354838 /root/logs/logt3.2019-07-26.txt (deleted)
node  786  root 19w   REG  202,64     2003838 5354840 /root/logs/logit.2019-07-26.txt (deleted)

分别为命令、PID、拥有者、FD、类型、设备、大小、节点、文件名称。只关注命令、PID、大小和文件名即可。

随便找一个进程,查看一下占用的文件句柄fd

ls -al /proc/74408/fd
total 0
dr-x------ 2 root root  0 Jul 31 12:19 .
dr-xr-xr-x 9 root root  0 Jun 27 12:27 ..
lrwx------ 1 root root 64 Jul 31 12:19 0 -> socket:[1673886202]
l-wx------ 1 root root 64 Jul 31 12:19 1 -> /root/.pm2/pm2.log
lr-x------ 1 root root 64 Jul 31 12:19 10 -> /dev/null
lrwx------ 1 root root 64 Jul 31 12:19 100 -> socket:[3240337185]
l-wx------ 1 root root 64 Jul 31 12:19 13 -> /root/.pm2/logs/1024-error-28.log (deleted)
lrwx------ 1 root root 64 Jul 31 12:19 130 -> socket:[3207913087]
l-wx------ 1 root root 64 Jul 31 12:19 29 -> /root/logs/log34.2019-06-27.txt (deleted)
lrwx------ 1 root root 64 Jul 31 12:19 3 -> socket:[2024014357]
l-wx------ 1 root root 64 Jul 31 12:19 30 -> /root/logs/log51.2019-06-27.txt (deleted)
l-wx------ 1 root root 64 Jul 31 12:19 31 -> /root/logs/logfa.2019-06-27.txt
l-wx------ 1 root root 64 Jul 31 12:19 32 -> /root/logs/log3f.2019-06-27.txt
l-wx------ 1 root root 64 Jul 31 12:19 33 -> /root/logs/log35.2019-06-27.txt (deleted)
lrwx------ 1 root root 64 Jul 31 12:19 41 -> socket:[2024031696]
lrwx------ 1 root root 64 Jul 31 12:19 42 -> socket:[2024031697]

至此,能定位到原因了:nodejs服务使用log4js作为日志管理模块,每天定时备份,将旧日志文件压缩为新文件,再删除旧日志文件,但程序一直在运行,并未退出,即占用的文件句柄并未释放,所以要积累大量的已删除文件,占用磁盘空间。

解决

将nodejs服务手动重启一次,释放了很多空间,下面是重启前的磁盘空间:

Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/xvda2      37024320  27152180   7984756  78% /
devtmpfs         3983648         0   3983648   0% /dev
tmpfs            3981888         0   3981888   0% /dev/shm
tmpfs            3981888     25672   3956216   1% /run
tmpfs            3981888         0   3981888   0% /sys/fs/cgroup
tmpfs             747024         0    747024   0% /run/user/0
/dev/xvde      515930552 189452476 300263676  39% /home

重启后,空间列表如下:

Filesystem     1K-blocks      Used Available Use% Mounted on
/dev/xvda2      37024320   9699008  25437928  28% /
devtmpfs         3983648         0   3983648   0% /dev
tmpfs            3981888         0   3981888   0% /dev/shm
tmpfs            3981888     25616   3956272   1% /run
tmpfs            3981888         0   3981888   0% /sys/fs/cgroup
tmpfs             747024         0    747024   0% /run/user/0
/dev/xvde      515930552 181515448 308200704  38% /home

其实笔者也有疑惑,按理说,log4js应该在备份日志后,会自动释放句柄,搜索资料未果。按观察之结果,重启一次nodejs即可解决问题。看来,后面要在一定时间间隔内,重启所有的nodejs进程才行。

指导

1、对于旧资料,该删除的要删除,不应保留,除了占用空间,没有其它用。本次排查,统计了旧资料,至少有1GB以上。囿于权限,本次不敢清理。
2、系统缓存(不管是npm、yarn还是apt、yum),建议隔一段时间删除。
3、对使用的程序模块要深入研究,不放过细节。(在追求速度时代,笔者还无法做到)
4、日志规范化,该写的信息要写,不该写的,不写。
5、程序安装目录,尽量不要选择系统目录,如/bin/sbin/usr/bin/usr/sbin,如果一定要安装,仅将必须的二进制安装在系统目录,配置文件、日志文件、数据文件,放到其它分区。本次发现,mysql和redis的数据目录与安装目录是在一起的,这样做不太合理。

拓展

yarn相关命令:
列出cache目录:yarn cache dir
列出详细文件:yarn cache list
清除cache:yarn cache clean
设置cache目录:yarn config set cache-folder <目录>

查找指定时间修改过的文件命令:
查找N天之内:find ./* -mtime -<N天>
查找N天之前:find ./* -mtime +<N天>
恰好N天:find ./* -mtime <N天>
-mtime以天为单位,类似的,-mmin以分钟为单位。

资料

磁盘空间被未知资源占尽分析
一次诡异的磁盘空间占用问题排查
诡异的Linux磁盘空间被莫名其妙占用问题

PS:本文为笔者参考网络资料,结合实验经历得到的一些记录,并不权威,仅为个人观点而已。

展开阅读全文

spring bean 被删除后仍起作用

04-16

springmvc框架,容器加载完成之后动态删除某个bean(一个controller),为何通过浏览器仍能访问到该bean中的接口呢?确认容器中已经不存在该beanrnrn1、删除时机:容器初始化rn[code=java]rn @Componentrnpublic class AfterContextReady implements ApplicationListener rn @Overridern public void onApplicationEvent(ContextRefreshedEvent event) rn //root application context 没有parent,他就是老大.rn if(event.getApplicationContext().getParent() == null)rn rn //需要执行的逻辑代码,当spring容器初始化完成后就会执行该方法。rn ApplicationContextUtil.removeBean("errorDealControllerExtra");rnrn rn rnrn[/code]rn2、删除方法:使用ApplicationContextAware拿到beanfactory,然后从factory中删掉rn[code=java]rn@Componentrnpublic class ApplicationContextUtil implements ApplicationContextAwarernrn private static ApplicationContext applicationContext = null;rn /** Creates a new instance of ApplicationContextUtil */rn public ApplicationContextUtil()rn rn rn public void setApplicationContext(ApplicationContext applicationContext)rn throws BeansExceptionrn rn this.applicationContext = applicationContext;rn rn public static ApplicationContext getApplicationContext()rn rn return applicationContext;rn rn rn public static Object getBean(String name)rn rn return applicationContext.getBean(name);rn rn @SuppressWarnings("unchecked")rn public static T getBeanByName(String name) throws BeansException rn return (T) applicationContext.getBean(name);rn rnrn public static DefaultListableBeanFactory getBeanFactory()rn return (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory();rn rnrn public static Map getClassByAnnotation(Class name)rn return applicationContext.getBeansWithAnnotation(name);rn rn public static void removeBean(String beanName)rn DefaultListableBeanFactory factory = getBeanFactory();rn if (factory.containsBean(beanName)) rn factory.removeBeanDefinition(beanName);rn rn rnrn[/code]rn3、要删除的bean(errorDealControllerExtra)rn[code=java]rn@Controllerrn@RequestMapping("/errorDeal")rnpublic class ErrorDealControllerExtra rn @ResponseBodyrn @RequestMapping("/dealPdfList")rn public String dealPdfList(HttpServletRequest request) rn "访问成功!";rn rnrn[/code]rn4、动态删除bean之后通过浏览器访问controller中的接口。仍然能够访问到。rn[img=https://img-bbs.csdn.net/upload/201904/16/1555393769_670576.png][/img]rnrnrn有人能给解释一下这是为什么吗?rn 论坛

没有更多推荐了,返回首页