借助go标准库container/list 双向链表,来实现LRU

    LRU 是 Least Recently Used 的缩写,这种算法认为最近使用的数据是热门数据,下一次很大概率将会再次被使用。而最近很少被使用的数据,很大概率下一次不再用到。当缓存容量满的时候,优先淘汰最近很少使用的数据。
    实现 LRU 缓存的常用方法是使用有界队列。实现 LRU 的关键是将所有最近使用的数据放在队列的开头。在每次插入之前,我们检查队列是否已满。如果队列已满,我们将删除其最后一个元素,并将新节点插入队列的开头。如果队列未满,我们只需将数据添加到队列的开头。

    LRU缓存的思想

    • 固定缓存大小,需要给缓存分配一个固定的大小。
    • 每次读取缓存都会改变缓存的使用时间,将缓存的存在时间重新刷新。
    • 需要在缓存满了后,将最近最久未使用的缓存删除,再添加最新的缓存。
      1:加入新的数据
      2:队列空间满了的时候,尾部的数据会被淘汰掉
      3:如果要访问队列中的数据,该数据会被移动到队列的头部

    缺点:频繁地访问,会造成数据被访问到的几率会下降,此外,这种normal模式也叫做LRU-1,即最近使用过1次。
    【命中率】
    当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
    【复杂度】
    实现简单。
    【代价】
    命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。

    1. package main
    2. import (
    3. "container/list"
    4. "errors"
    5. "fmt"
    6. )
    7. type CacheNode struct {
    8. Key,Value interface{}
    9. }
    10. func (cnode *CacheNode) NewCacheNode(k,v interface{}) *CacheNode {
    11. return &CacheNode{k,v}
    12. }
    13. type LRUCache struct {
    14. Capacity int
    15. dlist *list.List
    16. cacheMap map[interface{}]*list.Element
    17. }
    18. func NewLRUCache(cap int) *LRUCache {
    19. return &LRUCache{
    20. Capacity:cap,
    21. dlist: list.New(),
    22. cacheMap: make(map[interface{}]*list.Element)}
    23. }
    24. func (lru *LRUCache) Size() int {
    25. return lru.dlist.Len()
    26. }
    27. func (lru *LRUCache) Set(k,v interface{}) error {
    28. if lru.dlist == nil {
    29. return errors.New("LRUcache 结构未初始化")
    30. }
    31. if pElement, ok := lru.cacheMap[k]; ok {
    32. lru.dlist.MoveToFront(pElement)
    33. pElement.Value.(*CacheNode).Value = v
    34. return nil
    35. }
    36. newElement := lru.dlist.PushFront(&CacheNode{k,v})
    37. lru.cacheMap[k] = newElement
    38. if lru.dlist.Len() > lru.Capacity {
    39. lastElement := lru.dlist.Back()
    40. if lastElement == nil {
    41. return nil
    42. }
    43. CacheNode := lastElement.Value.(*CacheNode)
    44. delete(lru.cacheMap,CacheNode.Key)
    45. lru.dlist.Remove(lastElement)
    46. }
    47. return nil
    48. }
    49. func (lru *LRUCache) Get(k interface{}) (v interface{}, ret bool, err error) {
    50. if lru.cacheMap == nil {
    51. return v,false,errors.New("LRUCache 结构体未初始化")
    52. }
    53. if pElement, ok := lru.cacheMap[k]; ok {
    54. lru.dlist.MoveToFront(pElement)
    55. return pElement.Value.(*CacheNode).Value,true,nil
    56. }
    57. return v,false,nil
    58. }
    59. func (lru *LRUCache) Remove(k interface{}) bool {
    60. if lru.cacheMap == nil {
    61. return false
    62. }
    63. if pElement, ok := lru.cacheMap[k];ok {
    64. cacheNode := pElement.Value.(*CacheNode)
    65. delete(lru.cacheMap,cacheNode.Key)
    66. lru.dlist.Remove(pElement)
    67. return true
    68. }
    69. return false
    70. }
    71. func main() {
    72. lru := NewLRUCache(3)
    73. lru.Set(10,"value1")
    74. lru.Set(20,"value2")
    75. lru.Set(30,"value3")
    76. lru.Set(50,"value5")
    77. lru.Get(10)
    78. lru.Get(20)
    79. //lru.Set(50,"value5")
    80. fmt.Println("LRU size", lru.Size())
    81. v,ret,_ := lru.Get(30)
    82. if ret {
    83. fmt.Println("Get(30):", v)
    84. }
    85. //if lru.Remove(30) {
    86. // fmt.Println("Remove(30): true")
    87. //} else {
    88. // fmt.Println("Remove(30): false")
    89. //}
    90. fmt.Println("LRU size", lru.Size())
    91. fmt.Println(lru.dlist.Front().Value)
    92. fmt.Println(lru.dlist.Back().Value)
    93. }