Java failure algorithm and application (FIFO, LRU, LFU)

1. What is the failure algorithm

Invalidation algorithms are common in 缓存系统. Because the cache often occupies a large amount of memory, and the memory space is relatively expensive and limited, then for some values, it is necessary to operate according to the corresponding algorithm 失效或移除.

2. First come, first eliminated (FIFO)

1. FIFO overview

First In First Out, first come, first eliminated. This algorithm every time new data is inserted, if the queue is full, then 将最早插入的数据移除.

2. Java implements FIFO

It can be easily realized with the help of LinkedList.

public class FIFO {
    
    
    LinkedList<Integer> fifo = new LinkedList<Integer>();
    int size = 3;
    //添加元素
    public void add(int i){
    
    
        fifo.addFirst(i);
        if (fifo.size() > size){
    
    
            fifo.removeLast();
        }
        print();
    }
    //缓存命中
    public 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 void print(){
    
    
        System.out.println(this.fifo);
    }
    //测试
    public static void main(String[] args) {
    
    
        FIFO fifo = new FIFO();
        System.out.println("add 1-3:");
        fifo.add(1);
        fifo.add(2);
        fifo.add(3);
        System.out.println("add 4:");
        fifo.add(4);
        System.out.println("read 2:");
        fifo.read(2);
        System.out.println("read 100:");
        fifo.read(100);
        System.out.println("add 5:");
        fifo.add(5);
    }
}

结果:
add 1-3:
[1]
[2, 1]
[3, 2, 1]
add 4:
[4, 3, 2]
read 2:
find it!
[4, 3, 2]
read 100:
not found!
[4, 3, 2]
add 5:
[5, 4, 3]
Process finished with exit code 0

3. FIFO characteristics

We found that the implementation of FIFO is very simple. Regardless of the usage of elements, even if some data is frequently used, it will be kicked out for the longest time (not rational).

3. Longest unused elimination (LRU)

1. Overview of LRUs

The full name of LRU is Least Recently Used, ie 淘汰最后一次使用时间最久远的数值. FIFO is very rough, regardless of whether it is used or not, it will directly kick out the long-term elements. LRU believes that the data that has been frequently used recently will be frequently used in the future to a large extent, so those lazy data are eliminated.

2. Java implements LRU

LinkedHashMap, arrays, and linked lists can all implement LRU. The linked list is still used as an example below: the newly added data is placed at the head, and the most recently accessed data is also moved to the head. When the space is full, the tail elements are deleted.

public class LRU {
    
    
    LinkedList<Integer> lru = new LinkedList<Integer>();
    int size = 3;
    //添加元素
    public void add(int i){
    
    
        lru.addFirst(i);
        if (lru.size() > size){
    
    
            lru.removeLast(); // 删除尾部元素
        }
        print();
    }
    //缓存命中
    public 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 void print(){
    
    
        System.out.println(this.lru);
    }
    //测试
    public static void main(String[] args) {
    
    
        LRU lru = new LRU();
        System.out.println("add 1-3:");
        lru.add(1);
        lru.add(2);
        lru.add(3);
        System.out.println("add 4:");
        lru.add(4);
        System.out.println("read 2:");
        lru.read(2);
        System.out.println("read 100:");
        lru.read(100);
        System.out.println("add 5:");
        lru.add(5);
    }
}

结果:
add 1-3:
[1]
[2, 1]
[3, 2, 1]
add 4:
[4, 3, 2]
read 2:
find it!
[2, 4, 3]
read 100:
not found!
[2, 4, 3]
add 5:
[5, 2, 4]

4. Least Recently Used (LFU)

1. Overview of LFU

Least Frequently Used, that is, the least recently used. it wants 淘汰的是最近一段时间内,使用次数最少的值. It can be considered that there is one more judgment than LRU. LFU needs reference indicators in two dimensions of time and times.

It should be noted that the two dimensions may involve the same time period and the same number of visits, so a counter and a queue must be built in, the counter counts, and the queue places the access time when the count is the same.

2. Java implements LFU

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) {
    
    
        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;
    }
}
public class LFU {
    
    
    private final int size = 3;

    private Map<Integer,Integer> cache = new HashMap<>();

    private Map<Integer, Dto> count = new HashMap<>();

    //投放
    public 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;
    }

    //淘汰元素
    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 print(){
    
    
        System.out.println("cache="+cache);
        System.out.println("count="+count);
    }



    public static void main(String[] args) {
    
    
        LFU lfu = new LFU();

        //前3个容量没满,1,2,3均加入
        System.out.println("add 1-3:");
        lfu.put(1, 1);
        lfu.put(2, 2);
        lfu.put(3, 3);
        lfu.print();

        //1,2有访问,3没有,加入4,淘汰3
        System.out.println("read 1,2");
        lfu.get(1);
        lfu.get(2);
        lfu.print();
        System.out.println("add 4:");
        lfu.put(4, 4);
        lfu.print();

        //2=3次,1,4=2次,但是4加入较晚,再加入5时淘汰1
        System.out.println("read 2,4");
        lfu.get(2);
        lfu.get(4);
        lfu.print();
        System.out.println("add 5:");
        lfu.put(5, 5);
        lfu.print();

    }
}

5. Application Cases

Redis is a typical application scenario of cache invalidation. The common strategies are as follows:

noeviction: Do not delete the policy. When the maximum memory limit is reached, if more memory is needed, an error message will be returned directly (more dangerous).
allkeys-lru: For all keys, delete the least recently used key (LRU) first.
allkeys-random: For all keys, randomly delete some (sounds unreasonable).
volatile-lru: only limited to keys with expire set, priority is given to deleting the least recently used key (LRU).
volatile-random: Only limited to keys with expire set, some of them are randomly deleted.
volatile-ttl: only limited to keys with expire set, priority is given to deleting keys with a short remaining time (TTL).

Guess you like

Origin blog.csdn.net/A_art_xiang/article/details/132044941