一篇学会Caffeine W-TinyLFU源码分析

本文主要分析Caffeine缓存中W-TinyLFU的数据结构和淘汰策略。它使用accessOrderEdenDeque、accessOrderProbationDeque和accessOrderProtectedDeque三个双端队列实现LRU算法。eden队列通过LRU淘汰数据进入probation队列,probation队列中的数据根据频率PK进行淘汰。访问probation中的数据会转移到protected队列,保持大部分热数据。TinyLFU利用位图压缩存储频率,通过hash值定位频率,节省空间并有效管理缓存数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Caffeine使用一个ConcurrencyHashMap来保存所有数据,那它的过期淘汰策略采用什么方式与数据结构呢?其中写过期是使用writeOrderDeque,这个比较简单无需多说,而读过期相对复杂很多,使用W-TinyLFU的结构与算法。【资料获取】

网络上有很多文章介绍W-TinyLFU结构的,大家可以去查一下,这里主要是从源码来分析,总的来说它使用了三个双端队列:

  • accessOrderEdenDeque,accessOrderProbationDeque,accessOrderProtectedDeque,使用双端队列的原因是支持LRU算法比较方便。

  • accessOrderEdenDeque属于eden区,缓存1%的数据,其余的99%缓存在main区

  • accessOrderProbationDeque属于main区,缓存main内数据的20%,这部分是属于冷数据,即将补淘汰。

  • accessOrderProtectedDeque属于main区,缓存main内数据的80%,这部分是属于热数据,是整个缓存的主存区。

我们先看一下淘汰方法入口:

void evictEntries() {
    
  if (!evicts()) {
    
    return; 
  } 
  //先从edn区淘汰 
  int candidates = evictFromEden(); 
  //eden淘汰后的数据进入main区,然后再从main区淘汰 
  evictFromMain(candidates); 
} 

accessOrderEdenDeque对应W-TinyLFU的W(window),这里保存的是最新写入数据的引用,它使用LRU淘汰,这里面的数据主要是应对突发流量的问题,淘汰后的数据进入accessOrderProbationDeque.代码如下:

int evictFromEden() {
    
  int candidates = 0; 
  Node<K, V> node = accessOrderEdenDeque().peek(); 
  while (edenWeightedSize() > edenMaximum()) {
    
    // The pending operations will adjust the size to reflect the correct weight 
    if (node == null) {
    
      break; 
    } 
 
    Node<K, V> next = node.getNextInAccessOrder(); 
    if (node.getWeight() != 0) {
    
      node.makeMainProbation(); 
      //先从eden区移除 
      accessOrderEdenDeque().remove(node); 
      //移除的数据加入到main区的probation队列 
      accessOrderProbationDeque().add(node); 
      candidates++; 
 
      lazySetEdenWeightedSize(edenWeightedSize() - node.getPolicyWeight()); 
    } 
    node = next; 
  } 
 
  return candidates; 
} 

数据进入probation队列后,继续执行以下代码:

void evictFromMain(int candidates) {
    
  int victimQueue = PROBATION; 
  Node<K, V> victim = accessOrderProbationDeque().peekFirst(); 
  Node<K, V> candidate = accessOrderProbationDeque().peekLast(); 
  while (weightedSize() > maximum()) {
    
    // Stop trying to evict candidates and always prefer the victim 
    if (candidates == 0) {
    
      candidate = null; 
    } 
 
    // Try evicting from the protected and eden queues 
    if ((candidate == null) && (victim == null)) {
    
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值