昨日线上一台机器上的nginx rt飙高,@明俨 调查发现这台机器上的metaserver内存占用很高,同时还有个奇怪的现象,df发现/home的空间占用在增长飞快,但metaserver和nginx的日志文件增长都很慢,通过du -sh /home统计home下文件的总大小,发现跟df命令/home占用的空间小10+g,到底谁占用了我的磁盘空间?

后来把nginx进程都停掉后(当时应该先通过lsof或/proc/pid/fd/看看nginx当时打开的文件情况),df显示/home的空间一下降下来了,跟du统计的结果能匹配上,说明这些磁盘空间都被nginx进程占用着。内核组的同事告诉我们,一个文件被进程打开后,在关闭前如果文件被删除,此时这个文件已经在它父目录的目录项中被删除掉(在父目录ls已经看不到这个文件),但该进程依然能够正常的读写文件,直到文件被该进程关闭后,这个文件的空间才会被回收,通过下面一个小例子解释下。

int main()
{
  int fd = open("afile", O_CREAT | O_RDWR);
  assert(fd > 0); 
  unlink("afile");

  while (1) 
  {
    char buf[1024];  // 1K
    for (int i = 0; i < 102400; i++)
    {   
      int len = write(fd, buf, 1024);
      assert(len == 1024);
    }   

    lseek(fd, 0, SEEK_SET);  // keep writing, but don't make it increase
    sleep(5);
  }

  return 0;
}
# g++ filetest.cpp

# ls

a.out  filetest.cpp  没有afile这个文件 

# du -sh   

  16K     .

# df -m | grep home

  /dev/sda9               639406    **521255 **    85148  86% /home



# ./a.out &  执行测试程序,持续写文件前100M的空间

# ls

a.out  filetest.cpp  没有afile这个文件 

# du -sh   

  16K     .

# df -m | grep home

/dev/sda9               639406      **521355 **    85048  86% /home



# killall a.out  杀掉测试程序

# df -m | grep home

  /dev/sda9               639406    **521255 **    85148  86% /home

可以看出,进程在打开afile之后立即unlink掉这个文件,通过ls看不到这个文件,因为父目录中这个文件的信息已经删除,在进程持续写文件的过程中(为了避免空间暴增,一直写前100M,仅供测试观察之用),通过du -sh发现该目录下文件总空间没有变化,因为du是通过遍历文件来统计的,而通过df我们发现home的used刚好增加了100M的空间,接下来我们把测试程序关闭掉,home的used又降了100M,说明afile的空间已经被回收了。

nginx有个特性,post请求的body如果超过2个page(可配置),就会把body的数据写到磁盘暂存,等请求处理完后,再删除临时的文件。nginx在open创建临时文件后,会先unlink掉这个文件(为什么要这么做,有什么好处?),然后在请求处理完后close掉文件。告警的服务器当时由于metaserver占用了大部分的内存,已经出现了swap的情况,而通过nginx写的文件平均大小在60-70K左右,基本都要写临时文件,导致nginx的服务越来越慢,积压大量的POST请求,这些请求对应的临时文件一直占用着磁盘空间,但因为文件已经删除,外面已经看不到这些文件的存在,从而导致du,df显示的结果相差很大。


ref:
2013-04-19 14:13
blog.yunnotes.net

0 回复
需要 登录 后方可回复, 如果你还没有账号你可以 注册 一个帐号。