首页 > 编程语言 >LRU算法原理及其实现

LRU算法原理及其实现

时间:2024-09-13 16:50:48浏览次数:3  
标签:缓存 _. values posInfo 算法 LRU key 原理 淘汰

一、LRU是什么

        LRU算法的全称是“Least Recently Used”,即“最近最少使用”算法。LRU算法的基本思想是,当缓存空间已满时,优先淘汰最近最少使用的缓存数据,以腾出更多的缓存空间。因此,LRU算法需要维护一个缓存访问历史记录,以便确定哪些缓存数据是最近最少使用的。

        LRU算法的实现可以采用多种数据结构,其中最常见的是使用一个双向链表和一个哈希表双向链表用于维护缓存数据的访问顺序,哈希表用于快速查找缓存数据。具体来说,当新的数据被访问时,先在哈希表中查找该数据是否已经存在于缓存中,如果存在,则将该数据移动到双向链表的头部,表示该数据是最近访问的数据;如果不存在,则需要将该数据添加到缓存中,并将其添加到双向链表的头部。当缓存空间已满时,需要淘汰双向链表中最后一个节点,同时在哈希表中删除对应的缓存数据。

二、使用场景

        考虑这样一个场景,单位维护了一个资源下载网站。某天产品找到你,让你根据用户下载量对资源进行排序,同时定期删除不经常使用的资源,释放服务器压力,应该怎么做?

        简单,数据库加个计数和最近使用时间字段,返回接口根据这两个字段做个排序,然后再写个定时任务删除冗余资源就完事了,开干!

        哗哗哗写完后,自信提交。

        测试跑过来说,你这功能写的不行啊,我们才这么几个人用,搜索资源就开始卡了,真给你上线不得把服务器干翻?

        啊,真烦,功能实现了不就行了嘛,真是没事找事。

三、LRU实现

3.1 LRU缓存实体定义

struct List
{
    int val;
    List *pre, *nxt;
    List(int x, List *y, List *z)
    {
        val = x, pre = y, nxt = z;
    }
} *head;
int idx;
map<int, List *> mp;

再优化下,使用 unordered_map<KEY,std::list<VALUE>::iterator> 代替

3.2 CURD实现

    // 存储缓存的值的列表
    std::list<VALUE> values_;
    // 存储键到值列表中对应迭代器的映射
    std::unordered_map<KEY, typename std::list<VALUE>::iterator, HASH> posInfo_;

   // 插入键值对到缓存中
    void setKey(const KEY& key, const VALUE& data) {
        std::lock_guard<std::mutex> guard(mutex_);
        auto it = posInfo_.find(key);
        if (it!= posInfo_.end()) {
            // 键已存在,先删除旧值
            values_.erase(it->second);
            posInfo_.erase(it);
        }
        // 将新值插入到缓存列表头部,并更新键到迭代器的映射
        values_.push_front(data);
        posInfo_[key] = values_.begin();
    }

    // 根据键获取值
    bool getKey(const KEY& key, VALUE& data) {
        auto it = posInfo_.find(key);
        if (it == posInfo_.end()) {
            return false;
        }
        // 将对应的值移动到缓存列表头部,并更新键到迭代器的映射
        moveToFront(it);
        data = *(it->second);
        return true;
    }

    // 检查键是否存在于缓存中
    bool existKey(const KEY& key) {
        return posInfo_.find(key)!= posInfo_.end();
    }

    // 根据键删除值
    void delKey(const KEY& key) {
        auto it = posInfo_.find(key);
        if (it == posInfo_.end()) {
            return;
        }
        // 删除对应的值
        values_.erase(it->second);
        posInfo_.erase(it);
    }

    // 将对应的值移动到缓存列表头部,并更新键到迭代器的映射
    void moveToFront(const typename std::unordered_map<KEY, typename         
    std::list<VALUE>::iterator, HASH>::iterator& it) {
        values_.splice(values_.begin(), values_, it->second);
        it->second = values_.begin();
    }

3.3 数据淘汰

    // 存储被淘汰的值的向量
    std::vector<VALUE> elimiValues_;    
    // 根据给定函数进行淘汰处理
    void eliminate(std::function<bool(const VALUE&)> func, std::vector<VALUE>* _return = nullptr) {
        if (_return) {
            // 将被淘汰的值存储在 _return 指向的向量中,并返回
            std::swap(*_return, elimiValues_);
        } else {
            // 清空存储被淘汰值的向量
            elimiValues_.clear();
        }

        while (!values_.empty()) {
            VALUE last = values_.back();
            if (!func(last)) {
                break;
            }
            // 根据淘汰条件逐个淘汰缓存中的值
            KEY key = value.get_key_from_val(last);
            values_.pop_back();
            posInfo_.erase(key);
            if (_return) {
                _return->push_back(last);
            }
        }
    }

3.4 优化

我们的业务是个多线程场景,此外要考虑LRUCache 缓存满的场景。因此,简单调整下代码,再增加个数据淘汰策略。

3.4.1淘汰算法


    // 淘汰策略枚举
    enum class EN_ELIMINATE_POLICY {
        EP_THROW_DIRECT,    // 直接丢弃被淘汰的数据
        EP_PENDING_DISPOSAL // 将被淘汰的数据放入待处理区
    };

    // 处理缓存已满时的淘汰操作
    void handleEviction() {
        const VALUE& val = values_.back();
        if (policy_ == EN_ELIMINATE_POLICY::EP_PENDING_DISPOSAL) {
            elimiValues_.push_back(val);
        }
        KEY lastkey = getKeyFromValue(val);
        values_.pop_back();
        posInfo_.erase(lastkey);
    }

3.4.2 业务修改示例

    // 当前的淘汰策略
    EN_ELIMINATE_POLICY policy_;
    // 互斥锁,用于保证线程安全
    std::mutex mutex_;
  
    // 插入键值对到缓存中
    void setKey(const KEY& key, const VALUE& data) {
        std::lock_guard<std::mutex> guard(mutex_);
        auto it = posInfo_.find(key);
        if (it!= posInfo_.end()) {
            // 键已存在,先删除旧值
            values_.erase(it->second);
            posInfo_.erase(it);
        } else if (MAXSIZE > 0 && posInfo_.size() >= MAXSIZE) {
            // 缓存已满,根据淘汰策略处理被淘汰的值
            handleEviction();
        }
        // 将新值插入到缓存列表头部,并更新键到迭代器的映射
        values_.push_front(data);
        posInfo_[key] = values_.begin();
    }

四、总结

        LRU是个很常见的置换淘汰算法,在资源管理和缓存方面有很多应用,例如著名的redis中就有其相关应用

另附 :完整源码

标签:缓存,_.,values,posInfo,算法,LRU,key,原理,淘汰
From: https://blog.csdn.net/Damon_Don/article/details/142213181

相关文章

  • [Vue] Object.defineProperty 什么情况监听不到?和 Proxy 响应式原理又何区别?
    前言Vue2.x采用的是Object.defineProperty来实现响应式系统,它只能监听已经存在的属性,无法监听对象属性的新增或删除。Vue3使用Proxy拦截对对象和数组的访问和修改,实现了响应式系统。它通过拦截这些操作,追踪哪些数据被访问、修改,从而在数据变化时通知相关的依赖。Object......
  • LeetCode算法—分治法
    思路:分治法的核心思想是“分而治之”,即将一个复杂的问题分成多个较小的子问题,分别求解这些子问题,然后将子问题的解合并,得到原问题的解。具体到求众数的问题上,分治法通过递归地将数组分成两部分,分别找出每一部分的众数,最后通过合并步骤来确定整个数组的众数。LeetCode169多数元素......
  • 基于MATLAB蚁群算法优化的小波变换图像压缩
    随着计算机技术和网络速度的飞速发展,数字图像越来越成为人们生活中不可或缺的一部分,然而由于存储和传输的限制,如何对数字图像进行高效压缩成为了研究的热点问题,小波变换作为一种基于多尺度分析的信号处理方法,在数字图像压缩中有着广泛的应用。然而传统的小波变换图像压缩方法......
  • Java 并发编程深度解析:synchronized 关键字的内部原理与应用
    引言在并发编程中,当多个线程访问同一个共享资源时,我们必须考虑如何维护数据的原子性。Java是通过synchronized关键字实现锁功能来做到这点的,synchronized是JVM实现的一种内置锁,锁的获取和释放由JVM隐式实现。锁的本质如上图所示,多个线程要访问同一个资源。线程就......
  • 决策树算法上篇
    决策树概述决策树是属于有监督机器学习的一种,起源非常早,符合直觉并且非常直观,模仿人类做决策的过程,早期人工智能模型中有很多应用,现在更多的是使用基于决策树的一些集成学习的算法。示例一:上表根据历史数据,记录已有的用户是否可以偿还债务,以及相关的信息。通过该数据,构建......
  • Python与Go语言中的哈希算法实现及对比分析
    哈希算法是一种将任意大小的数据输入转化为固定大小的输出(通常为一个散列值)的算法,在密码学、数据完整性验证以及数据索引等场景中广泛应用。本文将详细介绍Python和Go语言如何实现常见的哈希算法,包括MD5、SHA-1、SHA-256等。文章不仅提供代码示例,还会详细解释每个算法的特点、应用......
  • 复合Simpson求积算法-C++【可直接复制粘贴/欢迎评论点赞】
    背景复合Simpson求积算法是基于Simpson1/3法则的推广。Simpson1/3法则是一种数值积分方法,它通过将积分区间划分为多个小区间,并在每个小区间上采用一个二次多项式来逼近原函数,进而求得积分的近似值。复合Simpson求积算法则是将这种方法应用于整个积分区间,即将整个区间划分为......
  • C# 开源教程带你轻松掌握数据结构与算法
    前言在项目开发过程中,理解数据结构和算法如同掌握盖房子的秘诀。算法不仅能帮助我们编写高效、优质的代码,还能解决项目中遇到的各种难题。给大家推荐一个支持C#的开源免费、新手友好的数据结构与算法入门教程:Hello算法。项目介绍《HelloAlgo》是一本开源免费、新手友好的数......
  • 数据结构和算法之基本概念
    原文出处:数据结构和算法之基本概念  关注码农爱刷题,看更多技术文章!!其他文章:Java基础之数组    在计算机领域中,数据元素都不是孤立存在的,而是在它们之间存在着某种关系,这种数据元素相互之间的关系称为结构(Structure)。数据结构是相互之间存在一种或多种特定关系的数......
  • 降维算法 0基础小白也能懂(附代码)
    降维算法0基础小白也能懂(附代码)原文链接啥是降维算法在互联网大数据场景下,我们经常需要面对高维数据,在对这些数据做分析和可视化的时候,我们通常会面对「高维」这个障碍。在数据挖掘和建模的过程中,高维数据也同样带来大的计算量,占据更多的资源,而且许多变量之间可能存在相关性......