常见缓存算法FIFO、LFU、LRU
缓存算法也称为缓存淘汰算法,或被称为页面置换算法。这些算法在不同层次的缓存上执行时拥有不同的效率和代价,需根据具体场合选择最合适的一种
FIFO
先进先出算法,淘汰最早进入的
- FIFO(First in First out),先进先出。其实在操作系统的设计理念中很多地方都利用到了先进先出的思想,比如作业调度(先来先服务)
- 这个原则简单、且符合人们的惯性思维,具备公平性,并且实现起来简单,直接使用数据结构中的队列即可实现
- 核心原则就是:如果一个数据最先进入缓存中,则应该最早淘汰掉。也就是说,当缓存满的时候,应当把最先进入缓存的数据给淘汰掉。
- 在FIFO Cache中应该支持以下操作
- get(key):如果Cache中存在该key,则返回对应的value值,否则,返回-1
- set(key,value):如果Cache中存在该key,则重置value值
- 如果不存在该key,则将该key插入到到Cache中,若Cache已满,则淘汰最早进入Cache的数据
举个例子:假如Cache大小为3,访问数据序列为set(1,1),set(2,2),set(3,3),set(4,4),get(2),set(5,5)
则Cache中的数据变化为:
(1,1) set(1,1)
(1,1) (2,2) set(2,2)
(1,1) (2,2) (3,3) set(3,3)
(2,2) (3,3) (4,4) set(4,4)
(2,2) (3,3) (4,4) get(2)
(3,3) (4,4) (5,5) set(5,5)
实现思路
- 利用一个双向链表保存数据
- 当来了新的数据之后便添加到链表末尾
- 如果Cache存满数据,则把链表头部数据删除
- 然后把新的数据添加到链表末尾
- 访问数据的时候,如果在Cache中存在该数据的话,则返回对应的value值
- 否则返回-1。如果想提高访问效率,可以利用hashmap来保存每个key在链表中对应的位置
LFU
最近最少使用算法
- LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路
- LFU更加注重于使用的频率(访问次数)
- 假设缓存大小为3,数据访问序列为set(2,2),set(1,1),get(2),get(1),get(2),set(3,3),set(4,4)
- 则在set(4,4)时对于LFU算法应该淘汰(3,3),而LRU应该淘汰(1,1)
- LFU Cache应该支持的操作为
- get(key):如果Cache中存在该key,则返回对应的value值,否则,返回-1
- set(key,value):如果Cache中存在该key,则重置value值;如果不存在该key,则将该key插入到到Cache中,若Cache已满,则淘汰最少访问的数据
实现思路
- LFU算法最简单的一种设计思路就是 利用一个数组存储 数据项,用hashmap存储每个数据项在数组中对应的位置,然后为每个数据项设计一个访问频次,当数据项被命中时,访问频次自增,在淘汰的时候淘汰访问频次最少的数据。这样一来的话,在插入数据和访问数据的时候都能达到O(1)的时间复杂度,在淘汰数据的时候,通过选择算法得到应该淘汰的数据项在数组中的索引,并将该索引位置的内容替换为新来的数据内容即可,这样的话,淘汰数据的操作时间复杂度为O(n)
- 还有一种实现思路就是利用 小顶堆+hashmap,小顶堆插入、删除操作都能达到O(logn)时间复杂度,因此效率相比第一种实现方法更加高效
LRU
最近最久未使用算法
LRU(The Least Recently Used),注重的是以时间衡量
- LRU的淘汰规则是基于访问时间
实现思路
- 利用链表和hashmap。当需要插入新的数据项的时候,如果新数据项在链表中存在(一般称为命中),则把该节点移到链表头部
- 如果不存在,则新建一个节点,放到链表头部
- 若缓存满了,则把链表最后一个节点删除即可
- 在访问数据的时候,如果数据项在链表中存在,则把该节点移到链表头部,否则返回-1
- 这样一来在链表尾部的节点就是最近最久未访问的数据项
OPT
最佳页面替换算法(OPTimal replacement,OPT)
- 淘汰以后不会使用的页面
- 发现 Belady 异常(Belady异常是一个计算机科学术语,会出现分配的页面数增多但缺页率反而提高的异常现象)的一个结果是寻找最优页面置换算法
- 这个算法具有所有算法的最低的缺页错误率,并且不会遭受 Belady 异常。这种算法确实存在,它被称为 OPT 或 MIN
- 这种页面置换算法确保对于给定数量的帧会产生最低的可能的缺页错误率
Clock
时钟置换算法
- 是一种性能和开销相对均衡的算法。也被称为最近未使用算法(Not Recenty Use,NRE)
- 简单的CLOCK算法为页面设置一个访问位,再将内存中的页面通过链接指针链接成一个循环队列。当某页被访问的时候,其访问位置设置为1。当淘汰一个页面的时候,只需要检查页的访问位,如果是0,就选择将该页换出(之前没被访问过);如果是1,则将其置换为0,暂时不换出(上次访问过,可能还会继续访问)。然后接着检查下一个页面,如果第一轮扫描结束后,所以的页面的访问位都是1,则会将这些页面全部置0,再进行第二次扫描(第二轮扫描中肯定有为0的页面)。因此,简单的CLOCK算法选择淘汰一个页面最多经过两轮扫描
MFU
Most Frequently Used算法
- 它选择访问次数最多的页面进行替换,也就是最经常使用