论文《TinyLFU: A Highly Ecient Cache Admission Policy》阅读笔记

1. Introduction

LFU的局限:前端

  • LFU实现须要维护大而复杂的元数据(频次统计数据等)
  • 大多数实际工做负载中,访问频率随着时间的推移而发生根本变化(这是外卖业务不适合朴素LFU的根本缘由)

针对LFU的问题,已经存在了一些LFU的替代或优化方案,例如WLFU(Window LFU):算法

  • 老化机制
  • 限制采样为:最后W次访问的有限大小窗口

在绝大多数状况下,新访问的数据老是被直接插入到缓存中,缓存方案仅设计驱逐策略,即,决定应该驱逐哪一个数据。这是由于维护当前不在缓存中的对象的元数据被认为是不切实际的。缓存

因为维护全部访问的频率数据消耗过大,不少LFU的实现只维护了缓存中数据的频率数据。对于前者,咱们称为Perfect LFU(PLFU),后者则称为In-Memory LFU。因为WLFU(Window-LFU)优于In-Memery LFU(以更大的元数据为代价),因此论文在第一节只讨论了WLFU和PLFU。数据结构

LRU是一个很常见的用来替代LFU的算法,LRU的淘汰最近最少使用的元素。相较于LFU,LRU可能会有更加高效的实现,能够并自动适应突发的访问请求;然而基于LRU的缓存,在较大的负载下,须要更多的空间来保持和LFU同样的缓存命中率。app

本文的贡献:性能

  • 阐明了一种近似LFU准入策略的缓存结构/算法的有效性优化

    • 提出了一种缓存结构,只有在准入策略认为将其替换进入缓存会提升缓存命中率的状况下,才会被插入
    • 提出了一种空间利用率很高的新的数据结构——TinyLFU,能够在较大访问量的场景下近似的替代LFU的数据统计部分(meta-data)。
    • 经过形式化的证实和模拟,证实了TinyLFU得到的Freq排序与真实的访问频率排序是几乎近似的
    • 以优化的形式将TinyLFU实如今了Caffeine项目中:W-TinyLFU
    • 与其余几种缓存管理策略进行了比较,包括ARC、LIRC

对于较为静态的访问分布,各类的缓存管理策略的差别是微不足道的,而对于偏倚较大的负载场景,TinyLFU体现出了较大的优点。即使是使用了TinyLFU准入策略进行优化的LRU也得到了较大的提高(LRU原生没有准入策略)。spa

2. Related Work

2.1 Cache Replacement

PLFU更加适合相对静态的访问分布,但不能很好的适应动态的访问频率分布。
In-Memory LFU仅维护已存在于缓存中的数据项的访问频率,并始终将最近访问的项插入缓存中,若是须要,逐出缓存中最不频繁访问的项。一般In-Memory LFU使用堆来管理缓存,而且由Anirban优化到了O(1)。然而即使有了这样的改进,但In-Memory LFU仍然对频率分布的变化显得反应迟缓。在静态的频率分布下,性能也落后于PLFU(由于其再也不为【不在缓存中的数据】维护任何频率数据)。设计

  • Aging对象

    • 是对In-Memory LFU的优化,提升了其对变化的响应能力。
    • 增长了一个【最大平均引用计数】,当缓存中的数据引用计数平均值超过了该值,则将全部的数据的频率技术都进行减小。可是如何减小(通常是存在一个衰减因子)是一个棘手且tricky的问题。
  • WLFU

    • 只保存一个时间窗口内的访问数据来统计频次,这个机制要求按照时序对访问进行持续的采样。对变化的调整是优于PLFU的。
  • ARC
  • LIRS
  • SLRU
  • 2Q
  • LRU-K
  • GDSF

2.2 近似统计结构(Approximate Counting Architectures)

简单说就是对全流量采样,TinyLFU须要一种近似的流量统计结构来替代全量Hash,减小内存的占用,而且要尽量高效,且尽可能保持精度。能够详见3.2

3. TinyLFU Architecture

3.1 TinyLFU Overview

TinyLFU的准入&淘汰策略是:新增一个新的元素时,判断使用该元素替换一个旧元素,是否能够提高缓存命中率。
clipboard.png

上图为TinyLFU的主要结构,可是在阐述前须要强调咱们面临的两个主要的挑战:

  1. 维护一个新鲜度机制(freshness mechanism),来保持历史最近访问且能够移除旧事件(keep the history recent and remove the history old events)
  2. 如何减小内存的消耗

3.2 Approximate Counting Overview

  • Minimal Increment Counting Bloom Filter Counting Bloom Filter参考连接

    • Minimal Increment CBF 和CBF 使用一个合适大小的计数器来代替了Bloom Filter中的bit
    • Minimal Increment CBF支持如下两个方法(下面的阐述,假设咱们的场景是来为key计算k=3个hash value,来定位)

      • Estimate

        • 例如,获取到3个hash位置上的计数值:{2, 2, 5},取全部值中的最小值返回
      • Add

        • 在元素到达后,获取到已有的3个计数值{2, 2, 5},Add操做仅增长两个较小的计数值,来避免针对最大值的没必要要的增长

clipboard.png

3.3 Freshness Mechanism

TinyLFU使用reset机制来保证sketch中的数据尽量最新。每增长一个新的元素到approximation sketches,会增长一个计数值,一旦计数值达到了一个预设的采样尺寸(W),就会将频率采样(CBF)维护的全部计数值除以2(可使用高效的寄存器位移来实现);论文也花了较大的篇幅来证实了这种Reset机制的正确性,且评估了其存在的截断错误(3会被reset为1,而非1.5),而且得出了如下结论:

reset在固定的频率分布下彻底正确,且能够应对流量频率的变化(数学概括法证实,感兴趣的能够参考原文3.3.1)

采样数W越大,截断错误的带来的影响越小

3.4 Space Reduction

clipboard.png
在两个维度减小了空间的占用:

  1. 减少了sketch中计数值的尺寸

    1. 对于咱们的采样大小W,咱们的计数值须要占用log(W) bit
  2. 减小了sketch分的计数器的数量

    1. Doorkeeper

      1. 引入了Doorkeeper机制,来避免为长尾流量分配计数器
      2. 由一个常规的Bloom Filter拦截在CBF以前
      3. 若是一个元素,在Doorkeeper中,则直接插入TinyLFU的主结构,不然先插入Doorkeeper;对于数据查询,会将Doorkeeper中的那一个计数值也加到计数值上去
      4. reset操做会清空Doorkeeper
      5. 这样对于每一个W周期内,都会过滤仅有一次的访问的数据

尽管Doorkeeper须要一些额外的空间,可是相对于在主要结构中节约的空间来讲,显得微不足道。

3.5 TingLFU vs a Strwman

3.6 Connecting TinyLFU to Caches

对于LRU和随机缓存而言,缓存存储本质上是个黑盒。而TinyLFU不一样,因为reset机制的存在,须要在reset时,同步更新缓存中的item;

4. Winwod TinyLFU(W-TinyLFU)

主要是优化了“sparse bursts(突发的稀疏流量)”,新的突发流量可能会由于没法构建足够的频率数据来保证本身驻留在缓存中。
clipboard.png

前端是一个小的LRU(window cache);window cache淘汰后,会送到DoorKeeper过滤;过滤经过后,元素存储到一个大的Segmented LRU缓存里。Window LRU,它的容量只占据1%的总空间,它的目的就是用来存放短时间突发访问记录。存放主要元素的Segmented LRU(SLRU)是一种LRU的改进,主要把在一个时间窗口内命中至少2次的记录和命中1次的单独存放,这样就能够把短时间内较频繁的缓存元素区分开来。具体作法上,SLRU包含2个固定尺寸的LRU,一个叫Probation段A1,一个叫Protection段A2。新记录老是插入到A1中,当A1的记录被再次访问,就把它移到A2,当A2满了须要驱逐记录时,会把驱逐记录插入到A1中。W-TinyLFU中,SLRU有80%空间被分配给A2段。

5. 实验

相关文章
相关标签/搜索