LRU算法记录一下

513 阅读5分钟

定义

  • lru 算法,全称是Least Recently Used。
  • lru是一种淘汰算法,淘汰掉最近最少使用的元素。当一个数据在最近一段时间没有被访问到,那么在之后的一段时间内也极少可能会被访问到,所以当数据池数据满了后,就淘汰删除掉最久没有访问到的数据。

实现方式

可以用数组实现

  • 可以为数据维护一个数据访问次数标识。
  • 首先定义一个定长数组。当一个数据加入到数组时,先找一个数组里是否有相同数据,如果没有就把数据添加到数组空位并把数据的访问次数标识设置为0,并且把数组里其他数据的访问次数标识都+1。如果在数组里找到相同数据,就把数据的访问次数标识设置为0,并且把数组里其他数据的访问次数标识都+1。如果加入新的数据时,数组里已经没有了剩余空位。就判断数组里数据访问次数最大的一个数据进行淘汰。
  • 用数组实现缺点是每插入数据都要遍历数据并且修改数据访问次数标识,所以时间复杂度是O(N)。
  • 而且这里淘汰的数据算法也不完美。当最后插入新数据时数组已满了,然后发现数组有两个数据的访问标识相等且都值都是最大,那么还应该在这两个数据中找到在数组里待的时间较长的数据,然后把这个数据进行淘汰。

可以用链表实现

  • 用数组实现会导致遍历整个数组,所以我们可以找一个有序的数据结构来实现lru算法。那么就可以使用链表数据结构来实现。
  • 首先定义一个链表,我们往头部节点插入数据,尾部节点淘汰数据。当添加一个数据时,先遍历链表,如果找到对应数据则把数据移到链表头部节点。如果添加的是新数据,那么则也从链表头部添加数据。如果链表数据容量满了后就从链表尾部淘汰数据即可。
  • 这种用链表实现lru算法的缺点和用数组实现的缺点是一样的,时间复杂度是O(n)。数据在插入是也会先遍历链表进行查询数据是否已经存在。

可以用HashMap + linkedHashMap实现

  • 如果我们想要一种时间复杂度为O(1)的实现算法,利用HashMap的快速查找和linkedHashMap双向链表的有序排列和快速插入删除来实现算法。
  • 定义一个hashmap和linkedhashmap。添加一个数据时,先对数据自建一个key,并把key和数据一起放在链表头部,在hashmap里存放key和以链表节点为value。如果hashMap里面key重复,说明数据已经存在,那么就把hashMap中key对应的value节点删除并重新添加在链表头部(这里如果已经在头部就不用操作)。最后如果添加数据是链表的数据容量满了,那么去除链表尾部节点的数据,并且删除对应hashMap里的key-value(这里因为链表节点里包含有key和value数据,所以可以根据链表找到对应hashMap里的key-value)。
  • 这里没有遍历链表结构,利用了hashMap快速查找和双向链表的快速删除和插入(单向链表在删除是无法快速知道前一个node节点)实现lru算法。所以时间效率上要比上面两个要快很多。

实际案例

mysql中lru淘汰算法的使用

  • mysql的数据是存放在磁盘中的,而进行磁盘IO又是非常耗时的操作,所以mysql为了减少磁盘IO而设计了缓存池 bufferPool。bufferPool是一块连续的内存,默认大小有128M,可以修改配置文件进行自定义设置。bufferPool分为很多个大小为16K的数据页(里面的数据大概占了15K,另外的1K 数据用来描述当前页的各种信息,包括上下页,数据的磁盘地址等等)。
  • 既然是缓存池,那么就会用满了的时候,mysql就使用了lru算法进行淘汰数据。在池里最少使用的数据就会被淘汰。但是mysql的预读操作或全表扫描炒作,就可能不需要的数据给替换出来,减低缓存数据的命中率,所以mysql的lru链表就分为了热数据和冷数据。

redis中lru淘汰算法的使用

  • redis作为高速内存缓存也是有内存使用完了的情况,那么redis有几种默认的淘汰数据策略。

    • noenviction:默认策略。不继续执行写请求(DEL 请求可以处理),读请求可以继续进行。这样可以保证不会丢失数据,但是会让线上的业务不能持续进行。
    • volatile-lru:从已设置过期时间的数据集中挑选最近最少使用的数据淘汰。没有设置过期时间的 key 不会被淘汰。
    • volatile-random:从已设置过期时间的数据集中随机选择数据淘汰。
    • volatile-ttl:从已设置过期时间的数据集中挑选将要过期的数据淘汰。
    • allkeys-lru:和 volatile-lru 不同的是,这个策略要淘汰的 key 对象是全体的 key 集合。
    • allkeys-random:从所有数据集中随机选择数据淘汰。
    • volatile-lfu:对有过期时间的 key 采用 LFU 淘汰算法(redis4.0新加入)
    • allkeys-lfu:对全部 key 采用 LFU 淘汰算法(redis4.0新加入)
  • 虚心学习,共同进步 -_- lru详细参考链接