借助go标准库container/list 双向链表,来实现LRU
LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓存容量满的时候,优先淘汰最近很少使用的数据。
实现 LRU 缓存的常用方法是使用有界队列。实现 LRU 的关键是将所有最近使用的数据放在队列的开头。在每次插入之前,我们检查队列是否已满。如果队列已满,我们将删除其最后一个元素,并将新节点插入队列的开头。如果队列未满,我们只需将数据添加到队列的开头。
LRU缓存的思想
- 固定缓存大小,需要给缓存分配一个固定的大小。
- 每次读取缓存都会改变缓存的使用时间,将缓存的存在时间重新刷新。
- 需要在缓存满了后,将最近最久未使用的缓存删除,再添加最新的缓存。
1:加入新的数据
2:队列空间满了的时候,尾部的数据会被淘汰掉
3:如果要访问队列中的数据,该数据会被移动到队列的头部
缺点:频繁地访问,会造成数据被访问到的几率会下降,此外,这种normal模式也叫做LRU-1,即最近使用过1次。
【命中率】
当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
【复杂度】
实现简单。
【代价】
命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
package mainimport ("container/list""errors""fmt")type CacheNode struct {Key,Value interface{}}func (cnode *CacheNode) NewCacheNode(k,v interface{}) *CacheNode {return &CacheNode{k,v}}type LRUCache struct {Capacity intdlist *list.ListcacheMap map[interface{}]*list.Element}func NewLRUCache(cap int) *LRUCache {return &LRUCache{Capacity:cap,dlist: list.New(),cacheMap: make(map[interface{}]*list.Element)}}func (lru *LRUCache) Size() int {return lru.dlist.Len()}func (lru *LRUCache) Set(k,v interface{}) error {if lru.dlist == nil {return errors.New("LRUcache 结构未初始化")}if pElement, ok := lru.cacheMap[k]; ok {lru.dlist.MoveToFront(pElement)pElement.Value.(*CacheNode).Value = vreturn nil}newElement := lru.dlist.PushFront(&CacheNode{k,v})lru.cacheMap[k] = newElementif lru.dlist.Len() > lru.Capacity {lastElement := lru.dlist.Back()if lastElement == nil {return nil}CacheNode := lastElement.Value.(*CacheNode)delete(lru.cacheMap,CacheNode.Key)lru.dlist.Remove(lastElement)}return nil}func (lru *LRUCache) Get(k interface{}) (v interface{}, ret bool, err error) {if lru.cacheMap == nil {return v,false,errors.New("LRUCache 结构体未初始化")}if pElement, ok := lru.cacheMap[k]; ok {lru.dlist.MoveToFront(pElement)return pElement.Value.(*CacheNode).Value,true,nil}return v,false,nil}func (lru *LRUCache) Remove(k interface{}) bool {if lru.cacheMap == nil {return false}if pElement, ok := lru.cacheMap[k];ok {cacheNode := pElement.Value.(*CacheNode)delete(lru.cacheMap,cacheNode.Key)lru.dlist.Remove(pElement)return true}return false}func main() {lru := NewLRUCache(3)lru.Set(10,"value1")lru.Set(20,"value2")lru.Set(30,"value3")lru.Set(50,"value5")lru.Get(10)lru.Get(20)//lru.Set(50,"value5")fmt.Println("LRU size", lru.Size())v,ret,_ := lru.Get(30)if ret {fmt.Println("Get(30):", v)}//if lru.Remove(30) {// fmt.Println("Remove(30): true")//} else {// fmt.Println("Remove(30): false")//}fmt.Println("LRU size", lru.Size())fmt.Println(lru.dlist.Front().Value)fmt.Println(lru.dlist.Back().Value)}
