目录1. LRU 淘汰算法LRU(Least Recently Used)
1.1 定义
type Cache struct { maxBytes int64 //允许使用的最大内存 Nbytes int64 //当前已使用的内存 ll *list.List //双向列表 cache map[string]*list.Element //键是字符串,值是双向链表中对应节点的指针 OnEvicted func(key string, value Value)//是某条记录被`移除`时的回调函数,可以为 nil。}// Value use Len to count how many bytes it takes/* 使用Len来计算需要多少个字节 允许值是实现了 Value 接口的任意类型 该接口只包含了一个方法 Len() int,用于返回值所占用的内存大小。 */type Value interface {Len() int} 1.2 实例化
// 对于Cache进行了实例化,能New一个Cache出来。func New(maxBytes int64, onEvicted func(string, Value)) *Cache {return &Cache{ maxBytes: maxBytes, ll: list.New(), cache: make(map[string]*list.Element), OnEvicted: onEvicted,}} 1.3 增加或更新
func (c *Cache) Add(key string, value Value) {if ele, ok := c.cache[key]; ok { c.ll.MoveToFront(ele) //这个键存在,移动到队尾 kv := ele.Value.(*entry)//kv 是值, 因为value是接口, //因此类型断言设置为*entry c.Nbytes += int64(value.Len()) - int64(kv.value.Len()) // 计算现在的缓存,以至于将其中的 kv.value = value } else { ele := c.ll.PushFront(&entry{key, value}) //没有的话,就创建这一个键值对 c.cache[key] = ele c.Nbytes += int64(len(key)) + int64(value.Len())}for c.maxBytes != 0 && c.maxBytes < c.Nbytes {// 如果超出缓存容量 c.RemoveOldest()// 那么移除双向链表中的最后一项}} 1.4 查询
// Get look ups a key's valuefunc (c *Cache) Get(key string) (value Value, ok bool) {if ele, ok := c.cache[key]; ok { // 如果这个需要查找的值是存在的,则将对应的节点移动到队尾,并返回查找到的值 c.ll.MoveToFront(ele)// 双向链表作为队列,队首队尾是相对的,在这里约定 front 为队尾 kv := ele.Value.(*entry)return kv.value, true}return} 1.5 删除
// RemoveOldest removes the oldest item//这里的删除,实际上是缓存淘汰。即移除最近最少访问的节点(队首)func (c *Cache) RemoveOldest() { ele := c.ll.Back() //取到队首节点if ele != nil { c.ll.Remove(ele) //从链表中删除 kv := ele.Value.(*entry) //获取值delete(c.cache, kv.key) //从字典 c.cache 中删除该节点的映射关系。 c.Nbytes -= int64(len(kv.key)) + int64(kv.value.Len()) //计算这里大小if c.OnEvicted != nil { c.OnEvicted(kv.key, kv.value)}}} 由于这种方法是无法支持并发的,因为一旦多个线程进行竞争,就会导致写入 |
|