LRU全称Least Recently Used,也就是 最近最少使用的意思,是一种内存管理算法,该算法最早应用于 Linux操作系统。
这个算法基于一种假设:长期不被使 用的数据,在未来被用到的几率也不大。因此,当数据所占内存达 到一定阈值时,我们要移除掉最近最少被使用的数据。
LRU算法中,使用了一种有趣的数据结构,这种数据结构叫作哈希 链表。
我们都知道,哈希表是由若干个Key-Value组成的。在“逻辑”上,这些 Key-Value是无所谓排列顺序的,谁先谁后都一样。
在哈希链表中,这些Key-Value不再是彼此无关的存在,而是被一个链 条串了起来。每一个Key-Value都具有它的前驱Key-Value、后继Key- Value,就像双向链表中的节点一样。
依靠哈希链表的有序性,我们可以把 Key-Value按照最后的使用时间进行排序。
LRU算法的基本思路
-
假设使用哈希链表来缓存用户信息,目前缓存了4个用户,这4个用户
是按照被访问的时间顺序依次从链表右端插入的。
-
如果这时业务方访问用户5,由于哈希链表中没有用户5的数据,需要
从数据库中读取出来,插入到缓存中。此时,链表最右端是最新被访问
的用户5,最左端是最近最少被访问的用户1。
-
接下来,如果业务方访问用户2,哈希链表中已经存在用户2的数据,
这时我们把用户2从它的前驱节点和后继节点之间移除,重新插入链表
的最右端。此时,链表的最右端变成了最新被访问的用户2,最左端仍
然是最近最少被访问的用户1
-
接下来,如果业务方请求修改用户4的信息。同样的道理,我们会把
用户4从原来的位置移动到链表的最右侧,并把用户信息的值更新。这
时,链表的最右端是最新被访问的用户4,最左端仍然是最近最少被访
问的用户1。
-
后来业务方又要访问用户6,用户6在缓存里没有,需要插入哈希链表
中。假设这时缓存容量已经达到上限,必须先删除最近最少被访问的数
据,那么位于哈希链表最左端的用户1就会被删除,然后再把用户6插入
最右端的位置。
代码实现
package algorithm
type lruNode struct {
key interface{}
data interface{}
next *lruNode
prev *lruNode
}
type LruCache struct {
limit int
head *lruNode
end *lruNode
value map[interface{}]*lruNode
}
func (l *LruCache) Get(key interface{}) (interface{},bool) {
a,b := l.value[key]
return a,b
}
func (l *LruCache) Add(key interface{}, value interface{}) {
if len(l.value) >= l.limit {
oldKey := l.removeNode(l.head)
delete(l.value, oldKey)
}
node := &lruNode{data: value, key: key}
if l.end != nil {
l.end.next = node
node.prev = l.end
node.next = nil
}
l.end = node
if l.head == nil {
l.head = node
}
l.value[key] = node
}
func (l *LruCache) removeNode(node *lruNode) interface{} {
if node == l.head && node == l.end {
} else if node == l.end {
l.end = l.end.prev
l.end.next = nil
} else if node == l.head {
l.head = l.head.next
l.head.prev = nil
} else {
node.prev.next = node.next
node.next.prev = node.prev
}
return node.key
}
func NewLruCache(cap int) *LruCache {
return &LruCache{limit: cap, value: make(map[interface{}]*lruNode)}
}