一:概述

  - LRU 用于管理缓存策略,其本身在 Linux/Redis/Mysql 中均有实现。只是实现方式不尽相同。

  - LRU 算法【Least recently used(最近最少使用)】

  - 根据数据的历史访问记录来进行淘汰数据,其核心思想是 "如果数据最近被访问过,那么将来被访问的几率也更高"。

   - GITHUB

二:单链表实现的 Lru 算法

  - 思路

    - 单链表实现

  - 流程

    - 1. 新数据插入到链表头部;

    - 2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;

    - 3. 当链表满的时候,将链表尾部的数据丢弃。

  - 优点

    - 实现简单。

    - 当存在热点数据时,LRU的效率很好。

  - 缺点

    - 偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。

    - 命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。

  - 流程图

    - 

  - 代码实现(PHP)

      1. /**
      2. * 思路
      3. * 单链表实现
      4. * 原理
      5. * 单链表
      6. * 流程
      7. * 1. 新数据插入到链表头部;
      8. * 2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
      9. * 3. 当链表满的时候,将链表尾部的数据丢弃。
      10. * 优点
      11. * 实现简单。
      12. * 当存在热点数据时,LRU的效率很好。
      13. * 缺点
      14. * 偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
      15. * 命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
      16. */
      17. class Lru
      18. {
      19. public static $lruList = []; // 顺序存储单链表结构
      20. public static $maxLen = ; // 链表允许最大长度
      21. public static $nowLen = ; // 链表当前长度
      22.  
      23. /**
      24. * LRU_1 constructor.
      25. * 由于 PHP 不是常驻进程程序,所以链表初始化可以通过 Mysql/Redis 实现
      26. */
      27. public function __construct()
      28. {
      29. self::$lruList = [];
      30. self::$nowLen = count(self::$lruList);
      31. }
      32.  
      33. /**
      34. * 获取 key => value
      35. * @param $key
      36. * @return null
      37. */
      38. public function get($key)
      39. {
      40. $value = null;
      41.  
      42. // lru 队列为空,直接返回
      43. if (!self::$lruList) {
      44. self::$lruList[] = [$key => $this->getData($key)]; // 根据实际项目情况获取数据
      45. self::$nowLen++;
      46. return $value;
      47. }
      48.  
      49. // 查找 lru 缓存
      50. for ($i = ; $i < self::$nowLen; $i++) {
      51.  
      52. // 如果存在缓存,则直接返回,并将数据重新插入链表头部
      53. if (isset(self::$lruList[$i][$key])) {
      54.  
      55. $value = self::$lruList[$i][$key];
      56.  
      57. unset(self::$lruList[$i]);
      58.  
      59. array_unshift(self::$lruList, [$key => $value]);
      60.  
      61. break;
      62. }
      63. }
      64.  
      65. // 如果没有找到 lru 缓存
      66. if (!isset($value)) {
      67.  
      68. // 插入头部
      69. array_unshift(self::$lruList, [$key => $this->getData($key)]); // 根据实际项目情况获取数据
      70. self::$nowLen++;
      71.  
      72. if (self::$nowLen > self::$maxLen) {
      73. self::$nowLen--;
      74. array_pop(self::$lruList);
      75. }
      76. }
      77.  
      78. return $value;
      79.  
      80. }
      81.  
      82. /**
      83. * 输出 Lru 队列
      84. */
      85. public function echoLruList()
      86. {
      87. var_dump(self::$lruList);
      88. }
      89.  
      90. /**
      91. * 根据真实环境获取数据
      92. * @param $key
      93. * @return string
      94. */
      95. public function getData($key)
      96. {
      97. return 'data';
      98. }
      99. }

三:Lru K 算法

  - 思路

    - 为了避免 LRU 的 '缓存污染' 问题

    - 增加一个队列来维护缓存出现的次数。其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。

  - 原理

    - 相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。

    - 只有当数据的访问次数达到K次的时候,才将数据放入缓存。

    - 当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据.

  - 流程

    - 1.数据第一次被访问,加入到访问历史列表;

    - 2.如果数据在访问历史列表里后没有达到K次访问,则按照一定规则 LRU淘汰;

    - 3.当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;

    - 4.缓存数据队列中被再次访问后,重新排序;

    - 5.需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。

  - 优点

    - LRU-K降低了“缓存污染”带来的问题,命中率比LRU要高。

  - 缺点

    - LRU-K队列是一个优先级队列,算法复杂度和代价比较高。

    - 由于LRU-K还需要维护历史队列,所以消耗的内存会更多。

  - 流程图

    - 

  - 代码实现

      1. /**
      2. * 思路
      3. * 为了避免 LRU 的 '缓存污染' 问题
      4. * 增加一个队列来维护缓存出现的次数。其核心思想是将“最近使用过1次”的判断标准扩展为“最近使用过K次”。
      5. * 原理
      6. * 相比LRU,LRU-K需要多维护一个队列,用于记录所有缓存数据被访问的历史。
      7. * 只有当数据的访问次数达到K次的时候,才将数据放入缓存。
      8. * 当需要淘汰数据时,LRU-K会淘汰第K次访问时间距当前时间最大的数据
      9. * 流程
      10. * 1.数据第一次被访问,加入到访问历史列表;
      11. * 2.如果数据在访问历史列表里后没有达到K次访问,则按照一定规则 LRU淘汰;
      12. * 3.当访问历史队列中的数据访问次数达到K次后,将数据索引从历史队列删除,将数据移到缓存队列中,并缓存此数据,缓存队列重新按照时间排序;
      13. * 4.缓存数据队列中被再次访问后,重新排序;
      14. * 5.需要淘汰数据时,淘汰缓存队列中排在末尾的数据,即:淘汰“倒数第K次访问离现在最久”的数据。
      15. * 优点
      16. * LRU-K降低了“缓存污染”带来的问题,命中率比LRU要高。
      17. * 缺点
      18. * LRU-K队列是一个优先级队列,算法复杂度和代价比较高。
      19. * 由于LRU-K还需要维护历史队列,所以消耗的内存会更多。
      20. */
      21. class Lru_K
      22. {
      23. public static $historyList = []; // 访问历史队列
      24. public static $lruList = []; // 顺序存储单链表结构
      25. public static $maxLen = ; // 链表允许最大长度
      26. public static $nowLen = ; // 链表当前长度
      27.  
      28. /**
      29. * LRU_K constructor.
      30. * 由于 PHP 不是常驻进程程序,所以链表初始化可以通过 Mysql/Redis 实现
      31. */
      32. public function __construct()
      33. {
      34. self::$lruList = [];
      35. self::$historyList = [];
      36. self::$nowLen = count(self::$lruList);
      37. }
      38.  
      39. /**
      40. * 获取 key => value
      41. * @param $key
      42. * @return null
      43. */
      44. public function get($key)
      45. {
      46. $value = null;
      47.  
      48. // 查找 lru 缓存
      49. for ($i = ; $i < self::$nowLen; $i++) {
      50.  
      51. // 如果存在缓存,则直接返回,并将数据重新插入链表头部
      52. if (isset(self::$lruList[$i][$key])) {
      53.  
      54. $value = self::$lruList[$i][$key];
      55.  
      56. unset(self::$lruList[$i]);
      57.  
      58. array_unshift(self::$lruList, [$key => $value]);
      59.  
      60. break;
      61. }
      62. }
      63.  
      64. // 如果没有找到 lru 缓存, 则进入历史队列进行计数,当次数大于等于5时候,进入缓存队列
      65. if (!isset($value)) {
      66. self::$historyList[$key]++;
      67.  
      68. $value = $this->getData($key);
      69.  
      70. // 进入缓存队列
      71. if (self::$historyList[$key] >= ) {
      72. array_unshift(self::$lruList, [$key => $value]); // 根据实际项目情况获取数据
      73. self::$nowLen++;
      74.  
      75. if (self::$nowLen > self::$maxLen) {
      76. self::$nowLen--;
      77. array_pop(self::$lruList);
      78. }
      79.  
      80. unset(self::$historyList[$key]); // 历史队列推出
      81. }
      82. }
      83.  
      84. return $value;
      85.  
      86. }
      87.  
      88. /**
      89. * 输出 Lru 队列
      90. */
      91. public function echoLruList()
      92. {
      93. var_dump(self::$lruList);
      94. var_dump(self::$historyList);
      95. }
      96.  
      97. /**
      98. * 根据真实环境获取数据
      99. * @param $key
      100. * @return string
      101. */
      102. public function getData($key)
      103. {
      104. return 'data';
      105. }
      106. }

《算法 - Lru算法》的更多相关文章

  1. 简单物联网:外网访问内网路由器下树莓派Flask服务器

    最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...

  2. 利用ssh反向代理以及autossh实现从外网连接内网服务器

    前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...

  3. 外网访问内网Docker容器

    外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...

  4. 外网访问内网SpringBoot

    外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...

  5. 外网访问内网Elasticsearch WEB

    外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...

  6. 怎样从外网访问内网Rails

    外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...

  7. 怎样从外网访问内网Memcached数据库

    外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...

  8. 怎样从外网访问内网CouchDB数据库

    外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...

  9. 怎样从外网访问内网DB2数据库

    外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...

  10. 怎样从外网访问内网OpenLDAP数据库

    外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...

随机推荐

  1. BM递推杜教版【扩展】

    也就是模数不是质数的时候, //下面的板子能求质数和非质数,只需要传不同的参数. #include <cstdio> #include <cstdlib> #include & ...

  2. 【BZOJ4237】 稻草人 CDQ分治+单调栈

    ## 题目描述 JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典. 有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地.和启示中的一样,田地需要满足以下 ...

  3. Mscordacwks.dll/SOS.dll 调试归档

    找到个好东西 为什么要归档 此存档提供帮助,并可能提供对以下问题的答案 是否可以使WinDBG在符号存储中找到mscordacwks.dll?, Windbg需要不同版本的mscordacwks.dl ...

  4. nginx配置ssl加密(单/双向认证、部分https)

    nginx下配置ssl本来是很简单的,无论是去认证中心买SSL安全证书还是自签署证书,但最近公司OA的一个需求,得以有个机会实际折腾一番.一开始采用的是全站加密,所有访问http:80的请求强制转换( ...

  5. 洛谷P4343 [SHOI2015]自动刷题机

    题目 易得该题目中的\(n\)和\(k\)具有单调性,满足二分的性质,因此该题目而已用二分来枚举\(n\),然后对于每个\(n\)模拟出它所对应的\(k\),然后注意注意代码细节,并且当当前\(k\) ...

  6. C博客作业01--分支顺序结构

    1.展示PTA总分 2.本章学习总结 ①C语言数据类型 ② if-else语句 if (条件) { 语句A; } else { 语句B; } 在if (条件)后不加":" 要用&q ...

  7. mysql 自联结

    mysql> select * from test; +----+------------+-------+-----------+ | id | name | score | subject ...

  8. 【Excel】多条件查找

    例如下图:要求在单元格从C10中根据分类与名称找出相应的数量 1.VLOOKUP函数(数组公式) {=VLOOKUP(A10&B10,IF({1,0},A2:A6&B2:B6,C2:C ...

  9. Win10电脑桌面壁纸自动变成黑色无法更换怎么解决

    很多用户在升级到win10之后,发现在使用过程中经常会碰到一些问题,就是电脑桌面壁纸总是会自动变成黑色,而且无法设置桌面背景壁纸,这是怎么回事呢,出现这样的问题可能是因为系统不是正版,或者是电脑设置不 ...

  10. win10如何删除自己设置过的头像

    把      %appdata%\Microsoft\Windows\AccountPictures  输入到地址栏  然后删除你想删除的照片即可