从稀疏文件中删除前导空块

从稀疏文件中删除前导空块

我使用logrotatecopytruncate选项。效果很好,可以创建一个稀疏文件,该文件以越来越多的“虚拟”空块开始,这些块不占用磁盘空间。

问题在于复制的文件:虽然它们占用的磁盘空间很小,但尝试使用它们检查它们less却需要很长时间,因为“虚拟”空块会扩展为实际空块。我真的很想从复制文件的开头消除初始稀疏空块。

这是我目前所知道的: ls -ls并且du可以告诉我文件中有多少是“真实的”。我认为dd可以用来制作没有前导空块的副本。但我无法将它们组合成可以放入文件postrotate部分的内容logrotate.conf

我发现了一些使用trsed删除空值的方法,但这需要扩展文件(使虚拟空值变为物理空值),而且随着时间的推移,文件可能会增长到超过 1TB!我需要一种更“精准”的方法,无需扩展文件即可工作。它应该只需要处理 inode,因为稀疏块就在那里(而不是实际分配的区域)。

当然,“真正的”解决办法是让生成程序SIGHUP重新打开其输出文件,但在这种情况下这是不可能的。

直接从稀疏文件中删除前导空块的最简单和最快的方法是什么?


附录:下面介绍如何制作您自己的稀疏文件:

$ dd if=/dev/zero of=sparse.txt bs=1 count=0 seek=8G
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000226785 s, 0.0 kB/s

$ echo 'Hello, World!' >>sparse.txt

$ ls -ls sparse.txt
4 -rwxrwxrwx 1 me me 8589934606 Nov  6 10:20 sparse.txt

$ ls -lsh sparse.txt 
4.0K -rwxrwxrwx 1 me me 8.1G Nov  6 10:20 sparse.txt

这个“巨大”的文件几乎不占用磁盘空间。现在试试less sparse.txt。您必须浏览 8G 的空值才能找到末尾的字符。甚至tail -n 1 sparse.txt需要很长时间。

答案1

首先我要感谢你们二位。

我确实使用以下 logrotate 配置测试了 @Michael 的解决方案,它按预期工作。但我们仍然有主日志文件和稀疏文件。

  access_logs.conf: |
    /var/log/access_v2.log {
        daily
        missingok
        rotate 7
        compress
        delaycompress
        dateformat -%Y%m%d_%H%M%S
        create
        su root root
        firstaction
            rm -rf /var/log/access_v2.log
            sh /etc/logrotate-truncat/truncat.sh /var/log/access.log >> /var/log/access_v2.log
            > /var/log/access.log
         endscript
    }

这是对此的另一种看法。如果我们删除有问题的日志文件(此处/var/log/access.log)后可以重新创建它,那么我们可以在firstaction部分中明确实现 copytrucate 所做的事情(复制文件然后截断它),但采用 copydelete 方式(复制文件然后删除它)。该文件/var/log/access.log将自动创建,并且它不是稀疏文件。因此,无需清理稀疏文件。

样本

  access_logs.conf: |
    /var/log/access_v2.log {
        daily
        missingok
        rotate 7
        compress
        delaycompress
        dateformat -%Y%m%d_%H%M%S
        create
        su root root
        firstaction
            rm -rf /var/log/access_v2.log
            cp /var/log/access.log /var/log/access_v2.log
            rm -rf /var/log/access.log
         endscript
    }

答案2

这是我第一次似乎有效的尝试,使用statdd,效果很好仅有的对于前导稀疏文件:

#! /bin/bash
for f in $@; do
  echo -n "$f : "
  fields=( `stat -c "%o %B %b %s" $f` )
  xfer_block_size=${fields[0]}
  alloc_block_size=${fields[1]}
  blocks_alloc=${fields[2]}
  size_bytes=${fields[3]}

  bytes_alloc=$(( $blocks_alloc * $alloc_block_size ))

  alloc_in_xfer_blocks=$(( ($bytes_alloc + ($xfer_block_size - 1))/$xfer_block_size ))
  size_in_xfer_blocks=$(( ($size_bytes + ($xfer_block_size - 1))/$xfer_block_size ))
  null_xfer_blocks=$(( $size_in_xfer_blocks - $alloc_in_xfer_blocks ))
  null_xfer_bytes=$(( $null_xfer_blocks * $xfer_block_size ))
  non_null_bytes=$(( $size_bytes - $null_xfer_bytes ))

  if [ "$non_null_bytes" -gt "0" -a "$non_null_bytes" -lt "$size_bytes" ]; then
    cmd="dd if=$f of=$f.new bs=1 skip=$null_xfer_bytes count=$non_null_bytes"
    echo $cmd
    exec $cmd
  else
    echo "Nothing to do: File is not sparse."
  fi
done

你怎么认为?

答案3

我在这里创建了一个帐户,以便感谢@BobC 的回答(和他的问题)。这是我解决长期存在的 Solr 日志问题所需要的催化剂。

我修改了Bobc的脚本,以将其优化为Logrotate用例(使用$xfer_block_sizeforibs和任意大(8m)obs,然后是atr -d "\000"以消除其余的空),然后在firstaction我的logrotate配置部分中使用它。

我猜我的解决方案有点不合常理,但它比在 80+ GB 的日志文件威胁到填满磁盘时必须弹出关键生产服务要好得多……

这就是我最终得到的结果:

#! /bin/bash
# truncat.sh
# Adapted from @BobC's script http://superuser.com/a/836950/539429
#
# Efficiently cat log files that have been previously truncated.  
# They are sparse -- many null blocks before the interesting content.
# This script skips the null blocks in bulk (except for the last) 
# and then uses tr to filter the remaining nulls.
#
for f in $@; do
  fields=( `stat -c "%o %B %b %s" $f` )
  xfer_block_size=${fields[0]}
  alloc_block_size=${fields[1]}
  blocks_alloc=${fields[2]}
  size_bytes=${fields[3]}

  bytes_alloc=$(( $blocks_alloc * $alloc_block_size ))

  alloc_in_xfer_blocks=$(( ($bytes_alloc + ($xfer_block_size - 1))/$xfer_block_size ))
  size_in_xfer_blocks=$(( ($size_bytes + ($xfer_block_size - 1))/$xfer_block_size ))
  null_xfer_blocks=$(( $size_in_xfer_blocks - $alloc_in_xfer_blocks ))
  null_xfer_bytes=$(( $null_xfer_blocks * $xfer_block_size ))
  non_null_bytes=$(( $size_bytes - $null_xfer_bytes ))

  if [ "$non_null_bytes" -gt "0" -a "$non_null_bytes" -lt "$size_bytes" ]; then
    cmd="dd if=$f ibs=$xfer_block_size obs=8M skip=$null_xfer_blocks "
    $cmd | tr -d "\000"
  else
    cat $f
  fi
done

使用更大的块可以使dd速度快几个数量级。 dd首先进行切割,然后tr修剪其余的空值。作为参考,对于 87 GiB 稀疏文件(包含 392 MiB 数据):

# ls -l 2015_10_12-025600113.start.log
-rw-r--r-- 1 solr solr 93153627360 Dec 31 10:34 2015_10_12-025600113.start.log
# du -shx 2015_10_12-025600113.start.log
392M    2015_10_12-025600113.start.log
#
# time truncat.sh 2015_10_12-025600113.start.log > test1
93275+1 records in
45+1 records out
382055799 bytes (382 MB) copied, 1.53881 seconds, 248 MB/s

real    0m1.545s
user    0m0.677s
sys 0m1.076s

# time cp --sparse=always 2015_10_12-025600113.start.log test2

real    1m37.057s
user    0m8.309s
sys 1m18.926s

# ls -l test1 test2
-rw-r--r-- 1 root root   381670701 Dec 31 10:07 test1
-rw-r--r-- 1 root root 93129872210 Dec 31 10:11 test2
# du -shx test1 test2
365M    test1
369M    test2

当我logrotate使用来处理这个问题时copytruncate,它花了一个多小时,并产生了一个完全实现的非稀疏文件 - 然后花了一个多小时来gzip

这是我的最终logrotate解决方案:

/var/log/solr/rotated.start.log {
    rotate 14
    daily
    missingok
    dateext
    compress
    create
    firstaction
        # this actually does the rotation.  At this point we expect 
        # an empty rotated.start.log file.
        rm -f /var/log/solr/rotated.start.log
        # Now, cat the contents of the log file (skipping leading nulls) 
        # onto the new rotated.start.log
        for i in /var/log/solr/20[0-9][0-9]_*.start.log ; do
           /usr/local/bin/truncat.sh $i >> /var/log/solr/rotated.start.log
           > $i  # truncate the real log
        done
     endscript
}

棘手的是,当您第一次设置它时,您必须创建一个空rotated.start.log文件,否则logrotate将永远不会选择并运行firstaction脚本。

我确实看到了您的logrotate错误单为此修复发布于logrotate 3.9.0。不幸的是,如果我没有看错的话,实施的修复程序只能解决部分问题。它正确地复制了稀疏日志文件以创建另一个稀疏文件。但正如您所观察到的,这并不是我们真正想要的;我们希望副本排除所有不相关的空块并仅保留日志条目。之后copytruncatelogrotate仍然必须保留gzip文件,并且gzip不能有效地处理稀疏文件(它读取并处理每个空字节。)

copytruncate我们的解决方案比修复更好,logrotate 3.9.x因为它可以生成可以轻松压缩的干净日志。

相关内容