faiculty 2020-04-30
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)} }