闲话缓存:ZFS 读缓存深入研究-ARC(一)

摘要:
然而,ZFS开发人员对IBM的ARC算法进行了一些扩展,以更适合ZFS应用场景。ZFSARC的最早实施在FAST 2003会议上介绍,并在杂志的一篇文章中详细描述;登录名:。在下面的描述中,我将尝试简化一些机制,使您更容易理解ZFSARC的工作原理。缓存在某些文件系统缓存中实现的标准LRU消除算法有一些缺点。在这种情况下,LRU链接列表的长度将增加1。使用这种行为,ARC可以使自己适应工作负载。ARC缓存的行为不同。

在Solaris ZFS 中实现的ARC(Adjustable Replacement Cache)读缓存淘汰算法真是很有意义的一块软件代码。它是基于IBM的Megiddo和Modha提出的ARC(Adaptive Replacement Cache)淘汰算法演化而来的。但是ZFS的开发者们对IBM 的ARC算法做了一些扩展,以更适用于ZFS的应用场景。ZFS ARC的最早实现展现在FAST 2003的会议上,并在杂志《;Login:》的一篇文章中被详细描述。

注:关于杂志《;Login:》,可参考这个链接:https://www.usenix.org/publications/login/2003-08/index.html

ZFS ARC真是一个优美的设计。在接下来的描述中,我将尽量简化一些机制,以便于大家更容易理解ZFS ARC的工作原理。关于ZFS ARC的权威描述,可以参考这个链接:http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/uts/common/fs/zfs/arc.c。在接下来的段落中,我将试着给大家深入讲解一下ZFS 读缓存的内部工作原理。我将关注点放在数据如何进入缓存,缓存如何调整它自己以适应I/O模式的变化,以及“Adjustable Replacement Cache”这个名字是如何来的。

缓存

嗯,在一些文件系统缓存中实现的标准的LRU淘汰算法是有一些缺点的。例如,它们对扫描读模式是没有抵抗性的。但你一次顺序读取大量的数据块时,这些数据块就会填满整个缓存空间,即使它们只是被读一次。当缓存空间满了之后,你如果想向缓存放入新的数据,那些最近最少被使用的页面将会被淘汰出去。在这种大量顺序读的情况下,我们的缓存将会只包含这些新读的数据,而不是那些真正被经常使用的数据。在这些顺序读出的数据仅仅只被使用一次的情况下,从缓存的角度来看,它将被这些无用的数据填满。

另外一个挑战是:一个缓存可以根据时间进行优化(缓存那些最近使用的页面),也可以根据频率进行优化(缓存那些最频繁使用的页面)。但是这两种方法都不能适应所有的workload。而一个好的缓存设计是能自动根据workload来调整它的优化策略。

ARC的内部工作原理

在ARC原始的实现(IBM的实现)和ZFS中的扩展实现都解决了这些挑战,或者说现存问题。我将描述由Megiddo和Modha提出的Adaptive Replacement Cache的一些基本概念,ZFS的实现版本作为这个实现机制的一个扩展来介绍。这两种实现(原始的Adaptive Replacement Cache和ZFS Adjustable Replacement Cache)共享一些基本的操作原理,所以我认为这种简化是一种用来解释ZFS ARC切实可行的途径。

首先,假设我们的缓存中有一个固定的页面数量。简单起见,假设我们有一个8个页面大小的缓存。为了是ARC可以工作,在缓存中,它需要一个2倍大小的管理表。

这个管理表分成4个链表。头两个链表是显而易见的:

·         最近最多使用的页面链表 (LRU list)

·         最近最频繁使用的页面链表(LFU list)

另外两个链表在它们的角色上有些奇怪。它们被称作ghost链表。那些最近被淘汰出去的页面信息被存储在这两个链表中:

·         存储那些最近从最近最多使用链表中淘汰的页面信息 (Ghost list for LRU)

·         存储那些最近从最近最频繁使用链表中淘汰的页面信息(Ghost list for LFU)

闲话缓存:ZFS 读缓存深入研究-ARC(一)第1张

这两个ghost链表不储存数据(仅仅储存页面信息,比如offset,dev-id),但是在它们之中的命中对ARC缓存工作的行为具有重要的影响,我将在后面介绍。那么在缓存中都发生了什么呢?

假设我们从磁盘上读取一个页面,并把它放入cache中。这个页面会放入LRU 链表中。

闲话缓存:ZFS 读缓存深入研究-ARC(一)第2张

接下来我们读取另外一个不同的页面。它也会被放入缓存。显然,他也会被放入LRU 链表的最近最多使用的位置(位置1):

闲话缓存:ZFS 读缓存深入研究-ARC(一)第3张

好,现在我们再读一次第一个页面。我们可以看到,这个页面在缓存中将会被移到LFU链表中。所有进入LRU链表中的页面都必须至少被访问两次。无论什么时候,一个已经在LFU链表中的页面被再次访问,它都会被放到LFU链表的开始位置(most  frequently used)。这么做,那些真正被频繁访问的页面将永远呆在缓存中,不经常访问的页面会向链表尾部移动,最终被淘汰出去。

闲话缓存:ZFS 读缓存深入研究-ARC(一)第4张

随着时间的推移,这两个链表不断的被填充,缓存也相应的被填充。这时,缓存已经满了,而你读进了一个没有被缓存的页面。所以,我们必须从缓存中淘汰一个页面,为这个新的数据页提供位置。这个数据页可能刚刚才被从缓存中淘汰出去,也就是说它不被缓存中任何的非ghost链表引用着。

假设LRU链表已经满了:

闲话缓存:ZFS 读缓存深入研究-ARC(一)第5张

这时在LRU链表中,最近最少使用的页面将会被淘汰出去。这个页面的信息会被放进LRU ghost链表中。

闲话缓存:ZFS 读缓存深入研究-ARC(一)第6张

现在这个被淘汰的页面不再被缓存引用,所以我们可以把这个数据页的数据释放掉。新的数据页将会被缓存表引用。

闲话缓存:ZFS 读缓存深入研究-ARC(一)第7张

随着更多的页面被淘汰,这个在LRU ghost中的页面信息也会向ghost链表尾部移动。在随后的一个时间点,这个被淘汰页面的信息也会到达链表尾部,LRU链表的下一次的淘汰过程发生之后,这个页面信息也会从LRU ghost链表中移除,那是就再也没有任何对它的引用了。

好的,如果这个页面在被从LRU ghost链表中移除之前,被再一次访问了,将会发生什么?这样的一个读将会引起一次幽灵(phantom)命中。由于这个页面的数据已经从缓存中移除了,所以系统还是必须从后端存储媒介中再读一次,但是由于这个幽灵命中,系统知道,这是一个刚刚淘汰的页面,而不是第一次读取或者说很久之前读取的一个页面。ARC用这个信息来调整它自己,以适应当前的I/O模式(workload)。

闲话缓存:ZFS 读缓存深入研究-ARC(一)第8张

很显然,这个迹象说明我们的LRU缓存太小了。在这种情况下,LRU链表的长度将会被增加一。显然,LFU链表的长度将会被减一。

闲话缓存:ZFS 读缓存深入研究-ARC(一)第9张

但是同样的机制存在于LFU这边。如果一次命中发生在LFU ghost 链表中,它会减少LRU链表的长度(减一),以此在LFU 链表中加一个可用空间。

利用这种行为,ARC使它自己自适应于工作负载。如果工作负载趋向于访问最近访问过的文件,将会有更多的命中发生在LRU Ghost链表中,也就是说这样会增加LRU的缓存空间。反过来一样,如果工作负载趋向于访问最近频繁访问的文件,更多的命中将会发生在LFU Ghost链表中,这样LFU的缓存空间将会增大。

进一步,这种行为开启了一个灵活的特性:假设你为处理log文件而读取了大量的文件。你只需要每个文件一次。一个LRU 缓存将会把所有的数据缓存住,这样也就把经常访问的数据也淘汰出去了。但是由于你仅仅访问这些文件一次,它们不会为你带来任何价值一旦它们填满了缓存。

一个ARC缓存的行为是不同的。显然这样的工作负载仅仅会很快填满LRU链表空间,而这些页面很快就会被淘汰出去。但是由于每个这样的页面仅仅被访问一次,它们基本不太可能在为最近访问的文件而设计的ghost链表中命中。这样,LRU的缓存空间不会因为这些仅读一次的页面而增加。

假如你把这些log文件与一个大的数据块联系在一起(为了简单起见,我们假设这个数据块没有自己的缓存机制)。数据文件中的数据页应该会被频繁的访问。被LFU ghost链表引用的正在被访问的页面就很有可能大大的高于LRU ghost链表。这样,经常被访问的数据库页面的缓存空间就会增加。最终,我们的缓存机制就会向缓存数据块页面优化,而不是用log文件来污染我们的缓存空间。

 

免责声明:文章转载自《闲话缓存:ZFS 读缓存深入研究-ARC(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇unity editor模式下读取文件夹资源js 判断大小端存储下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

判断栈和堆的生长方向

如何判断栈的增长方向? 对于一个用惯了i386系列机器的人来说,这似乎是一个无聊的问题,因为栈就是从高地址向低地址增长。不过,显然这不是这个问题的目的,既然把这个问题拿出来,问的就不只是i386系列的机器,跨硬件平台是这个问题的首先要考虑到的因素。 在一个物质极大丰富的年代,除非无路可退,否则我们坚决不会使用汇编去解决问题,而对于这种有系统编程味道的问题,...

linux下操作PostgreSQL的常用命令

一般性: copyright 显示PostgreSQL的使用和发行许可条款g [文件] or; 执行查询 (并把结果写入文件或 |管道)h [名称] SQL命令语法上的说明,用*显示全部命令的语法说明q 退出 psql 查询缓存区e [FILE] [LINE] 使用外部编辑器编辑查询缓存区(或文件)ef [FUNCNAME [LINE]] 使用外部编辑器编...

Cache缓存使用

之前做项目的时候需要用到缓存技术对客户端页面的一个List进行缓存设置,在这之前没使用过,因此对此做个记录: 1、缓存的引用空间:System.Web.Caching; 缓存命名空间主要提供三种操作:缓存数据对象、对象的缓存依赖和数据库的缓存依赖。其中缓存任何对象都使用一个类Cache,但当缓存发生改变时,普通对象和数据库对象的依赖处理不同。 2、管理缓存...

mysql 禁用查询缓存 query cache

os:centos 6.8 mysql: 5.5.49 MySQL Query Cache 会缓存select 查询,但是在调优sql查询及测试数据库的性能时需要禁用该功能。 查看变量、状态 mysql> show global variables like '%cache%'; +------------------------------+--...

关于微服务(四)

一.架构实现 总的来说我的通用架构还是以三层架构为基础进行演变的,在经典的三层架构中,最上层的是controller,中间是service,下层是dao。 在微服务的架构中,最上层是网关层,controller只是网关的一种,中间是业务层,service只是业务层的入口,最下层是基础层,dao只是基础层中的数据存储组件。 二.网关层 网关层本质上是对不...

关于WinForm/Web如何使用缓存Cach

Cache 的绝对到期与滑动到期 绝对到期:设置绝对过期时间 到了指定时间以后会失效。(类似Cookie机制) 相对到期也称滑动到期:设置相对过期时间 指定时间内无访问会失效。(类似Session机制) HttpRuntime.Cache与HttpContext.Current.Cache 为同一个对象HttpRuntime.Cache.Add 存在相同...