caffeine 淘汰策略
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 淘汰策略相关推荐
- Redis的内存淘汰策略问题
点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 来源:33h.co/ewcf Redis是基于内存的key-value数据 ...
- redistemplate.opsforhash设置过期时间_Redis详解(十一)------ 过期删除策略和内存淘汰策略...
大家好,我是可乐,一个专注原创,乐于分享的程序猿. 本系列教程持续更新,可以微信搜索「 IT可乐 」第一时间阅读.回复<电子书>有我为大家特别筛选的海量免费书籍资料 在介绍这篇文章之前,我 ...
- redis的过期策略和淘汰策略
过期键删除策略 1.定时删除:在设置键的过期时间的同时,创建一个定时器timer,让定时器在键过期时间来临时,立即执行对键的删除操作. 2.惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查 ...
- 【带你重拾Redis】Redis过期策略 和 内存淘汰策略(key回收)
过期策略 我们set key的时候,都可以给一个expire time,就是过期时间,指定这个key比如说只能存活1个小时,我们自己可以指定缓存到期就失效. 如果假设你设置一个一批key只能存活1个小 ...
- redis php数据插入失败,redis插入数据,恢复数据测试(禁止淘汰策略下恢复大于redis内存限制数据情况)...
环境准备: redis php的redis扩展 redis version=4.0.8 php version: php version.png php redis extension: php re ...
- Redis 的内存淘汰策略问题
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | 33h.co/ewcf Redis是基于内存的 ...
- oracle定时器定时删除30天前的数据_Redis-数据淘汰策略持久化方式(RDB/AOF)Redis与Memcached区别...
Redis与Memcached区别: 两者都是非关系型数据库.主要有以下不同: 数据类型: Memcached仅支持字符串类型. redis支持:String,List,set,zset,hash 可 ...
- redis同步效率秒_redis过期策略、内存淘汰策略、持久化方式、主从复制
一.Redis的过期策略以及内存淘汰策略: 1.过期策略:定期删除+惰性删除: ①定期删除:redis默认每隔100ms就随机抽取一些设置了过期时间的key,检查其是否过期,如果有过期就删除.注意这里 ...
- redis高级-内存淘汰策略
目录 一.redis缓存过期淘汰策略 1.redis默认内存 2.修改redis内存 3.查看redis内存情况 二.redis内存超出了设置的最大值会怎么样 1.设置1个字节之后再赋值,会爆OOM ...
最新文章
- 将txt文件和excel文件导入SQL2000数据库
- ksql中定义的本体在dataModel中不存在
- 计算机组成原理 — 服务器组成
- 大数据标准化白皮书正式发布
- pandas使用字典格式修改columns列名
- SAP License:满足管理三重属性 ERP发展专业化是方向
- java 获取调用者方法_java获取调用当前方法的方法名和行数
- 华为:行业危机时,只做一件事:抢人!
- RHCE033内容摘要
- LoadRunner11破解方法
- 新睿云告诉您主流操作:分布式操作系统、批处理操作系统、分时操作系统优缺点分析!
- 相较于本地渲染,云渲染用起来感觉怎么样?
- 微信开发高级群发接口
- Github Pages + Hugo 搭建个人博客
- php打印马赛克,PHP-如何用PHP给一张图生成马赛克效果?
- 大小不一样的图片随着盒子大小自适应
- c语言double型小数点后几位_double类型的数据在输出的时候,C语言编译器对小数部分可以精确到小数点后面的第几位?...
- [数据讨论][解包相关]下江小春也能轻松掌握的碧蓝档案提取工具
- css水墨背景,PS古风水墨背景教程
- 10 模拟SPI驱动PS2无线手柄
热门文章
- EasyUI Numberbox 数字框(限制仅输入数字)
- python模块的发布_(转载)Python中模块的发布与安装
- Resx 文件无效。未能加载 .RESX 文件中使用的类型 System.Collections.Generic.List`1请确保已在项目中添加了必需的引用。
- STL(五)——slist/list链表
- Day12-BigDecimal笔记及练习
- android 点击热区,增大UIButton的点击热区
- ASP.NET中密码保护,MD5和SHA1算法的使用
- 华为成立德国实验室属实 但并非为5G牌照
- PostgreSQL 设置单条SQL的执行超时 - 防雪崩
- 静态网站优化技巧总结