磁盘空间去哪了

磁盘空间去哪了

问题背景

QBus是一套分布式消息队列系统,具有高性能,高吞吐,可持久化的特点,并且支持订阅功能。可持久化就意味着需要将消息保存在服务器的磁盘中。近期,QBus多个集群因磁盘空间满报警,比如rzwt集群的一台机器,使用df -h看到数据文件所在磁盘空间使用率确实超过了87%,但是重启其QBus进程以后,磁盘空间使用率回落到60%~70%。

PS:QBus服务器会定期扫描数据文件,并对过期的文件进行删除,默认扫描周期为10分钟。

问题追查

是QBus进程退出时做了又进行了一次删除过期文件的操作吗?

通过重启前后数据目录下总文件数的对比,文件总数和文件名一样时,磁盘空间也会回落,而且kafka的删除操作是针对整个文件的,不会存在删除文件部分数据的可能性,所以文件个数和名称一致即代表没有进行删除操作。

是kafka后台线程删除文件以后,用于存取数据的线程还未关闭文件吗?

在kafka运行时用lsof -p输出进程打开的文件列表与ls打出的一样,排除这种可能性

是不是df工具本身的问题呢

df源码

于是下载了 coreutils的源码(df属于这个包),可以看到是取磁盘超级块的内容

coreutils 8.23 fsusage.c

  173 #elif defined STAT_READ_FILSYS          /* SVR2 */
  174 # ifndef SUPERBOFF
  175 #  define SUPERBOFF (SUPERB * 512)
  176 # endif
  177 
  178   struct filsys fsd;
  179   int fd;
  180 
  181   if (! disk)
  182     {
  183       errno = 0;
  184       return -1;
  185     }
  186 
  187   fd = open (disk, O_RDONLY);
  188   if (fd < 0)
  189     return -1;
  190   lseek (fd, (off_t) SUPERBOFF, 0);
  191   if (full_read (fd, (char *) &fsd, sizeof fsd) != sizeof fsd)
  192     {
  193       close (fd);
  194       return -1;
  195     }
  196   close (fd);
  197 
  198   fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512);
  199   fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize);
  200   fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree);
  201   fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree);
  202   fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0;
  203   fsp->fsu_files = (fsd.s_isize == -1
  204                     ? UINTMAX_MAX
  205                     : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1));
  206   fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode);

磁盘空间的划分

Disk is divided up into 512-byte blocks

  • Block 0: boot block
  • Block 1: superblock (struct filsys)
  • Blocks 2 – f(Ninodes): inodes (16 inodes/block)
  • Rest of disk contains file data (and spare blocks for “bad block” handling)

Free Space management

  • Up to 99 blocks, referenced directly in the superblock.
  • 1 block as the head of a linked list of blocks containing addresses of other free blocks (pictures coming).

超级块

struct filsys {
int s_isize; /* size in blocks of the I list */
int s_fsize; /* size in blocks of the entire volume */
int s_nfree; /* number of in core free blocks
! ! (between 0 and 100) */
int s_free[100]; /* in core free blocks */
int s_ninode; /* number of in core I nodes (0-100) */
int s_inode[100]; /* in core free I nodes */
char s_flock; /* lock during free list manipulation */
char s_ilock; /* lock during I list manipulation */
char s_fmod; /* super block modified flag */
char s_ronly; /* superb lock modified flag */
int s_time[2]; /* current date of last update */
int pad[50];
}

从磁盘超级块中读取没有任何问题,因为磁盘空间分配也是要以超级块中的内容为准,比如可用的block数,可用的inode数。

df命令本身没有任何问题。

是不是磁盘缓存的问题(这个不要讲了,肯定不是这个的原因)

在test集群测试,可以sync; echo 3 > /proc/sys/vm/drop_caches freeing the pagecache, dentries and inodes。发现确实会减小磁盘使用率,下降了0.22%(此时test集群的服务器磁盘写入速度不快),但是停止kafka进程却会使得磁盘使用率下降2%左右并非完全是磁盘缓存的原因。

[qiaojunlong@mq2501v ~]$ sysctl -a | grep dirty
vm.dirty_background_ratio = 10
vm.dirty_background_bytes = 0
vm.dirty_ratio = 20
vm.dirty_bytes = 0
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 3000

可以看到pdflush/flush/kdmflush将脏页刷入磁盘之前,脏页最多达到内存的10%,按照rtest机器的内存,就是400M左右,400M/300G也就是

是不是磁盘碎片的问题呢,使用filefrag查看(这个也不可能,linux又不会定期整理磁盘碎片)

ncdu

使用du -s –apparent-size

重启kafka进程之前,磁盘占用率68%

使用ncdu查看

Total disk usage:   1.0TiB  Apparent size: 856.1GiB  Items: 1874

重启kafka进程之后,磁盘占用率59%

Total disk usage: 911.7GiB  Apparent size: 854.2GiB  Items: 1872

##

此时我们注意到一个现象,并非所有集群的机器都有此问题,对比后发现,用于保存数据的磁盘文件系统不一样,出现此问题的磁盘文件系统为xfs,那么是不是文件系统的原因呢?

查了XFS的相关资料,其官网FAQ(最后五个问题及其答案)印证了我的猜测:

Q: Why do files on XFS use more data blocks than expected?

在缓冲写入工作中,XFS预分配算法分配超出文件结尾(EOF)额外的块,减少文件碎片。受益于这种行为的工作,包括缓慢增长的文件、并发写和混合读/写。还能防止碎片化,防止脏数据的足够缓冲以形成大量数据连续的区域。 后EOF块分配与EOF内的块一样占用空间。算在st_blocks中,也就是说通过stat()系统调用看到的占用空间包括这一块。由统计计数()系统调用可见,会占用全局分配空间以及相关文件的配额。这块空间被很多系统工具计算在内,如stat、du、df、ls,因此会使系统管理员困惑。但是不用太担心,后EOF块是暂时存在的,在XFS中会通过几种机制回收 看到投机预分配的FAQ条目的详细信息。

Q: What is speculative preallocation?

XFS基于文件未来的扩展写入,对文件扩展写入预先分配后EOF块。预分配的大小是动态的,取决于文件和fs的运行状态。一般来说,对于非常小文件预分配是禁用的,预分配大小随着文件增大而越来越大。 预分配的大小受限于文件系统所支持的最大extent。在文件系统磁盘空间接近满或者达到文件的其它分配限制(如quota)时,会自动减小预分配的大小。 在大多数情况下,当一个文件被关闭时,预分配的空间会被自动回收。应用程序重复触发预分配和回收周期(例如,在文件服务器或日志文件程序中很常见)可能会导致碎片。因此,当检测到这种模式时,会使预分配持续到超出文件描述符的生命周期。

Q: How can I speed up or avoid delayed removal of speculative preallocation?

Linux 3.8或更高版本包含一个扫描器,会在后台执行文件整理。扫描器会绕过脏文件,以避免和正在进行的写操作冲突。默认5分钟扫描一次,可以通过proc文件来调整 /proc/sys/fs/xfs/speculative_prealloc_lifetime

Q: Is speculative preallocation permanent?

一般情况下,预分配的空间会在文件关闭、回收inode、unmount、后台写操作下降时被回收,也可以永久化,通过fallocate或类似接口。文件大小超出后EOF块(例如,extent截断)或者crash。在crash的情况下,内存中用于跟踪和回收预分配空间的数据丢失。

Q: My workload has known characteristics - can I disable speculative preallocation or tune it to an optimal fixed size?

** 预分配机制不能被关闭 **,但是XFS提供了一种方式可以将其调整为固定大小,在mount时加入选项’allocsize=’,这样mount以后的XFS文件系统不会动态改变预分配大小,当然,这可能会导致磁盘碎片的增加。 使用’allocate=64K’可以使文件系统恢复动态的预分配大小。

结论

后记

kafka官方使用的文件系统是Ext4

kafka官方文档只提到了两个文件系统,Ext4和XFS

Ext4 may or may not be the best filesystem for Kafka. Filesystems like XFS supposedly handle locking during fsync better. We have only tried Ext4, though.

XFS

特性

Full 64-bit file capabilities (files larger than 2 GB)

Rapid and reliable recovery after system crashes because of journaling technology

Efficient support of large, sparse files (files with “holes”)

Integrated, full-function volume manager support

Extremely high I/O performance that scales well on multiprocessing systems

User-specified filesystem block sizes ranging from 512 bytes up to a maximum of the filesystem page size

空间管理

不同文件系统的介绍

GNU/Linux下的常用文件系统:ext2、ext3、ext4、XFS、JFS等,除了ext2,其它都是日志型文件系统。日志文件系统会用一些额外的磁盘空间记录磁盘数据状态,在非正常关机以后,无需扫描整个磁盘来恢复到正常状态。

ext2:曾经在ubuntu10.04下用过ext2,掉电以后启动就要检查很长时间,而且莫名其妙会丢失一些文件。 ext3:ext2的升级版,增加了日志文件系统,但磁盘使用率不高(93%左右),格式化与创建文件系统时间是其它文件系统的数十倍。 ext4: ReiserFS(性能高):专长是处理小文件,处理大文件的性能也在中上。 XFS(性能高): JFS(CPU占用率低):出自IBM,为AIX服务器设计的,特点是稳定,CPU占用率低。

centos7已经用XFS作为默认的文件系统了,XFS有哪些特性

  • xfs_freeze, snapshot, xfs_unfreeze, guarantees that the snapshot isn’t corrupted by in-air writes. xfs_freeze 工具冻结文件系统的I/O,然后等待卷管理器完成实际的快照创建,再解冻I/O,继续正常的操作。之后这个快照可以被当作备份,以只读方式挂载。

  • filesystem dump while the filesystem is still active

xfsdump xfsrestore

Xfsdump can be extremely powerful, allowing for incremental backups, file exclusions, and even the ability to suspend and resume in-progress dumps.

  • no fsck, ext4 does

装了XFS的系统一般会有下列命令

[qiaojunlong@w-app05 ~]$ xfs_
xfs_admin      xfs_copy       xfs_freeze     xfs_info       xfs_mdrestore  xfs_ncheck     xfs_rtcp
xfs_bmap       xfs_db         xfs_fsr        xfs_io         xfs_metadump   xfs_quota
xfs_check      xfs_estimate   xfs_growfs     xfs_logprint   xfs_mkfile     xfs_repair

相关资料

  • 维基百科上各种文件系统的比较(非常全面):http://en.wikipedia.org/wiki/Comparison_of_file_systems
  • man xfs
  • http://xfs.org/docs/xfsdocs-xml-dev/XFS_User_Guide/tmp/en-US/html/index.html
  • http://xfs.9218.n7.nabble.com/understanding-speculative-preallocation-td35003.html
  • https://kafka.apache.org/08/ops.html
  • coreutils源码:http://fossies.org/dox/coreutils-8.23/fsusage_8c_source.html

磁盘空间去哪了

问题背景

QBus是一套分布式消息队列系统,具有高性能,高吞吐,可持久化的特点,并且支持订阅功能。可持久化就意味着需要将消息保存在服务器的磁盘中。近期,QBus多个集群因磁盘空间满报警,比如rzwt集群的一台机器,使用df -h看到数据文件所在磁盘空间使用率确实超过了87%,但是重启其QBus进程以后,磁盘空间使用率回落到63%。

PS:QBus服务器会定期扫描数据文件,并对过期的文件进行删除,默认扫描周期为10分钟。

image

问题追查

QBus服务端进程

是kafka后台线程删除文件以后,用于存取数据的线程还未关闭文件吗?

在kafka运行时用lsof -p输出进程打开的文件列表与ls打出的一样,排除这种可能性

是QBus进程退出时做了又进行了一次删除过期文件的操作吗?

通过重启前后数据目录下总文件数的对比,文件总数和文件名一样时,磁盘空间也会回落,而且kafka的删除操作是针对整个文件的,不会存在删除文件部分数据的可能性,所以文件个数和名称一致即代表没有进行删除操作。

磁盘空间检测工具

是不是df工具本身的问题呢

df源码

于是下载了 coreutils的源码(df属于这个包),可以看到是取磁盘超级块的内容

coreutils 8.23 fsusage.c

  173 #elif defined STAT_READ_FILSYS          /* SVR2 */
  174 # ifndef SUPERBOFF
  175 #  define SUPERBOFF (SUPERB * 512)
  176 # endif
  177 
  178   struct filsys fsd;
  179   int fd;
  180 
  181   if (! disk)
  182     {
  183       errno = 0;
  184       return -1;
  185     }
  186 
  187   fd = open (disk, O_RDONLY);
  188   if (fd < 0)
  189     return -1;
  190   lseek (fd, (off_t) SUPERBOFF, 0);
  191   if (full_read (fd, (char *) &fsd, sizeof fsd) != sizeof fsd)
  192     {
  193       close (fd);
  194       return -1;
  195     }
  196   close (fd);
  197 
  198   fsp->fsu_blocksize = (fsd.s_type == Fs2b ? 1024 : 512);
  199   fsp->fsu_blocks = PROPAGATE_ALL_ONES (fsd.s_fsize);
  200   fsp->fsu_bfree = PROPAGATE_ALL_ONES (fsd.s_tfree);
  201   fsp->fsu_bavail = PROPAGATE_TOP_BIT (fsd.s_tfree);
  202   fsp->fsu_bavail_top_bit_set = EXTRACT_TOP_BIT (fsd.s_tfree) != 0;
  203   fsp->fsu_files = (fsd.s_isize == -1
  204                     ? UINTMAX_MAX
  205                     : (fsd.s_isize - 2) * INOPB * (fsd.s_type == Fs2b ? 2 : 1));
  206   fsp->fsu_ffree = PROPAGATE_ALL_ONES (fsd.s_tinode);

磁盘空间的划分

Disk is divided up into 512-byte blocks

  • Block 0: boot block
  • Block 1: superblock (struct filsys)
  • Blocks 2 – f(Ninodes): inodes (16 inodes/block)
  • Rest of disk contains file data (and spare blocks for “bad block” handling)

Free Space management

  • Up to 99 blocks, referenced directly in the superblock.
  • 1 block as the head of a linked list of blocks containing addresses of other free blocks (pictures coming).

超级块

struct filsys {
int s_isize; /* size in blocks of the I list */
int s_fsize; /* size in blocks of the entire volume */
int s_nfree; /* number of in core free blocks
! ! (between 0 and 100) */
int s_free[100]; /* in core free blocks */
int s_ninode; /* number of in core I nodes (0-100) */
int s_inode[100]; /* in core free I nodes */
char s_flock; /* lock during free list manipulation */
char s_ilock; /* lock during I list manipulation */
char s_fmod; /* super block modified flag */
char s_ronly; /* superb lock modified flag */
int s_time[2]; /* current date of last update */
int pad[50];
}

从磁盘超级块中读取没有任何问题,因为磁盘空间分配也是要以超级块中的内容为准,比如可用的block数,可用的inode数。

df命令本身没有任何问题。

是不是磁盘缓存的问题(这个不要讲了,肯定不是这个的原因)

在test集群测试,可以sync; echo 3 > /proc/sys/vm/drop_caches freeing the pagecache, dentries and inodes。发现确实会减小磁盘使用率,下降了0.22%(此时test集群的服务器磁盘写入速度不快),但是停止kafka进程却会使得磁盘使用率下降2%左右并非完全是磁盘缓存的原因。

[qiaojunlong@mq2501v ~]$ sysctl -a | grep dirty
vm.dirty_background_ratio = 10
vm.dirty_background_bytes = 0
vm.dirty_ratio = 20
vm.dirty_bytes = 0
vm.dirty_writeback_centisecs = 500
vm.dirty_expire_centisecs = 3000

可以看到pdflush/flush/kdmflush将脏页刷入磁盘之前,脏页最多达到内存的10%,按照rtest机器的内存,就是400M左右,400M/300G也就是

是不是磁盘碎片的问题呢,使用filefrag查看(这个也不可能,linux又不会定期整理磁盘碎片)

ncdu

使用du -s –apparent-size

重启kafka进程之前,磁盘占用率68%

使用ncdu查看

Total disk usage:   1.0TiB  Apparent size: 856.1GiB  Items: 1874

重启kafka进程之后,磁盘占用率59%

Total disk usage: 911.7GiB  Apparent size: 854.2GiB  Items: 1872

##

此时我们注意到一个现象,并非所有集群的机器都有此问题,对比后发现,用于保存数据的磁盘文件系统不一样,出现此问题的磁盘文件系统为xfs,那么是不是文件系统的原因呢?

查了XFS的相关资料,其官网FAQ(最后五个问题及其答案)印证了我的猜测:

Q: Why do files on XFS use more data blocks than expected?

在缓冲写入工作中,XFS预分配算法分配超出文件结尾(EOF)额外的块,减少文件碎片。受益于这种行为的工作,包括缓慢增长的文件、并发写和混合读/写。还能防止碎片化,防止脏数据的足够缓冲以形成大量数据连续的区域。 后EOF块分配与EOF内的块一样占用空间。算在st_blocks中,也就是说通过stat()系统调用看到的占用空间包括这一块。由统计计数()系统调用可见,会占用全局分配空间以及相关文件的配额。这块空间被很多系统工具计算在内,如stat、du、df、ls,因此会使系统管理员困惑。但是不用太担心,后EOF块是暂时存在的,在XFS中会通过几种机制回收 看到投机预分配的FAQ条目的详细信息。

Q: What is speculative preallocation?

XFS基于文件未来的扩展写入,对文件扩展写入预先分配后EOF块。预分配的大小是动态的,取决于文件和fs的运行状态。一般来说,对于非常小文件预分配是禁用的,预分配大小随着文件增大而越来越大。 预分配的大小受限于文件系统所支持的最大extent。在文件系统磁盘空间接近满或者达到文件的其它分配限制(如quota)时,会自动减小预分配的大小。 在大多数情况下,当一个文件被关闭时,预分配的空间会被自动回收。应用程序重复触发预分配和回收周期(例如,在文件服务器或日志文件程序中很常见)可能会导致碎片。因此,当检测到这种模式时,会使预分配持续到超出文件描述符的生命周期。

Q: How can I speed up or avoid delayed removal of speculative preallocation?

Linux 3.8或更高版本包含一个扫描器,会在后台执行文件整理。扫描器会绕过脏文件,以避免和正在进行的写操作冲突。默认5分钟扫描一次,可以通过proc文件来调整 /proc/sys/fs/xfs/speculative_prealloc_lifetime

Q: Is speculative preallocation permanent?

一般情况下,预分配的空间会在文件关闭、回收inode、unmount、后台写操作下降时被回收,也可以永久化,通过fallocate或类似接口。文件大小超出后EOF块(例如,extent截断)或者crash。在crash的情况下,内存中用于跟踪和回收预分配空间的数据丢失。

Q: My workload has known characteristics - can I disable speculative preallocation or tune it to an optimal fixed size?

** 预分配机制不能被关闭 **,但是XFS提供了一种方式可以将其调整为固定大小,在mount时加入选项’allocsize=’,这样mount以后的XFS文件系统不会动态改变预分配大小,当然,这可能会导致磁盘碎片的增加。 使用’allocate=64K’可以使文件系统恢复动态的预分配大小。

结论

后记