LRU算法原理,图片来自https://mp.weixin.qq.com/s/h_Ns5HY27NmL_odCYLgx_Q
    1.假设我们使用哈希链表来缓存用户信息,目前缓存了4个用户,这4个用户是按照时间顺序依次从链表右端插入的
    image.png
    2.此时,业务方访问用户5,由于哈希链表中没有用户5的数据,我们从数据库中读取出来,插入到缓存当中。这时候,链表中最右端是最新访问到的用户5,最左端是最近最少访问的用户1。
    image.png
    3.接下来,业务方访问用户2,哈希链表中存在用户2的数据,我们怎么做呢?我们把用户2从它的前驱节点和后继节点之间移除,重新插入到链表最右端。这时候,链表中最右端变成了最新访问到的用户2,最左端仍然是最近最少访问的用户1
    image.png
    4.接下来,业务方请求修改用户4的信息。同样道理,我们把用户4从原来的位置移动到链表最右侧,并把用户信息的值更新。这时候,链表中最右端是最新访问到的用户4,最左端仍然是最近最少访问的用户1。
    image.png

    5.后来业务方换口味了,访问用户6,用户6在缓存里没有,需要插入到哈希链表。假设这时候缓存容量已经达到上限,必须先删除最近最少访问的数据,那么位于哈希链表最左端的用户1就会被删除掉,然后再把用户6插入到最右端。
    image.png

    简易实现

    1. package main
    2. import (
    3. "errors"
    4. "fmt"
    5. )
    6. type Cache interface {
    7. Len() int64
    8. Set(k string, v interface{}) error
    9. Get(k string) (interface{}, error)
    10. Refresh(k string) error
    11. Del(k string) error
    12. DelOld() error
    13. ForEach()
    14. }
    15. type Node struct {
    16. Key string
    17. Value interface{}
    18. Next *Node
    19. Pre *Node
    20. }
    21. type List struct {
    22. len int64
    23. head *Node
    24. end *Node
    25. }
    26. func (l *List) Len() int64 {
    27. return l.Len()
    28. }
    29. func (l *List) Set(k string, v interface{}) error {
    30. node := &Node{Key: k, Value: v}
    31. if l.len == 0 {
    32. node.Pre, node.Next = nil, nil
    33. l.head, l.end = node, node
    34. } else {
    35. node.Pre, node.Next = l.end, nil
    36. l.end.Next = node
    37. l.end = node
    38. }
    39. l.len++
    40. return nil
    41. }
    42. func (l *List) Refresh(k string) error {
    43. if l.len < 1 { // 只有一个节点,不需要更新
    44. return nil
    45. }
    46. current := l.head
    47. if current.Key == k { // 如果是头节点
    48. l.head = current.Next
    49. l.head.Pre = nil
    50. current.Next = nil
    51. current.Pre = l.end
    52. l.end.Next = current
    53. l.end = current
    54. return nil
    55. }
    56. if l.end.Key == k { // 如果已经是最新的一个元素,不需要更新
    57. return nil
    58. }
    59. for {
    60. if current.Key == k {
    61. current.Pre.Next = current.Next
    62. current.Next.Pre = current.Pre
    63. current.Next = nil
    64. current.Pre = l.end
    65. l.end.Next = current
    66. l.end = current
    67. return nil
    68. }
    69. if current.Next == nil {
    70. return errors.New("没找到该节点")
    71. }
    72. current = current.Next
    73. }
    74. return nil
    75. }
    76. func (l *List) Get(k string) (interface{}, error) {
    77. var v interface{}
    78. var err error
    79. if l.len < 1 {
    80. err = errors.New("list 为空")
    81. return v, err
    82. }
    83. current := l.head
    84. if current.Key == k { // 如果是头节点
    85. v = current.Value
    86. l.head = current.Next
    87. l.head.Pre = nil
    88. current.Next = nil
    89. current.Pre = l.end
    90. l.end.Next = current
    91. l.end = current
    92. return v, err
    93. }
    94. if l.end.Key == k { // 如果尾节点是要找的节点
    95. v = current.Value
    96. return v, err
    97. }
    98. for {
    99. if current.Key == k {
    100. v = current.Value
    101. current.Pre.Next = current.Next
    102. current.Next.Pre = current.Pre
    103. current.Next = nil
    104. current.Pre = l.end
    105. l.end.Next = current
    106. l.end = current
    107. return v, err
    108. }
    109. if current.Next == nil {
    110. err = errors.New("没找到该节点")
    111. return v, err
    112. }
    113. current = current.Next
    114. }
    115. return v, err
    116. }
    117. func (l *List) Del(k string) error {
    118. var err error
    119. current := l.head
    120. if current.Key == k { // 如果是头节点
    121. current.Next.Pre = nil
    122. l.head = current.Next
    123. return err
    124. }
    125. if l.end.Key == k { // 如果尾节点
    126. l.end.Pre.Next = nil
    127. l.end = l.end.Pre
    128. }
    129. for {
    130. if current.Key == k {
    131. current.Pre.Next = current.Next
    132. current.Next.Pre = current.Pre
    133. }
    134. if current.Next == nil {
    135. err = errors.New("没找到该节点")
    136. return err
    137. }
    138. current = current.Next
    139. }
    140. }
    141. func (l *List) DelOld() error {
    142. current := l.head
    143. current.Next.Pre = nil
    144. l.head = current.Next
    145. return nil
    146. }
    147. func (l *List) ForEach() {
    148. current := l.head
    149. for {
    150. fmt.Print(current.Value)
    151. if current.Next == nil {
    152. break
    153. }
    154. current = current.Next
    155. }
    156. fmt.Println()
    157. }
    158. func main() {
    159. var c Cache = &List{}
    160. c.Set("1", 1)
    161. c.Set("2", 2)
    162. c.Set("3", 3)
    163. c.Set("4", 4)
    164. c.Set("5", 5)
    165. c.Refresh("1")
    166. c.ForEach()
    167. c.Get("3")
    168. c.ForEach()
    169. c.Del("4")
    170. c.ForEach()
    171. c.DelOld()
    172. c.ForEach()
    173. }

    一种O1的lru思路
    节点的结构如下:

    type List struct {
        len  int64
        head *Node
        end  *Node
    }
    
    type Node struct {
        Key   string
        Value interface{}
        Next  *Node
        Pre   *Node
    }
    

    普通的链表结构,在更新查找时,复杂度总是O(n),我们可以额外定义一张hash表

    // key: node中的key, value: node节点
    map[string]*Node
    

    例如:此时访问了对象 “A”, 我们需要把”A” 改为最热的状态,需经过如下步骤:

    • 在map中找到对象”A” 对应的Node,成为 AN,复杂度O(1)
    • 将A的上一个节点与下一个节点建立关系,复杂度O(1)
    • 将当前的 end 与 A建立关系,复杂度O(1)
    • 更新当前的 end 为A ,复杂度O(1)