《算法 - Lru算法》
一:概述
- LRU 用于管理缓存策略,其本身在 Linux/Redis/Mysql 中均有实现。只是实现方式不尽相同。
- LRU 算法【Least recently used(最近最少使用)】
- 根据数据的历史访问记录来进行淘汰数据,其核心思想是 "如果数据最近被访问过,那么将来被访问的几率也更高"。
二:单链表实现的 Lru 算法
- 思路
- 单链表实现
- 流程
- 1. 新数据插入到链表头部;
- 2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- 3. 当链表满的时候,将链表尾部的数据丢弃。
- 优点
- 实现简单。
- 当存在热点数据时,LRU的效率很好。
- 缺点
- 偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
- 命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
- 流程图
-
- 代码实现(PHP)
- /**
- * 思路
- * 单链表实现
- * 原理
- * 单链表
- * 流程
- * 1. 新数据插入到链表头部;
- * 2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
- * 3. 当链表满的时候,将链表尾部的数据丢弃。
- * 优点
- * 实现简单。
- * 当存在热点数据时,LRU的效率很好。
- * 缺点
- * 偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
- * 命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
- */
- class Lru
- {
- public static $lruList = []; // 顺序存储单链表结构
- public static $maxLen = ; // 链表允许最大长度
- public static $nowLen = ; // 链表当前长度
- /**
- * LRU_1 constructor.
- * 由于 PHP 不是常驻进程程序,所以链表初始化可以通过 Mysql/Redis 实现
- */
- public function __construct()
- {
- self::$lruList = [];
- self::$nowLen = count(self::$lruList);
- }
- /**
- * 获取 key => value
- * @param $key
- * @return null
- */
- public function get($key)
- {
- $value = null;
- // lru 队列为空,直接返回
- if (!self::$lruList) {
- self::$lruList[] = [$key => $this->getData($key)]; // 根据实际项目情况获取数据
- self::$nowLen++;
- return $value;
- }
- // 查找 lru 缓存
- for ($i = ; $i < self::$nowLen; $i++) {
- // 如果存在缓存,则直接返回,并将数据重新插入链表头部
- if (isset(self::$lruList[$i][$key])) {
- $value = self::$lruList[$i][$key];
- unset(self::$lruList[$i]);
- array_unshift(self::$lruList, [$key => $value]);
- break;
- }
- }
- // 如果没有找到 lru 缓存
- if (!isset($value)) {
- // 插入头部
- array_unshift(self::$lruList, [$key => $this->getData($key)]); // 根据实际项目情况获取数据
- self::$nowLen++;
- if (self::$nowLen > self::$maxLen) {
- self::$nowLen--;
- array_pop(self::$lruList);
- }
- }
- return $value;
- }
- /**
- * 输出 Lru 队列
- */
- public function echoLruList()
- {
- var_dump(self::$lruList);
- }
- /**
- * 根据真实环境获取数据
- * @param $key
- * @return string
- */
- public function getData($key)
- {
- return 'data';
- }
- }
- /**
三: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还需要维护历史队列,所以消耗的内存会更多。
- 流程图
-
- 代码实现
- /**
- * 思路
- * 为了避免 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还需要维护历史队列,所以消耗的内存会更多。
- */
- class Lru_K
- {
- public static $historyList = []; // 访问历史队列
- public static $lruList = []; // 顺序存储单链表结构
- public static $maxLen = ; // 链表允许最大长度
- public static $nowLen = ; // 链表当前长度
- /**
- * LRU_K constructor.
- * 由于 PHP 不是常驻进程程序,所以链表初始化可以通过 Mysql/Redis 实现
- */
- public function __construct()
- {
- self::$lruList = [];
- self::$historyList = [];
- self::$nowLen = count(self::$lruList);
- }
- /**
- * 获取 key => value
- * @param $key
- * @return null
- */
- public function get($key)
- {
- $value = null;
- // 查找 lru 缓存
- for ($i = ; $i < self::$nowLen; $i++) {
- // 如果存在缓存,则直接返回,并将数据重新插入链表头部
- if (isset(self::$lruList[$i][$key])) {
- $value = self::$lruList[$i][$key];
- unset(self::$lruList[$i]);
- array_unshift(self::$lruList, [$key => $value]);
- break;
- }
- }
- // 如果没有找到 lru 缓存, 则进入历史队列进行计数,当次数大于等于5时候,进入缓存队列
- if (!isset($value)) {
- self::$historyList[$key]++;
- $value = $this->getData($key);
- // 进入缓存队列
- if (self::$historyList[$key] >= ) {
- array_unshift(self::$lruList, [$key => $value]); // 根据实际项目情况获取数据
- self::$nowLen++;
- if (self::$nowLen > self::$maxLen) {
- self::$nowLen--;
- array_pop(self::$lruList);
- }
- unset(self::$historyList[$key]); // 历史队列推出
- }
- }
- return $value;
- }
- /**
- * 输出 Lru 队列
- */
- public function echoLruList()
- {
- var_dump(self::$lruList);
- var_dump(self::$historyList);
- }
- /**
- * 根据真实环境获取数据
- * @param $key
- * @return string
- */
- public function getData($key)
- {
- return 'data';
- }
- }
- /**
《算法 - Lru算法》的更多相关文章
- 简单物联网:外网访问内网路由器下树莓派Flask服务器
最近做一个小东西,大概过程就是想在教室,宿舍控制实验室的一些设备. 已经在树莓上搭了一个轻量的flask服务器,在实验室的路由器下,任何设备都是可以访问的:但是有一些限制条件,比如我想在宿舍控制我种花 ...
- 利用ssh反向代理以及autossh实现从外网连接内网服务器
前言 最近遇到这样一个问题,我在实验室架设了一台服务器,给师弟或者小伙伴练习Linux用,然后平时在实验室这边直接连接是没有问题的,都是内网嘛.但是回到宿舍问题出来了,使用校园网的童鞋还是能连接上,使 ...
- 外网访问内网Docker容器
外网访问内网Docker容器 本地安装了Docker容器,只能在局域网内访问,怎样从外网也能访问本地Docker容器? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Docker容器 ...
- 外网访问内网SpringBoot
外网访问内网SpringBoot 本地安装了SpringBoot,只能在局域网内访问,怎样从外网也能访问本地SpringBoot? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装Java 1 ...
- 外网访问内网Elasticsearch WEB
外网访问内网Elasticsearch WEB 本地安装了Elasticsearch,只能在局域网内访问其WEB,怎样从外网也能访问本地Elasticsearch? 本文将介绍具体的实现步骤. 1. ...
- 怎样从外网访问内网Rails
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
- 怎样从外网访问内网Memcached数据库
外网访问内网Memcached数据库 本地安装了Memcached数据库,只能在局域网内访问,怎样从外网也能访问本地Memcached数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装 ...
- 怎样从外网访问内网CouchDB数据库
外网访问内网CouchDB数据库 本地安装了CouchDB数据库,只能在局域网内访问,怎样从外网也能访问本地CouchDB数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Cou ...
- 怎样从外网访问内网DB2数据库
外网访问内网DB2数据库 本地安装了DB2数据库,只能在局域网内访问,怎样从外网也能访问本地DB2数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动DB2数据库 默认安装的DB2 ...
- 怎样从外网访问内网OpenLDAP数据库
外网访问内网OpenLDAP数据库 本地安装了OpenLDAP数据库,只能在局域网内访问,怎样从外网也能访问本地OpenLDAP数据库? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动 ...
随机推荐
- BM递推杜教版【扩展】
也就是模数不是质数的时候, //下面的板子能求质数和非质数,只需要传不同的参数. #include <cstdio> #include <cstdlib> #include & ...
- 【BZOJ4237】 稻草人 CDQ分治+单调栈
## 题目描述 JOI村有一片荒地,上面竖着N个稻草人,村民们每年多次在稻草人们的周围举行祭典. 有一次,JOI村的村长听到了稻草人们的启示,计划在荒地中开垦一片田地.和启示中的一样,田地需要满足以下 ...
- Mscordacwks.dll/SOS.dll 调试归档
找到个好东西 为什么要归档 此存档提供帮助,并可能提供对以下问题的答案 是否可以使WinDBG在符号存储中找到mscordacwks.dll?, Windbg需要不同版本的mscordacwks.dl ...
- nginx配置ssl加密(单/双向认证、部分https)
nginx下配置ssl本来是很简单的,无论是去认证中心买SSL安全证书还是自签署证书,但最近公司OA的一个需求,得以有个机会实际折腾一番.一开始采用的是全站加密,所有访问http:80的请求强制转换( ...
- 洛谷P4343 [SHOI2015]自动刷题机
题目 易得该题目中的\(n\)和\(k\)具有单调性,满足二分的性质,因此该题目而已用二分来枚举\(n\),然后对于每个\(n\)模拟出它所对应的\(k\),然后注意注意代码细节,并且当当前\(k\) ...
- C博客作业01--分支顺序结构
1.展示PTA总分 2.本章学习总结 ①C语言数据类型 ② if-else语句 if (条件) { 语句A; } else { 语句B; } 在if (条件)后不加":" 要用&q ...
- mysql 自联结
mysql> select * from test; +----+------------+-------+-----------+ | id | name | score | subject ...
- 【Excel】多条件查找
例如下图:要求在单元格从C10中根据分类与名称找出相应的数量 1.VLOOKUP函数(数组公式) {=VLOOKUP(A10&B10,IF({1,0},A2:A6&B2:B6,C2:C ...
- Win10电脑桌面壁纸自动变成黑色无法更换怎么解决
很多用户在升级到win10之后,发现在使用过程中经常会碰到一些问题,就是电脑桌面壁纸总是会自动变成黑色,而且无法设置桌面背景壁纸,这是怎么回事呢,出现这样的问题可能是因为系统不是正版,或者是电脑设置不 ...
- win10如何删除自己设置过的头像
把 %appdata%\Microsoft\Windows\AccountPictures 输入到地址栏 然后删除你想删除的照片即可