缓存失效算法
先来先淘汰(FIFO)
First In First Out,先来先淘汰。这种算法在每一次新数据插入时,如果队列已满,则将最早插入的数据移除。
可以方便的借助LinkedList来实现
package cache;
import java.util.Iterator;
import java.util.LinkedList;
public class FIFO {
LinkedList<Integer> fifo = new LinkedList<Integer>();
int size = 3;
private void print() {
System.out.println(this.fifo);
}
public void add(int i) {
fifo.addFirst(i);
if(fifo.size() > size) {
fifo.removeLast();
}
print();
}
private void read(int i) {
Iterator<Integer> iterator = fifo.iterator();
while(iterator.hasNext()) {
int j = iterator.next();
if(i == j) {
System.out.println("find it!");
print();
return;
}
}
System.out.println("not found");
print();
}
public static void main(String[] args) {
FIFO fifo = new FIFO();
fifo.add(1);
fifo.add(2);
fifo.add(3);
fifo.read(1);
fifo.read(100);
}
}
最久未用淘汰(LRU)
LRU全称是Least Recently Used,即淘汰最后一次使用时间最久远的数值。FIFO非常的粗暴,不管有没有用到,直接踢掉时间久的元素。而LRU认为,最近频繁使用过的数据,将来也很大程度上会被频繁用到,故而淘汰那些懒惰的数据。LinkedHashMap,数组,链表均可实现LRU,下面仍然以链表为例:新加入的数据放在头部,最近访问的,也移到头部,空间满时,将尾部元素删除
package cache;
import java.util.Iterator;
import java.util.LinkedList;
public class LRU {
LinkedList<Integer> lru = new LinkedList<Integer>();
int size = 3;
private void print() {
System.out.println(this.lru);
}
public void add(int i) {
lru.addFirst(i);
if(lru.size() > size) {
lru.removeLast();
}
print();
}
private void read(int i) {
Iterator<Integer> iterator = lru.iterator();
int index = 0;
while(iterator.hasNext()) {
int j = iterator.next();
if(i == j) {
System.out.println("find it!");
lru.remove(index);
lru.addFirst(j);
print();
return;
}
index++;
}
System.out.println("not found");
print();
}
public static void main(String[] args) {
LRU lru = new LRU();
lru.add(1);
lru.add(2);
lru.add(3);
lru.read(2);
lru.read(100);
}
}
最近最少使用(LFU)
Least Frequently Used,即最近最少使用。它要淘汰的是最近一段时间内,使用次数最少的值。可以认为比LRU多了一重判断。LFU需要时间和次数两个维度的参考指标。需要注意的是,两个维度就可能涉及到同一时间段内,访问次数相同的情况,就必须内置一个计数器和一个队列,计数器算数,队列放置相同计数时的访问时间。
package cache;
public class Dto implements Comparable<Dto> {
private Integer key;
private int count;
private long lastTime;
public Dto(Integer key, int count, long lastTime) {
this.key = key;
this.count = count;
this.lastTime = lastTime;
}
@Override
public int compareTo(Dto o) {
// TODO Auto-generated method stub
int compare = Integer.compare(this.count, o.count);
return compare == 0 ?Long.compare(this.lastTime, o.lastTime) :compare;
}
@Override
public String toString() {
return String.format("[key=%s,count=%s,lastTime=%s]",key,count,lastTime);
}
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public long getLastTime() {
return lastTime;
}
public void setLastTime(long lastTime) {
this.lastTime = lastTime;
}
}
package cache;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class LFU {
private final int size =3;
private Map<Integer, Integer> cache = new HashMap<>();
private Map<Integer, Dto> count = new HashMap<>();
private void print() {
System.out.println("cache="+cache);
System.out.println("count="+count);
}
private void removeElement() {
Dto dto = Collections.min(count.values());
cache.remove(dto.getKey());
count.remove(dto.getKey());
}
private void addCount(Integer key) {
Dto Dto = count.get(key);
Dto.setCount(Dto.getCount()+1);
Dto.setLastTime(System.currentTimeMillis());
}
private void put(Integer key, Integer value) {
Integer v = cache.get(key);
if(v == null) {
if(cache.size() == size) {
removeElement();
}
count.put(key, new Dto(key, 1, System.currentTimeMillis()));
}else {
addCount(key);
}
cache.put(key, value);
}
public Integer get(Integer key) {
Integer value = cache.get(key);
if(value != null) {
addCount(key);
return value;
}
return null;
}
public static void main(String[] args) {
LFU lfu = new LFU();
System.out.println("add 1-3:");
lfu.put(1, 1);
lfu.put(2, 2);
lfu.put(3, 3);
lfu.print();
System.out.println("1,2有访问,3没有,加入4,淘汰3");
lfu.get(1);
lfu.get(2);
lfu.print();
lfu.put(4, 4);
lfu.print();
System.out.println("2=3次,1,4=2次,但是4加入较晚,再加入5时淘汰1");
lfu.get(2);
lfu.get(4);
lfu.print();
System.out.println("add 5:");
lfu.put(5, 5);
lfu.print();
}
}
应用
redis属于缓存失效的典型应用场景,常见策略如下:
- noeviction: 不删除策略, 达到最大内存限制时, 如果需要更多内存, 直接返回错误信息( 比较危险)。
- allkeys-lru:对所有key,优先删除最近最少使用的 key (LRU)。
- allkeys-random: 对所有key, 随机删除一部分(听起来毫无道理)。
- volatile-lru:只限于设置了 expire 的key,优先删除最近最少使用的key (LRU)。
- volatile-random:只限于设置了 expire 的key,随机删除一部分。
- volatile-ttl:只限于设置了 expire 的key,优先删除剩余时间(TTL) 短的key。
每个人都有潜在的能量,只是很容易被习惯所掩盖,被时间所迷离,被惰性所消磨~
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库