1.原理
LRU
(least recently used, 最近最少使用),LRU
算法的设计原则是:
如果一个 数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。
(最开始了解这个算法的时候一直不明白为什么要淘汰最近的数据,而不是淘汰最远的数据,看到设计原则的时候才恍然大悟)
值得一提的是redis
采用拉链法解决哈希碰撞,采用的头插法将最新的数据存储到链表的头部,其设计思想也是
最先存储的数据也是最快被使用到的,从而提高数据的查找效率
2.实现
可能大多数人会想到:用一个数组来存储数据,给每一个数据项标记一个访问的时间戳;每次插入新数据项的时候,将已存在的数据项的时间戳自增,并将新数据的时间戳置为0
;每次访问数据项的时候,将被访问的数据项的时间戳置为0
。
当数组空间已满的时候,将时间戳最大的数据项淘汰。这种实现的方式很简单,但是有个问题,需要不挺的维护数据项的访问时间戳,另外在插入,删除以及访问数据时,时间复杂的都是O(n)
。
我们需要关注淘汰算法的读性能和写性能,理想的LRU
应该可以在O(1)
的时间内读取一条数据或更新一条数据,也就是说读写的时间复杂度都是O(1)
。
所以最好的实现方式就是利用链表和HashTable
HashTable
主要用于查找数据,保证通过key
访问数据的时间复杂度为O(1)
。而用链表用来保存缓存数据。
算法的实现过程如下
步骤:
1。新数据插入到链表头部;(O(1))
2。每当缓存命中即缓存数据被访问的时候,将数据移到链表头部;(不需要移动大量数据,只需修改链表指针O(1)
)
3。当链表满的时候,将链表尾部的数据清除;(O(1))
3.php实现
php代码转自github:php-lruchache
4.例题
题目是某缓存系统采用LRU算法,假定缓存容量为4,并且初始为空,那么在顺序访问以下数据项的时候,
1,5,1,3,5,2,4,1,2
出现缓存直接命中的次数是(3)次,最后缓存中即将准备淘汰的数据项是(5)。
访问过程如下:
get | memory | hit |
---|---|---|
1 | 1 | |
5 | 5 1 | |
1 | 1 5 | true |
3 | 3 1 5 | |
5 | 5 3 1 | true |
2 | 2 5 3 1 | |
4 | 4 2 5 3 (尾部的1淘汰) | |
1 | 1 4 2 5 (尾部的3淘汰) | |
2 | 2 1 4 5 | true |
结论:
缓存命中3
次
5
在链表尾部,缓存已满,所以即将淘汰的是5