7-linked list: (below) LRU cache

1. Principle

 

The LRU (Least recently used) algorithm eliminates data based on the historical access records of the data. If the data has been accessed recently, the probability of being accessed in the future is also higher.

According to this principle, the secondary cache structure of microservices can be designed to store frequently accessed hot data, and realize the same data logic that avoids frequent access to the back-end database. When using this cache as a secondary cache, you need to ensure the consistency of the data in the cache and the back-end data.

2. Implementation

The implementation of lrucache is to use a linked list to save cached data. The detailed algorithm is as follows:

 

  1. New data is inserted at the end of the linked list;

  2. Whenever the cache hits (that is, the cached data is accessed), the data is moved to the end of the linked list;

  3. When the linked list is full, the data at the head of the linked list is discarded.

3. Analysis

[Hit Rate] When hot data exists, the efficiency of LRU is very good, but the occasional and periodic batch operations will cause the LRU hit rate to drop sharply, and the cache pollution is more serious.

【the complexity】

O (1) -O (n)

【cost】

When hitting, you need to traverse the linked list to find the index of the hit data block, and then you need to move the data to the head.

Logic flow:

github related resources:

https://github.com/yiekue/lru4go

https://github.com/wonderivan/lrucache

https://github.com/wonderivan/craw

Implementation one:

package main;
 
import (
    "container/list"
    "errors"
    "sync"
    "fmt"
    "encoding/json"
)
 
//LRU(Least recently used)最近最少使用,算法根据数据的历史访问记录来进行淘汰数据
//核心思想是"如果数据最近被访问过,那么将来被访问的几率也更高"
//常见的实现方式是用一个链表保存数据
//1. 新数据插入到链表头部
//2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部
//3. 当链表满的时候,将链表尾部的数据丢弃
 
type cacheItem struct {
    Key string;
    Val interface{};
}
 
type LRU struct {
    //最大存储数量
    maxNum int;
    //当前存储数量
    curNum int;
    //锁,保证数据一致性
    mutex  sync.Mutex;
    //链表
    data   *list.List;
}
 
//添加数据
func (l *LRU) add(key string, value interface{}) error {
    //判断key是否存在
    if e, _ := l.exist(key); e {
        return errors.New(key + "已存在");
    }
    //判断当前存储数量与最大存储数量
    if l.maxNum == l.curNum {
        //链表已满,则删除链表尾部元素
        l.clear();
    }
    l.mutex.Lock();
    l.curNum++;
    //json序列化数据
    data, _ := json.Marshal(cacheItem{key, value});
    //把数据保存到链表头部
    l.data.PushFront(data);
    l.mutex.Unlock();
    return nil;
}
 
//设置数据
func (l *LRU) set(key string, value interface{}) error {
    e, item := l.exist(key);
    if !e {
        return l.add(key, value);
    }
    l.mutex.Lock();
    data, _ := json.Marshal(cacheItem{key, value});
    //设置链表元素数据
    item.Value = data;
    l.mutex.Unlock();
    return nil;
}
 
//清理数据
func (l *LRU) clear() interface{} {
    l.mutex.Lock();
    l.curNum--;
    //删除链表尾部元素
    v := l.data.Remove(l.data.Back());
    l.mutex.Unlock();
    return v;
}
 
//获取数据
func (l *LRU) get(key string) interface{} {
    e, item := l.exist(key);
    if !e {
        return nil;
    }
    l.mutex.Lock();
    //数据被访问,则把元素移动到链表头部
    l.data.MoveToFront(item);
    l.mutex.Unlock();
    var data cacheItem;
    json.Unmarshal(item.Value.([]byte), &data);
    return data.Val;
}
 
//删除数据
func (l *LRU) del(key string) error {
    e, item := l.exist(key);
    if !e {
        return errors.New(key + "不存在");
    }
    l.mutex.Lock();
    l.curNum--;
    //删除链表元素
    l.data.Remove(item);
    l.mutex.Unlock();
    return nil;
}
 
//判断是否存在
func (l *LRU) exist(key string) (bool, *list.Element) {
    var data cacheItem;
    //循环链表,判断key是否存在
    for v := l.data.Front(); v != nil; v = v.Next() {
        json.Unmarshal(v.Value.([]byte), &data);
        if key == data.Key {
            return true, v;
        }
    }
    return false, nil;
}
 
//返回长度
func (l *LRU) len() int {
    return l.curNum;
}
 
//打印链表
func (l *LRU) print() {
    var data cacheItem;
    for v := l.data.Front(); v != nil; v = v.Next() {
        json.Unmarshal(v.Value.([]byte), &data);
        fmt.Println("key:", data.Key, " value:", data.Val);
    }
}
 
//创建一个新的LRU
func LRUNew(maxNum int) *LRU {
    return &LRU{
        maxNum: maxNum,
        curNum: 0,
        mutex:  sync.Mutex{},
        data:   list.New(),
    };
}
 
func main() {
    lru := LRUNew(5);
    lru.add("1111", 1111);
    lru.add("2222", 2222);
    lru.add("3333", 3333);
    lru.add("4444", 4444);
    lru.add("5555", 5555);
    lru.print();
    //get成功后,可以看到3333元素移动到了链表头
    fmt.Println(lru.get("3333"));
    lru.print();
    //再次添加元素,如果超过最大数量,则删除链表尾部元素,将新元素添加到链表头
    lru.add("6666", 6666);
    lru.print();
    lru.del("4444");
    lru.print();
    lru.set("2222", "242424");
    lru.print();
}

From: https://www.cnblogs.com/jkko123/p/6971133.html

 

Information link 2:

https://www.jianshu.com/p/2aff66fac48a

 

 

Published 127 original articles · Likes 24 · Visits 130,000+

Guess you like

Origin blog.csdn.net/Linzhongyilisha/article/details/99355436