在 Solaris 11.3 上启动 ZFS L2ARC 缓存

在 Solaris 11.3 上启动 ZFS L2ARC 缓存

有没有一个好的方法可以在 Solaris 11.3 上启动 ZFS L2ARC 缓存?

L2ARC 的设计目的是忽略已从文件中顺序读取的块。这对于正在进行的操作很有意义,但很难为初始预热或基准测试准备好缓存。

此外,高度碎片化的文件可能会从 L2ARC 中缓存的顺序读取中受益匪浅(因为在磁盘上它们是随机读取),但使用当前的启发式方法,即使 L2ARC 只占 10%,这些文件也永远不会被缓存。

在 Solaris 10 和 11 的先前版本中,我成功地dd对每个文件连续使用了两次。第一次dd将文件读入 ARC,第二次dd似乎触发了缓冲区,因此它们有资格进行 L2ARC 缓存。同样的技术似乎在 Solaris 11.3 中不起作用。

我已经确认有问题的文件有 8k 记录大小,我已经尝试设置zfs_prefetch_disable,但这对 L2ARC 行为没有影响更新:zfs_prefetch_disable结果很重要,请参阅下面的回答。

如果没有好的方法,我会考虑使用一个可以对文件 100% 进行随机读取的工具。考虑到缓存在 11.3 中是持久的,这可能是值得的。有这样的工具吗?

答案1

经过一些实验,我发现了四种可能的解决方案。

对于每种方法,您都需要执行这些步骤,然后继续读取更多数据以填充 ZFS ARC 缓存并触发从 ARC 到 L2ARC 的馈送。请注意,如果数据已缓存在内存中,或者每个块在磁盘上的压缩大小大于 32kB,则这些方法通常不会执行任何操作。

1. 设置记录的内核标志zfs_prefetch_disable

L2ARC 默认拒绝缓存已自动预取的数据。我们可以通过禁用 ZFS 预取功能来绕过此问题。无论如何,此标志对于数据库工作负载来说通常是一个好主意。

echo "zfs_prefetch_disable/W0t1" | mdb -kw

..或者要永久设置它,请将以下内容添加到/etc/system

set zfs:zfs_prefetch_disable = 1

现在,当使用 读取文件时dd,它们仍然符合 L2ARC 的资格。

从操作上讲,在我的测试中,这一变化还改善了读取行为。通常,当 ZFS 检测到顺序读取时,它会在数据 vdev 和缓存 vdev 之间平衡吞吐量,而不是只从缓存中读取 - 但如果缓存设备的延迟明显低于数据设备或吞吐量明显高于数据设备,这会损害性能。

2. 重新写入数据

当数据写入 ZFS 文件系统时,它会缓存在 ARC 中,并且(如果符合块大小标准)可以输入到 L2ARC 中。重写数据并不总是那么容易,但有些应用程序和数据库可以实时执行此操作,例如通过应用程序级文件镜像或移动数据文件。

问题:

  • 根据应用程序的不同,并不总是可行的。
  • 如果正在使用快照,则会消耗额外的空间。
  • (但从好的方面来看,生成的文件已经经过碎片整理了。)

3. 取消设置未记录的内核标志l2arc_noprefetch

这是基于阅读 OpenSolaris 源代码得出的结论,毫无疑问是完全不受支持的。使用时请自担风险。

  1. 禁用l2arc_noprefetch标志:

    echo "l2arc_noprefetch/W0" | mdb -kw
    

    当此标志被禁用时,读入 ARC 的数据将符合 L2ARC 的条件,即使是连续读取(只要磁盘上的块最多为 32k)。

  2. 从磁盘读取文件:

    dd if=filename.bin of=/dev/null bs=1024k
    
  3. 重新启用l2arc_noprefetch标志:

    echo "l2arc_noprefetch/W1" | mdb -kw
    

4.随机读取数据

我编写了一个 Perl 脚本,以伪随机方式(基于 Perl 哈希的顺序)读取 8kB 块中的文件。它也可能适用于更大的块,但我尚未测试过。

#!/usr/bin/perl -W

my $BLOCK_SIZE = 8*2**10;
my $MAX_ERRS = 5;

foreach my $file (@ARGV) {
        print "Reading $file...\n";
        my $size;
        unless($size = (stat($file))[7]) {print STDERR "Unable to stat file $file.\n"; next; }
        unless(open(FILE, "<$file")) {print STDERR "Unable to open file $file.\n"; next; }
        my $buf;
        my %blocks;
        for(my $i=0;$i<$size/$BLOCK_SIZE;$i++) { $blocks{"$i"} = 0; }
        my $errs = 0;
        foreach my $block (keys %blocks) {
                unless(sysseek(FILE, $block*$BLOCK_SIZE, 0) && sysread(FILE, $buf, $BLOCK_SIZE)) {
                        print STDERR "Error reading $BLOCK_SIZE bytes from offset " . $block * $BLOCK_SIZE . "\n";
                        if(++$errs == $MAX_ERRS) { print STDERR "Giving up on this file.\n"; last; }
                        next;
                }
        }
        close(FILE);
}

问题:

  • 这会花费很长时间并且会给磁盘带来繁重的负担。

剩余问题

  • 上述方法会将数据放入主内存,适合输入到 L2ARC,但它们不会触发输入。我知道触发写入 L2ARC 的唯一方法是继续读取数据以对 ARC 施加压力。
  • 在带有 SRU 1.3.9.4.0 的 Solaris 11.3 上,L2ARC 很少会增长到预期的全部量。evict_l2_eligible即使 SSD 设备没有压力,kstat 也会增加,这表明数据正在被丢弃。剩余的未缓存数据对性能的影响不成比例。

答案2

我建议使用真实的工作量并用 监控结果arcstat

就像是:

arcstat.py -f "time,read,l2read,hit%,hits,miss%,miss,l2hit%,l2miss%,arcsz,c,l2size" 1

我认为没有必要“填充”缓存。如果您的工作负载没有自然填充缓存,那么它就不是具有代表性的基准测试工作负载,对吗?

也许你有一个特殊的用例(您的数据集大小、ARC 大小和工作集大小是多少?)但总体来说,对 L2ARC 的关注被过分强调了。

相关内容