Caffeine在开启了淘汰策略的时候,维护了三条队列,分别是eden,protected,probation队列。具体的三条队列大小分配方案如下所示,

long max = Math.min(maximum, MAXIMUM_CAPACITY);
long eden = max - (long) (max * PERCENT_MAIN);
long mainProtected = (long) ((max - eden) * PERCENT_MAIN_PROTECTED);

其中,eden为总大小的百分之1,当总大小为1000的时候,eden也只有10的长度,而protected则占剩下百分之99的百分之80,也就是800,剩下的百分之20则为probation队列,大小则为190。

当数据刚添加的时候将会将其加入到eden队列,eden队列中的数据不会具体参与到淘汰的过程中,保证在lfu算法下新进入的数据不会由于刚进入而被淘汰。

淘汰的具体方法为evictEntries()方法。

void evictEntries() {if (!evicts()) {return;}int candidates = evictFromEden();evictFromMain(candidates);
}

其中,一共分为两步,第一步则通过evictFromEden()方法将eden队列中的元素个数平衡到eden的最大数量以下,并将移出的元素放到probation队列中,而后通过evictFromMain()方法开始从probation和protected淘汰数据。

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 weightif (node == null) {break;}Node<K, V> next = node.getNextInAccessOrder();if (node.getWeight() != 0) {node.makeMainProbation();accessOrderEdenDeque().remove(node);accessOrderProbationDeque().add(node);candidates++;lazySetEdenWeightedSize(edenWeightedSize() - node.getPolicyWeight());}node = next;}return candidates;
}

evictFromEden()方法的实现如上,逻辑很简单,当eden元素个数大于总量的百分之一的时候,从eden队首获取元素,并将其移到probation的队尾重复该操作,直到eden剩余数量小于预设的eden长度为止。该方法会返回从eden移入到probation的个数,这几个新加入probation的成员会优先与probation的队首键进行频度比较。

而后,通过evictFromMain()方法开始正式进行淘汰,其中,优先会从probation中将数据进行淘汰,直到probation队列为空才会从protected队列中获取元素尝试淘汰,而当protected也而无法获取元素,则会从protected队列中进行获取。probation中的元素被外部访问一次就会被晋升到protected队列。

int victimQueue = PROBATION;
Node<K, V> victim = accessOrderProbationDeque().peekFirst();
Node<K, V> candidate = accessOrderProbationDeque().peekLast();

evictFromMain()方法很长,首先,会从probation中获取队首和队尾两个元素,其中,队尾的元素为刚刚上一步从eden移入到probation的元素。

if ((candidate == null) && (victim == null)) {if (victimQueue == PROBATION) {victim = accessOrderProtectedDeque().peekFirst();victimQueue = PROTECTED;continue;} else if (victimQueue == PROTECTED) {victim = accessOrderEdenDeque().peekFirst();victimQueue = EDEN;continue;}// The pending operations will adjust the size to reflect the correct weightbreak;
}
if ((victim != null) && (victim.getPolicyWeight() == 0)) {victim = victim.getNextInAccessOrder();continue;
} else if ((candidate != null) && (candidate.getPolicyWeight() == 0)) {candidate = candidate.getPreviousInAccessOrder();candidates--;continue;
}

首先,如果队首队尾都无法正常获取元素,则会变更淘汰处理的队列,变更顺序已经有提,并略过0权重下的数据。

if (victim == null) {candidates--;Node<K, V> evict = candidate;candidate = candidate.getPreviousInAccessOrder();evictEntry(evict, RemovalCause.SIZE, 0L);continue;
} else if (candidate == null) {Node<K, V> evict = victim;victim = victim.getNextInAccessOrder();evictEntry(evict, RemovalCause.SIZE, 0L);continue;
}

如果队首队尾元素有一个为null,那么就直接淘汰其中不为null的元素。

K victimKey = victim.getKey();
K candidateKey = candidate.getKey();
if (victimKey == null) {Node<K, V> evict = victim;victim = victim.getNextInAccessOrder();evictEntry(evict, RemovalCause.COLLECTED, 0L);continue;
} else if (candidateKey == null) {candidates--;Node<K, V> evict = candidate;candidate = candidate.getPreviousInAccessOrder();evictEntry(evict, RemovalCause.COLLECTED, 0L);continue;
}

之后,如果其中任意一个元素的key已经被回收,那么也直接就淘汰掉。

candidates--;
if (admit(candidateKey, victimKey)) {Node<K, V> evict = victim;victim = victim.getNextInAccessOrder();evictEntry(evict, RemovalCause.SIZE, 0L);candidate = candidate.getPreviousInAccessOrder();
} else {Node<K, V> evict = candidate;candidate = candidate.getPreviousInAccessOrder();evictEntry(evict, RemovalCause.SIZE, 0L);
}

最后,才采用admit()方法通过lfu算法进行淘汰,其中,lfu的具体实现在admit()方法中。

boolean admit(K candidateKey, K victimKey) {int victimFreq = frequencySketch().frequency(victimKey);int candidateFreq = frequencySketch().frequency(candidateKey);if (candidateFreq > victimFreq) {return true;} else if (candidateFreq <= 5) {// The maximum frequency is 15 and halved to 7 after a reset to age the history. An attack// exploits that a hot candidate is rejected in favor of a hot victim. The threshold of a warm// candidate reduces the number of random acceptances to minimize the impact on the hit rate.return false;}int random = ThreadLocalRandom.current().nextInt();return ((random & 127) == 0);
}

frequecy()方法来具体获得两个key的频度,其中具体的实现算法介绍在上一篇博文中已经提及。

如果队尾元素的频度大于队首,那么就直接淘汰队首,当队尾频度小于等于队首,且频度小于5的时候,直接将其淘汰,否则,通过随机的方式进行淘汰任意一个键。

这里的5的选择,与caffeine底层具体的lfu实现有关,因为底层通过4位记录一个频度,最大值是15,老的键可能由于超过15,导致频度减半变为7,所以队尾新加入的元素如果小于5会直接淘汰。

重复以上流程,淘汰到数据总量小于设定的最大容量。

caffeine 淘汰策略相关推荐

  1. Redis的内存淘汰策略问题

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 来源:33h.co/ewcf Redis是基于内存的key-value数据 ...

  2. redistemplate.opsforhash设置过期时间_Redis详解(十一)------ 过期删除策略和内存淘汰策略...

    大家好,我是可乐,一个专注原创,乐于分享的程序猿. 本系列教程持续更新,可以微信搜索「 IT可乐 」第一时间阅读.回复<电子书>有我为大家特别筛选的海量免费书籍资料 在介绍这篇文章之前,我 ...

  3. redis的过期策略和淘汰策略

    过期键删除策略 1.定时删除:在设置键的过期时间的同时,创建一个定时器timer,让定时器在键过期时间来临时,立即执行对键的删除操作. 2.惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查 ...

  4. 【带你重拾Redis】Redis过期策略 和 内存淘汰策略(key回收)

    过期策略 我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效. 如果假设你设置一个一批key只能存活1个小 ...

  5. redis php数据插入失败,redis插入数据,恢复数据测试(禁止淘汰策略下恢复大于redis内存限制数据情况)...

    环境准备: redis php的redis扩展 redis version=4.0.8 php version: php version.png php redis extension: php re ...

  6. Redis 的内存淘汰策略问题

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | 33h.co/ewcf Redis是基于内存的 ...

  7. oracle定时器定时删除30天前的数据_Redis-数据淘汰策略持久化方式(RDB/AOF)Redis与Memcached区别...

    Redis与Memcached区别: 两者都是非关系型数据库.主要有以下不同: 数据类型: Memcached仅支持字符串类型. redis支持:String,List,set,zset,hash 可 ...

  8. redis同步效率秒_redis过期策略、内存淘汰策略、持久化方式、主从复制

    一.Redis的过期策略以及内存淘汰策略: 1.过期策略:定期删除+惰性删除: ①定期删除:redis默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果有过期就删除.注意这里 ...

  9. redis高级-内存淘汰策略

    目录 一.redis缓存过期淘汰策略 1.redis默认内存 2.修改redis内存 3.查看redis内存情况 二.redis内存超出了设置的最大值会怎么样 1.设置1个字节之后再赋值,会爆OOM ...

最新文章

  1. 将txt文件和excel文件导入SQL2000数据库
  2. ksql中定义的本体在dataModel中不存在
  3. 计算机组成原理 — 服务器组成
  4. 大数据标准化白皮书正式发布
  5. pandas使用字典格式修改columns列名
  6. SAP License:满足管理三重属性 ERP发展专业化是方向
  7. java 获取调用者方法_java获取调用当前方法的方法名和行数
  8. 华为:行业危机时,只做一件事:抢人!
  9. RHCE033内容摘要
  10. LoadRunner11破解方法
  11. 新睿云告诉您主流操作:分布式操作系统、批处理操作系统、分时操作系统优缺点分析!
  12. 相较于本地渲染,云渲染用起来感觉怎么样?
  13. 微信开发高级群发接口
  14. Github Pages + Hugo 搭建个人博客
  15. php打印马赛克,PHP-如何用PHP给一张图生成马赛克效果?
  16. 大小不一样的图片随着盒子大小自适应
  17. c语言double型小数点后几位_double类型的数据在输出的时候,C语言编译器对小数部分可以精确到小数点后面的第几位?...
  18. [数据讨论][解包相关]下江小春也能轻松掌握的碧蓝档案提取工具
  19. css水墨背景,PS古风水墨背景教程
  20. 10 模拟SPI驱动PS2无线手柄

热门文章

  1. EasyUI Numberbox 数字框(限制仅输入数字)
  2. python模块的发布_(转载)Python中模块的发布与安装
  3. Resx 文件无效。未能加载 .RESX 文件中使用的类型 System.Collections.Generic.List`1请确保已在项目中添加了必需的引用。
  4. STL(五)——slist/list链表
  5. Day12-BigDecimal笔记及练习
  6. android 点击热区,增大UIButton的点击热区
  7. ASP.NET中密码保护,MD5和SHA1算法的使用
  8. 华为成立德国实验室属实 但并非为5G牌照
  9. PostgreSQL 设置单条SQL的执行超时 - 防雪崩
  10. 静态网站优化技巧总结