ZFS管理手册:第四章ARC

本文深入探讨了ZFS的可调式替换缓存(ARC)机制,解析了其如何结合最近最常使用(MRU)和最常使用(MFU)缓存算法以提高性能,并介绍了二级ARC(L2ARC)的应用及配置。

ZFS管理手册:第四章ARC

我们在ZFS管理系列中继续介绍另一个zpool VDEV,称为Adjustable Replacement Cache,简称ARC。我们在上一篇文章中讨论了ZFS Intent Log(ZIL)和单独的Intent Logging Device(SLOG)。

传统缓存

Linux和其他操作系统上的缓存机制为最近最少使用Least Recently Used的缓存算法。LRU算法的工作方式是,当应用程序读取数据块时,它们被放入缓存中。随着越来越多的数据被读取并放入高速缓存,高速缓存将被填满。但是,缓存是FIFO(先进先出)算法。因此,当高速缓存已满时,较旧的页面将被从cache中清除。即使那些较旧的页面被更频繁地访问。把整个过程想象成一条传送带。数据块被放入缓存中最近使用的位置。随着读取更多的数据块,会将较旧的数据块推送到cache中最近最少使用的位置上,直到它们从传送带上掉下来,或者换句话说,它们会被从缓冲中清理出去。
LRU
上图显示了传统的LRU缓存方案。

当从磁盘读取大量顺序读取并将其放入缓存时,即使这些数据只需要一次,但也有从缓存中清除更为频繁请求的内容的趋势。因此,从缓存的角度来看,它最终会得到许多不再需要的毫无价值的无用数据。当然,当请求更新的数据块时,它最终会被替换。

还存在最不常用least frequently used(LFU)缓存。但是,它们有一个问题,即如果读取较新的数据不够频繁,可能会将其从缓存中逐出。因此,有大量的磁盘请求,这从一开始就违背了缓存的目的。因此,显而易见的方法似乎是以某种方式将两者结合起来–同时拥有一个LRU和一个LFU。

ZFS ARC

ZFS adjustable replacement cache (ARC)是这样一种高速缓存机制,其高速缓存最近的块请求以及频繁的块请求。它是获得专利的IBM adaptive replacement cache的实现,并进行了一些修改和扩展。

术语

  • Adjustable Replacement Cache(ARC): 驻留在物理RAM中的缓存。它使用两个缓存组成–最常用的缓存和最近使用的缓存。高速缓存目录对缓存的指针进行索引,包括指向频繁使用的高速缓存和最近使用的高速缓存的磁盘的指针。
  • 缓存目录: 目录内保存MRU、MFU、Ghost MRU和Ghost MFU缓存的指针索引。
  • Most Recently Used(MRU)缓存: ARC最近使用的缓存。最近从文件系统请求的数据块缓存在此处。
  • Most Frequently Used(MFU)缓存: ARC最常用的缓存。文件系统中请求最频繁的数据块缓存在此处。
  • Ghost MRU: 将页面从MRU缓存收回到磁盘,以节省MRU中的空间。指针仍然跟踪被逐出的页面在磁盘上的位置。
  • Ghost MFU: 将页面从MFU缓存收回到磁盘,以节省MFU中的空间。指针仍然跟踪被逐出的页面在磁盘上的位置。
  • Level 2 Adjustable Replacement Cache(L2ARC): 驻留在物理内存外部的缓存,通常位于FAST SSD上。它是RAM ARC的物理扩展。

ARC算法

这是IBM ARC工作原理的简化版本,但它应该帮助您理解如何将优先级放在MRU和MFU上。首先,让我们假设您的缓存中有八个页面。缓存中的四个页面将用于MRU,四个页面将用于MFU。此外,还将有四个指针为幽灵MRU和四个指针为幽灵MFU。因此,缓存目录将引用16页实时缓存或收回的缓存。
arc1

  1. 正如预期的那样,当从文件系统读取块A时,它将被缓存在MRU中。缓存目录中的索引指针将引用MRU页。
    arc2
  2. 假设现在从文件系统读取了一个不同的块(块B)。它也将被缓存在MRU中,缓存目录中的索引指针将引用第二个MRU页。由于块B的读取时间比块A晚,因此它在MRU缓存中的优先级高于块A。现在,MRU缓存中有两个页面。
    arc3
  3. 现在,假设再次从文件系统读取块A。这将是块A的第二次读取。A被频繁地读取,因此它将被存储在MFU中。数据块必须至少读取两次才能存储在此处。此外,这也是最近的一个请求。所以该块不仅缓存在MFU中,而且还在缓存目录的MRU中被引用。因此,尽管两个页面驻留在缓存中,但缓存目录中有三个指针指向缓存中的两个块。
    acr4
  4. 最终,缓存被上述步骤填满,并且我们在缓存目录的MRU和MFU的指针也被设置满。
    acr5
  5. 这就是事情变得有趣的地方。假设我们现在需要从未缓存的文件系统中读取新的块。我们要缓存的页面比我们可以存储的页面多。因此,我们将需要从缓存中逐出一个页面。MRU中最旧的页面(称为最近最少使用的LRU)收到驱逐通知,并被Ghost MRU引用。新的页面现在将在MRU中可用于新读取的块。
    acr6
  6. 如预期的那样,在从文件系统读取新读取的块之后,它被存储在MRU中并相应地被引用。因此,我们有一个Ghost MRU页面引用和一个已填满的缓存。
    acr7
  7. 让我们假设从文件系统中重新读取最近被逐出的页面,这会给整个过程带来麻烦。因为Ghost MRU知道它最近被从缓存中逐出,所以我们将其称为“Ghost缓存命中”。因为ZFS知道它最近被缓存了,所以我们需要把它放回MRU缓存中;而不是MFU缓存中,因为它没有被Ghost MFU所引用。
    arc8
  8. 遗憾的是,我们的缓存太小,无法存储页面。因此,我们必须将MRU增加一页来存储新的幻影命中。然而,我们的缓存只有这么大,所以我们必须对MFU的大小进行调整,缩小一页,以便为MRU腾出空间。当然,该算法在MFU和 Ghost MFU上以类似的方式进行处理。Ghost MFU的幻影点击量将扩大MFU,缩小MRU,为新页面腾出空间。
    acr9

ZFS ARC扩展

前面的算法是IBM设计的ARC算法的简化版本。ZFS已经做了一些扩展,我将在这里说明一下:

  • ZFS ARC将占用可用RAM的1/2。然而,这并不是固定的。如果您的服务器中有32 GB的RAM,这并不意味着缓存大小固定为16 GB。相反,总缓存将根据内核决策调整其大小。如果内核需要更多的RAM来执行调度进程,则ZFS ARC将进行调整,以便为内核所需处理的任何内容腾出空间。然而,如果有足够的RAM,ZFS ARC则可以占用的这部份的空间。
  • ZFS ARC可以处理多种块大小,而IBM的实现只可以使用静态块大小。
  • 内存页可以锁定在MRU或MFU中,以防止被逐出。IBM实现没有此功能。因此,在选择何时从cache中清除内存页时,ZFS ARC算法稍微复杂一些。

L2ARC

Level 2 ARC 或者 L2ARC 应该部署在一个读写速度很快的硬盘上。正如我在上一篇关于ZIL的文章中提到的,L2ARC 应该也是部署在DRAM DIMM(不一定是电池供电的)、快速SSD,或者10k以上的企业级SAS或FC磁盘。如果您决定为您的ZI和您的L2ARC使用相同的设备(这当然是可以接受的),那么您应该对其进行分区,以便ZIL占用非常少的空间,比如512MB或1 GB,并将其余的空间以 RAID-0 L2ARC提供添加到池里。因为 L2ARC 中的数据不需要持久保存,因为它启动时缓存将被擦除。
hybird-pool
L2ARC是ARC在RAM中的扩展,当存在L2ARC时,以前的算法保持不变。这意味着随着MRU或MFU的增长,它们不会同时共享RAM中的ARC和SSD上的L2ARC,这将对性能产生巨大影响。取而代之的是,当页面即将被逐出时,遍历算法将把MRU和MFU页面逐出到8MB缓冲区中,该缓冲区稍后被设置为对L2ARC的原子写事务。这里的明显优势是从缓存中收回页面的延迟不会受到影响。此外,如果将大量数据块读取发送到高速缓存,则这些块在L2ARC遍历之前被逐出,而不是发送到L2ARC。这最大限度地减少了大规模顺序读取对L2ARC的污染。填充L2ARC也可能非常慢,也可能非常快,这取决于对数据的访问。

增加L2ARC

警告:某些主板在重新启动后会以不同方式向Linux内核注册磁盘。因此,在一次引导时标识为/dev/sda的磁盘在下一次引导时可能是/dev/sdb。对于存储数据的主池,这不是问题,因为ZFS可以根据元数据拓扑信息重建VDEV。然而,对于您的L2ARC和SLOG设备,不存在这样的元数据。所以与其通过它们的/dev/sdX将它们添加到池中,更应该使用/dev/disk/by-id/里的设备(实际上这是一个符号连接,指向/dev/sdX) ,把它添加到对应的Zpool中。如果您不注意此警告,您的slog设备可能根本没有添加到您的混合池中,您只能在后续把设备重新加入到池中。是否存在性能很好的SLOG,将很大程度的影响到应用程序的性能。

您可以使用“cache” VDEV添加L2ARC。建议您在L2ARC创建再RAID 0设备上,以便使用最多的空间和最快的速度。L2ARC不需要持久数据,因此它可以是易失性DIMM。假设我的池中有4个磁盘,以及两个60 GB的OCZ RevoDrive SSD。我将对SSD上的驱动器进行分区,将4 GB分配给zil,其余的分配给L2ARC。这就是如何将L2ARC添加到池中。在这里,我使用GNU parted首先创建分区,然后添加SSD。/dev/disk/by-id/中的设备指向/dev/sda和/dev/sdb。

# parted /dev/sda unit s mklabel gpt mkpart primary zfs 2048 4G mkpart primary zfs 4G 109418255
# parted /dev/sdb unit s mklabel gpt mkpart primary zfs 2048 4G mkpart primary zfs 4G 109418255
# zpool add tank cache \
/dev/disk/by-id/ata-OCZ-REVODRIVE_OCZ-33W9WE11E9X73Y41-part2 \
/dev/disk/by-id/ata-OCZ-REVODRIVE_OCZ-X5RG0EIY7MN7676K-part2 \
log mirror \
/dev/disk/by-id/ata-OCZ-REVODRIVE_OCZ-69ZO5475MT43KNTU-part1 \
/dev/disk/by-id/ata-OCZ-REVODRIVE_OCZ-9724MG8BII8G3255-part1
# zpool status tank
  pool: tank
 state: ONLINE
 scan: scrub repaired 0 in 1h8m with 0 errors on Sun Dec  2 01:08:26 2012
config:

        NAME                                              STATE     READ WRITE CKSUM
        tank                                              ONLINE       0     0     0
          raidz1-0                                        ONLINE       0     0     0
            sdd                                           ONLINE       0     0     0
            sde                                           ONLINE       0     0     0
            sdf                                           ONLINE       0     0     0
            sdg                                           ONLINE       0     0     0
        logs
          mirror-1                                        ONLINE       0     0     0
            ata-OCZ-REVODRIVE_OCZ-69ZO5475MT43KNTU-part1  ONLINE       0     0     0
            ata-OCZ-REVODRIVE_OCZ-9724MG8BII8G3255-part1  ONLINE       0     0     0
        cache
          ata-OCZ-REVODRIVE_OCZ-69ZO5475MT43KNTU-part2    ONLINE       0     0     0
          ata-OCZ-REVODRIVE_OCZ-9724MG8BII8G3255-part2    ONLINE       0     0     0

errors: No known data errors

此外,我可以随时检查L2ARC的大小:

#zpool iostat -v
                                                     capacity     operations    bandwidth
pool                                              alloc   free   read  write   read  write
------------------------------------------------  -----  -----  -----  -----  -----  -----
pool                                               824G  2.82T     11     60   862K  1.05M
  raidz1                                           824G  2.82T     11     52   862K   972K
    sdd                                               -      -      5     29   289K   329K
    sde                                               -      -      5     28   289K   327K
    sdf                                               -      -      5     29   289K   329K
    sdg                                               -      -      7     35   289K   326K
logs                                                  -      -      -      -      -      -
  mirror                                          1.38M  3.72G      0     19      0   277K
    ata-OCZ-REVODRIVE_OCZ-69ZO5475MT43KNTU-part1      -      -      0     19      0   277K
    ata-OCZ-REVODRIVE_OCZ-9724MG8BII8G3255-part1      -      -      0     19      0   277K
cache                                                 -      -      -      -      -      -
  ata-OCZ-REVODRIVE_OCZ-69ZO5475MT43KNTU-part2    2.34G  49.8G      0      0    739  4.32K
  ata-OCZ-REVODRIVE_OCZ-9724MG8BII8G3255-part2    2.23G  49.9G      0      0    801  4.11K
------------------------------------------------  -----  -----  -----  -----  -----  -----

在本例中,我得L2ARC中保存了大约5 GB的缓存数据(请记住,它是基于RAID 0的),但还有足够的缓存空间可用。事实上,上面的粘贴来自一个拥有32 GB DDR2内存的实时运行系统,该系统目前托管着这个博客。L2ARC在一周前进行了修改和重新添加。这向您显示了我在系统上填充L2ARC的速度。你的情况可能有所不同,但这并不令人惊讶。Ben Rockwood有一个Perl脚本,它可以根据MRU、mfu、ghost以及其他统计数据来分解ARC和L2ARC。在http://www.cuddletech.com/blog/pivot/entry.php?id=979上查看(我没有任何使用该脚本的经验)。

总结

ZFS ARC 改进了IBM最初的 ARC,同时保持了IBM的设计。然而,与Linux内核和其他操作系统部署的传统LRU和LFU缓存相比,ZFS ARC具有巨大的优势。而且,通过在快速固态硬盘或磁盘上添加L2ARC,我们可以快速检索大量数据,同时仍允许主机内核根据需要调整内存要求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值