缓存算法(FIFO 、LRU、LFU三种算法的区别)

 

缓存算法(FIFO 、LRU、LFU三种算法的区别)

 

FIFO算法#

FIFO 算法是一种比较容易实现的算法。它的思想是先进先出(FIFO,队列),这是最简单、最公平的一种思想,即若是一个数据是最早进入的,那么能够认为在未来它被访问的可能性很小。空间满的时候,最早进入的数据会被最先置换(淘汰)掉html

FIFO 算法的描述:设计一种缓存结构,该结构在构造时肯定大小,假设大小为 K,并有两个功能:java

  1. set(key,value):将记录(key,value)插入该结构。当缓存满时,将最早进入缓存的数据置换掉。
  2. get(key):返回key对应的value值。

实现:维护一个FIFO队列,按照时间顺序将各数据(已分配页面)连接起来组成队列,并将置换指针指向队列的队首。再进行置换时,只需把置换指针所指的数据(页面)顺次换出,并把新加入的数据插到队尾便可。算法

缺点:判断一个页面置换算法优劣的指标就是缺页率,而FIFO算法的一个显著的缺点是,在某些特定的时刻,缺页率反而会随着分配页面的增长而增长,这称为Belady现象。产生Belady现象现象的缘由是,FIFO置换算法与进程访问内存的动态特征是不相容的,被置换的内存页面每每是被频繁访问的,或者没有给进程分配足够的页面,所以FIFO算法会使一些页面频繁地被替换和从新申请内存,从而致使缺页率增长。所以,如今再也不使用FIFO算法sql

LRU算法#

LRU(The Least Recently Used,最近最久未使用算法)是一种常见的缓存算法,在不少分布式缓存系统(如Redis, Memcached)中都有普遍使用。数组

LRU算法的思想是:若是一个数据在最近一段时间没有被访问到,那么能够认为在未来它被访问的可能性也很小。所以,当空间满时,最久没有访问的数据最早被置换(淘汰)缓存

LRU算法的描述: 设计一种缓存结构,该结构在构造时肯定大小,假设大小为 K,并有两个功能:markdown

  1. set(key,value):将记录(key,value)插入该结构。当缓存满时,将最久未使用的数据置换掉。
  2. get(key):返回key对应的value值。

实现:最朴素的思想就是用数组+时间戳的方式,不过这样作效率较低。所以,咱们能够用双向链表(LinkedList)+哈希表(HashMap)实现(链表用来表示位置,哈希表用来存储和查找),在Java里有对应的数据结构LinkedHashMap数据结构

LInkedHashMap#

利用JavaLinkedHashMap用很是简单的代码来实现基于LRU算法的Cache功能分布式

Copy
import java.util.LinkedHashMap; import java.util.Map; /** * 简单用LinkedHashMap来实现的LRU算法的缓存 */ public class LRUCache<K, V> extends LinkedHashMap<K, V> { private int cacheSize; public LRUCache(int cacheSize) { super(16, (float) 0.75, true); this.cacheSize = cacheSize; } protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > cacheSize; } }

测试:post

Copy
import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class LRUCacheTest { private static final Logger log = LoggerFactory.getLogger(LRUCacheTest.class); private static LRUCache<String, Integer> cache = new LRUCache<>(10); public static void main(String[] args) { for (int i = 0; i < 10; i++) { cache.put("k" + i, i); } log.info("all cache :'{}'",cache); cache.get("k3"); log.info("get k3 :'{}'", cache); cache.get("k4"); log.info("get k4 :'{}'", cache); cache.get("k4"); log.info("get k4 :'{}'", cache); cache.put("k" + 10, 10); log.info("After running the LRU algorithm cache :'{}'", cache); } }

Output:

Copy
all cache :'{k0=0, k1=1, k2=2, k3=3, k4=4, k5=5, k6=6, k7=7, k8=8, k9=9}' get k3 :'{k0=0, k1=1, k2=2, k4=4, k5=5, k6=6, k7=7, k8=8, k9=9, k3=3}' get k4 :'{k0=0, k1=1, k2=2, k5=5, k6=6, k7=7, k8=8, k9=9, k3=3, k4=4}' get k4 :'{k0=0, k1=1, k2=2, k5=5, k6=6, k7=7, k8=8, k9=9, k3=3, k4=4}' After running the LRU algorithm cache :'{k1=1, k2=2, k5=5, k6=6, k7=7, k8=8, k9=9, k3=3, k4=4, k10=10}'

LFU算法#

LFU(Least Frequently Used ,最近最少使用算法)也是一种常见的缓存算法。

顾名思义,LFU算法的思想是:若是一个数据在最近一段时间不多被访问到,那么能够认为在未来它被访问的可能性也很小。所以,当空间满时,最小频率访问的数据最早被淘汰

LFU 算法的描述:

设计一种缓存结构,该结构在构造时肯定大小,假设大小为 K,并有两个功能:

  1. set(key,value):将记录(key,value)插入该结构。当缓存满时,将访问频率最低的数据置换掉。
  2. get(key):返回key对应的value值。

算法实现策略:考虑到 LFU 会淘汰访问频率最小的数据,咱们须要一种合适的方法按大小顺序维护数据访问的频率。LFU 算法本质上能够看作是一个 top K 问题(K = 1),即选出频率最小的元素,所以咱们很容易想到能够用二项堆来选择频率最小的元素,这样的实现比较高效。最终实现策略为小顶堆+哈希表。

 

参考:缓存算法(FIFO 、LRU、LFU三种算法的区别)

相关文章
相关标签/搜索