1.原則
LRU(Least Recently Used)アルゴリズムは、データの履歴アクセスレコードに基づいてデータを削除します。データが最近アクセスされた場合、将来アクセスされる可能性も高くなります。
この原理によれば、マイクロサービスの二次キャッシュ構造は、頻繁にアクセスされるホットデータを格納するように設計でき、バックエンドデータベースへの頻繁なアクセスを回避する同じデータロジックを実現します。このキャッシュを二次キャッシュとして使用する場合、キャッシュ内のデータとバックエンドデータの整合性を確保する必要があります。
2.実装
lrucacheの実装では、リンクリストを使用してキャッシュデータを保存します。詳細なアルゴリズムは次のとおりです。
-
リンクされたリストの最後に新しいデータが挿入されます。
-
キャッシュがヒットする(つまり、キャッシュされたデータがアクセスされる)と、データはリンクリストの最後に移動されます。
-
リンクリストがいっぱいになると、リンクリストの先頭にあるデータは破棄されます。
3.分析
[ヒット率]ホットデータが存在する場合、LRUの効率は非常に良好ですが、時々または定期的なバッチ操作によりLRUヒット率が急激に低下し、キャッシュの汚染がより深刻になります。
【複雑さ】
O(1)-O(n)
【費用】
ヒットするときは、リンクリストをたどってヒットデータブロックのインデックスを見つけてから、データを先頭に移動する必要があります。
ロジックフロー:
github関連リソース:
https://github.com/yiekue/lru4go
https://github.com/wonderivan/lrucache
https://github.com/wonderivan/craw
実装1:
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();
}
送信元:https : //www.cnblogs.com/jkko123/p/6971133.html
情報リンク2:
https://www.jianshu.com/p/2aff66fac48a