索引
【go项目-geecache】动手写分布式缓存 - day1 - 实现LRU算法
【go项目-geecache】动手写分布式缓存 - day2 - 单机并发缓存
【go项目-geecache】动手写分布式缓存 - day3 - HTTP 服务端
【go项目-geecache】动手写分布式缓存 - day4 - 一致性哈希(hash)
【go项目-geecache】动手写分布式缓存 - day5 - 分布式节点
【go项目-geecache】动手写分布式缓存 - day6 - 防止缓存击穿
【go项目-geecache】动手写分布式缓存 - day7 - 使用 Protobuf 通信
介绍 LRU 内存淘汰算法
LRU(Least Recently Used) 最近最少使用 算法 ,系统认为如果这个数据最近使用过那么它被再次使用的概率会高,所以系统会先淘汰最久没被使用的数据
基本逻辑
-----------------------------------------------------------------------出自极客兔兔
k(绿色)为map,即实际中的缓存,当我们读取数据时就是先从这个查询和获取,复杂度为log 级别,非常快
n(红色)为双向链表,用来记录哪个数据是最晚出现的,使用双向链表的原因是为了快速让数据放在队首/队尾,当我们访问一个数据时,我们把这个数据放在链表队首,当我们需要淘汰内存时我们删除队尾的数据,这个数据就是最晚出现的
具体实现
实现数据结构
- 实现Cache,包含允许的最大缓存,当前的缓存,双向链表,map,回调函数
type Cache struct {
maxBytes int64 // 允许的最大内存
nbytes int64 // 已使用的内存
ll *list.List //双向链表
cache map[string]*list.Element // map
OnEvicted func(key string, value Value) //是某条记录被移除时的回调函数
}
- 实现双向链表的数据类型,便于删除双向链表时根据key删除map的值
type entry struct {
// 代表双向链表的数据类型
key string
value Value
}
- 为了存取数据的通用性,实现Len()获取元素个数
type Value interface {
Len() int
}
初始化缓存函数
func New(maxBytes int64, onEvicted func(string, Value)) *Cache {
// 创建
return &Cache{
maxBytes: maxBytes,
ll: list.New(),
cache: make(map[string]*list.Element),
OnEvicted: onEvicted,
}
}
实现查找功能
这个函数首先在缓存(map)中查询是否存在,如果存在就返回这个值,并且把这个值放在双向链表的首部,也标记成最新出现
func (c *Cache) Get(key string) (value Value, ok bool) {
// 查找
if ele, ok := c.cache[key]; ok {
c.ll.MoveToFront(ele) // 移动到队首
kv := ele.Value.(*entry) // 找到值
return kv.value, true
}
return
}
在这里写的时候 我对这段代码有疑惑
kv := ele.Value.(*entry) // 找到值
错误的认为ele.Value.(T) 是 Value中的一个成员变量,但实际又不是
实际上这是接口类型的类型转换 Value.(Type) 是吧接口类型Value转换成Type类型
补充知识 :Go语言接口和类型之间的转换
类型断言
类型断言用于将接口类型转换为指定类型,其语法为:
value.(type) 或者 value.(T)
类型转换
类型转换用于将一个接口类型的值转换为另一个接口类型,其语法为:
T(value)
实现内存淘汰功能 即删除
内存删除就是移除最近最少访问的节点,就是删除队尾,然后修改当前缓存大小,使用回调函数通知系统
func (c *Cache) RemoveOldest()