目录

一、LRU(最近最少使用)

工作原理

操作流程

基本特征

二、LFU(最不常使用)

工作原理

操作流程

基本特征

三、ARC 自适应

工作原理

操作流程

基本特征

四、TTL(生存时间)

工作原理

操作流程

基本特征

五、FIFO

工作原理

操作流程

基本特征

六、Random

工作原理

操作流程

基本特征

七、使用建议


一、LRU(最近最少使用)

工作原理

LRU(Least Recently Used) 策略基于“时间局部性”原理,认为最近被访问过的数据在未来更可能被访问。它维护一个按访问时间排序的数据结构(通常使用双向链表),最近访问的放在头部,最久未访问的放在尾部。当缓存达到容量上限时,淘汰最久未访问的数据(即链表尾部)。

操作流程

  • 查询:若数据存在,将其移动到链表头部(表示最近使用),返回数据。
  • 新增/更新:若数据已存在,更新值并移动到头部;若不存在,在头部插入新节点。当缓存满时,先淘汰尾部节点再插入。
#include <list>
#include <unordered_map>class LRUCache {
private:int capacity;std::list<std::pair<int, int>> cache; // {key, value}// map 保存的是value的迭代器(实际数据存储在 cache 中),方便使用std::unordered_map<int, std::list<std::pair<int, int>>::iterator> map;public:LRUCache(int cap) : capacity(cap) {}int get(int key) {// map 统计 key 数量,不存在则返回空;if(!map.count(key)) return -1;auto it = map[key];// it 就是 cache 的迭代器,将其移动到 begin 位置cache.splice(cache.begin(), cache, it); // 移到头部return it->second;}void put(int key, int value) {// 若已存在数据,则更新 value,并移动到头部if(map.count(key)) {auto it = map[key];it->second = value;cache.splice(cache.begin(), cache, it);return;}// 若塞满了,则删除末尾的缓存valueif(cache.size() == capacity) {int del_key = cache.back().first;cache.pop_back();map.erase(del_key);}// 在 cache 头部直接构造 value 对象并插入,避免了重复创建 value 对象cache.emplace_front(key, value);map[key] = cache.begin();}
};

基本特征

优点

  • 对热点数据友好,能有效利用访问历史。
  • 实现高效(哈希表+双向链表可实现O(1)操作)。

缺点

  • 偶发性批量操作(如全表扫描)可能污染缓存,淘汰真正热点数据。
  • 链表指针占用额外内存。

适用场景:通用缓存场景(如网页缓存、数据库查询缓存)。

二、LFU(最不常使用)

工作原理

LFU (Least Frequently Used)策略根据数据的历史访问频率淘汰数据。它记录每个数据的访问次数,当缓存满时淘汰访问频率最低的数据。若频率相同,则淘汰其中最久未使用的数据(解决旧频率堆积问题)。

操作流程

  • 查询:若数据存在,增加其访问频率,并根据新频率调整位置(如移到更高频率链表的头部)。
  • 新增/更新:新数据初始频率为1。若缓存满,淘汰最低频率链表中的尾部节点(同频最久未用)。
#include <list>
#include <unordered_map>class LFUCache {
private:struct Node {int key, val, freq;Node(int k, int v, int f) : key(k), val(v), freq(f) {}};int capacity, minFreq;// key-valuestd::unordered_map<int, std::list<Node>::iterator> keyMap;// 频率-valuestd::unordered_map<int, std::list<Node>> freqMap;void updateFreq(std::list<Node>::iterator it) {int curFreq = it->freq;freqMap[curFreq].erase(it);/*minFreq是当前缓存中最小的频率。当某个频率的链表变空后,说明没有节点再使用这个频率了,保留它在 freqMap 中是没有意义的,删除它,避免占用不必要的内存。*/if(freqMap[curFreq].empty()) {freqMap.erase(curFreq);// 如果这个频率就是当前的 minFreq,那么我们需要更新 minFreq 到下一个更小的频率(比如 minFreq + 1)if(minFreq == curFreq) minFreq++;}it->freq++;// 更新频率后插入同频率链表头部freqMap[it->freq].push_front(*it);keyMap[it->key] = freqMap[it->freq].begin();}public:LFUCache(int cap) : capacity(cap), minFreq(1) {}// 根据 key 快速查找 value,并更新其使用频率int get(int key) {if(!keyMap.count(key)) return -1;auto it = keyMap[key];int val = it->val;updateFreq(it);return val;}void put(int key, int value) {if(capacity <= 0) return;// 若已存在同 key 的数据,则更新 value 与频率;if(keyMap.count(key)) {auto it = keyMap[key];it->val = value;updateFreq(it);return;}// 缓存满了之后,获取最小使用频率中的链表,并删除末尾的 value 节点;if(keyMap.size() >= capacity) {auto& minList = freqMap[minFreq];int del_key = minList.back().key;minList.pop_back();keyMap.erase(del_key);}// 新的 (key、value)插入频率为 1 的链表头部;freqMap[1].emplace_front(key, value, 1);// 写入缓存keyMap[key] = freqMap[1].begin();// 更新最小频率为 1minFreq = 1;}
};

基本特征

优点

  • 长期保护高频数据,不受偶发操作影响。

缺点

  • 新数据因频率低易被淘汰(“缓存冷启动”问题)。
  • 频率计数需长期存储,内存开销大。
  • 实现复杂(需双层结构:频率哈希表+节点链表)。

适用场景:长期热点数据缓存(如电商商品详情页、视频热门排行榜)。

三、ARC 自适应

工作原理

ARC 是一种自适应的缓存淘汰算法,通过动态平衡 LRU 和 LFU 策略来适应不同的访问模式。

ARC算法通过维护两个LRU链表和两个幽灵(ghost)链表来实现自适应:

两个实际缓存链表

  • T1:存储最近被访问的项(类似于LRU)。
  • T2:存储被访问多次的项(类似于LFU中的频繁访问项)。

两个幽灵链表

  • B1:存储从T1中被淘汰的项的键(只存储键,不存储数据)。
  • B2:存储从T2中被淘汰的项的键。

自适应参数 p

  • T1 实际容量 = p
  • T2 实际容量 = capacity - p

操作

p值变化

T1空间变化

T2空间变化

实际效果

访问B1

增大

增加

减少

强化LRU特性(保留新访问数据)

访问B2

减小

减少

增加

强化LFU特性(保留热点数据)

操作流程

  • 读操作

  1. 如果数据在 T1 或 T2 中(缓存命中),则将该项移动到 T2 的 MRU(最近使用)端。
  2. 如果数据在 B1 中(幽灵链表),说明最近被淘汰的项又被访问了,此时增加 T1 的大小(即增加 p),强化 LRU 特性。然后从存储中加载数据到 T2 的 MRU 端,并从 B1 中移除该项。
  3. 如果数据在 B2 中,则增加 T2 的大小(即减少 p),强化 LFU 特性。然后从存储中加载数据到 T2 的 MRU 端,并从 B2 中移除该项。
  4. 如果都不在(缓存未命中),则从存储中加载数据,并放入 T1 的 MRU 端。
  • 写操作:通常与读操作类似,但需要考虑写策略(如写回或写穿)。在缓存中,写操作通常也会更新缓存项的位置(类似于读操作),并将数据标记为脏(如果使用写回策略)。
#include <list>
#include <unordered_map>
#include <optional>template <typename K, typename V>
class ARCCache {size_t capacity;size_t p = 0;  // 自适应参数// 主缓存std::list<std::pair<K, V>> t1;std::list<std::pair<K, V>> t2;// 幽灵缓存(仅存储键)std::list<K> b1;std::list<K> b2;// 快速查找表std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> t1_map;std::unordered_map<K, typename std::list<std::pair<K, V>>::iterator> t2_map;std::unordered_map<K, typename std::list<K>::iterator> b1_map;std::unordered_map<K, typename std::list<K>::iterator> b2_map;void evict() {// 根据p值决定淘汰策略,t1 容量等于 pif (t1.size() > std::max(size_t(1), p)) {// 淘汰T1尾部auto [key, val] = t1.back();t1_map.erase(key);b1.push_front(key);b1_map[key] = b1.begin();t1.pop_back();} else {// 淘汰T2尾部auto [key, val] = t2.back();t2_map.erase(key);b2.push_front(key);b2_map[key] = b2.begin();t2.pop_back();}}public:ARCCache(size_t cap) : capacity(cap) {}std::optional<V> get(const K& key) {// 在T1中找到if (auto it = t1_map.find(key); it != t1_map.end()) {t2.splice(t2.begin(), t1, it->second);  // 移动到T2头部t2_map[key] = t2.begin();t1_map.erase(key);return t2.front().second;}// 在T2中找到if (auto it = t2_map.find(key); it != t2_map.end()) {t2.splice(t2.begin(), t2, it->second);  // 移动到头部return t2.front().second;}// 在B1中找到(幽灵缓存)if (auto it = b1_map.find(key); it != b1_map.end()) {p = std::min(capacity, p + std::max(b2.size() / b1.size(), size_t(1)));replace();put(key, load_from_disk(key));  // 实际需替换为数据加载b1_map.erase(key);b1.erase(it->second);return get(key);  // 递归获取}// 在B2中找到(幽灵缓存)if (auto it = b2_map.find(key); it != b2_map.end()) {p = std::max(size_t(0), p - std::max(b1.size() / b2.size(), size_t(1)));replace();put(key, load_from_disk(key));b2_map.erase(key);b2.erase(it->second);return get(key);}return std::nullopt;  // 未命中}void put(const K& key, const V& value) {// 如果已在缓存中,更新位置if (get(key).has_value()) return;// 需要淘汰旧数据if (t1.size() + t2.size() >= capacity) {evict();}// 新数据加入T1t1.emplace_front(key, value);t1_map[key] = t1.begin();}// 模拟从磁盘加载数据V load_from_disk(const K& key) { /* ... */ }
};

基本特征

优点

  • 自适应不同访问模式(时间局部性/频率局部性)
  • 抵抗缓存扫描攻击
  • 幽灵列表提供历史信息且内存占用小
  • 常数时间操作(O(1))
  • 综合性能优于 LRU/LFU

缺点

  • 实现复杂度较高(需维护4个链表)
  • 幽灵列表需要额外内存
  • 参数 p 的调整需要精细控制
  • 对极端访问模式适应性有限

适用场景

  • 数据库缓存系统(如 Oracle, PostgreSQL)
  • 操作系统页面缓存
  • CDN 内容缓存
  • 大规模键值存储系统
  • 需要高命中率的缓存场景

四、TTL(生存时间)

工作原理

TTL 策略为每个数据设置一个过期时间(如30秒)。数据过期后自动淘汰,无论其是否被访问。淘汰机制分为两种:

  • 惰性删除:访问时检查过期则删除。
  • 主动清理:后台线程定期扫描过期数据。

操作流程

  • 查询:检查数据是否过期,过期则删除并返回空。
  • 新增/更新:写入时设置过期时间戳。
  • 淘汰:由访问触发(惰性)或定时任务触发(主动)。
#include <unordered_map>
#include <queue>
#include <chrono>class TTLValue {
public:int value;std::chrono::system_clock::time_point expire;TTLValue(int v, int ttl_ms) : value(v) {expire = std::chrono::system_clock::now() + std::chrono::milliseconds(ttl_ms);}bool expired() const {return std::chrono::system_clock::now() > expire;}
};class TTLCache {
private:std::unordered_map<int, TTLValue> cache;public:void put(int key, int value, int ttl_ms) {cache[key] = TTLValue(value, ttl_ms);}int get(int key) {if(!cache.count(key)) return -1;if(cache[key].expired()) {cache.erase(key);return -1;}return cache[key].value;}// 主动清理(示例)void cleanExpired() {for(auto it = cache.begin(); it != cache.end(); ) {if(it->second.expired()) it = cache.erase(it);else ++it;}}
};

基本特征

优点

  • 保证数据新鲜度,避免使用过时数据。
  • 实现简单(仅需存储过期时间)。

缺点

  • 可能提前删除仍有价值的数据(如过期但频繁访问)。
  • 主动清理需额外CPU开销。

适用场景:时效性敏感数据(如用户会话Token、验证码、新闻缓存)。

五、FIFO

工作原理

FIFO 策略按数据进入缓存的顺序淘汰,类似队列。最早进入的数据最先被淘汰,与访问频率无关。

操作流程

  • 查询:返回数据,不改变任何顺序。
  • 新增/更新:新数据加入队列尾部。若缓存满,淘汰队列头部数据。
#include <queue>
#include <unordered_map>class FIFOCache {
private:int capacity;std::queue<int> entryQueue;std::unordered_map<int, int> cache;public:FIFOCache(int cap) : capacity(cap) {}int get(int key) {if(!cache.count(key)) return -1;return cache[key]; // 不改变队列顺序}void put(int key, int value) {if(cache.size() >= capacity) {int del_key = entryQueue.front();entryQueue.pop();cache.erase(del_key);}cache[key] = value;entryQueue.push(key);}
};

基本特征

优点

  • 实现极其简单(队列+哈希表)。
  • 无额外计算开销。

缺点

  • 无视访问模式,可能淘汰热点数据。

适用场景:顺序无关的低价值缓存(如日志临时缓存、下载缓冲池)。

六、Random

工作原理

随机策略在缓存满时随机选择一个数据淘汰。

操作流程

  • 查询:直接返回数据。
  • 新增/更新:若缓存满,随机选择一个键删除,再插入新数据。
#include <vector>
#include <unordered_map>
#include <cstdlib>
#include <ctime>class RandomCache {
private:int capacity;std::vector<int> keys;std::unordered_map<int, int> cache;public:RandomCache(int cap) : capacity(cap) { srand(time(0)); }int get(int key) {return cache.count(key) ? cache[key] : -1;}void put(int key, int value) {if(cache.size() >= capacity) {int idx = rand() % keys.size();int del_key = keys[idx];keys[idx] = keys.back(); // 交换到末尾keys.pop_back();cache.erase(del_key);}cache[key] = value;keys.push_back(key);}
};

基本特征

优点

  • 实现简单,零维护成本。
  • 无任何排序开销。

缺点

  • 结果不可控,可能淘汰重要数据。

适用场景:对性能要求极高且数据价值均匀的场景(如内存紧张的低优先级缓存)。

七、使用建议

特性

LRU

LFU

TTL

FIFO

Random

排序依据

访问时间

访问频率

过期时间

进入顺序

实现复杂度

极低

极低

热点保护

★★★★

★★★★★

★★

时效保证

★★★★★

内存开销

适用场景

通用缓存

长期热点

时效数据

流水数据

临时缓存

  1. 首选LRU:85%场景的最佳平衡选择(如Redis的allkeys-lru)
  2. 组合策略:LRU+TTL 组合使用(如Redis的volatile-lru)
  3. 监控调整:定期检查淘汰率,超过 5% 需扩容
  4. 分级缓存:高频数据用 LFU,普通数据用 LRU
  5. 特殊场景:时效数据强制使用 TTL,低价值数据用 Random

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/87010.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/87010.shtml
英文地址,请注明出处:http://en.pswp.cn/web/87010.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TypeScript 安装使用教程

一、TypeScript 简介 TypeScript 是由微软开发的开源编程语言&#xff0c;是 JavaScript 的超集&#xff0c;添加了静态类型、接口、枚举、类等特性&#xff0c;使开发大型应用更安全、可维护、可扩展。最终会被编译为标准的 JavaScript 代码在浏览器或 Node.js 中运行。 二、…

强化学习系列--dpo损失函数

DPO 概要 DPO&#xff08;Direct Preference Optimization&#xff0c;直接偏好优化&#xff09;是由斯坦福大学等研究团队于2023年提出的一种偏好优化算法&#xff0c;可用于LLM、VLM与MLLM的对齐训练。 算法基于PPO的RLHF基础上进行了大幅简化。DPO算法跳过了训练奖励模型这…

UniApp完全支持快应用QUICKAPP-以及如何采用 Uni 模式开发发行快应用优雅草卓伊凡

UniApp完全支持快应用QUICKAPP-以及如何采用 Uni 模式开发发行快应用优雅草卓伊凡 一、UniApp 对快应用的支持深度 UniApp 已完全支持快应用的开发和发布&#xff0c;具体包括&#xff1a; 两种渲染模式&#xff1a; Webview 渲染&#xff08;快应用 Light 版&#xff09;&a…

js 允许生成特殊的变量名 基于字符集编码混淆的 XSS 绕过漏洞 -- Google 2025 Lost In Transliteration

题目实现了一个字符转换工具 在/file路由用户可以通过 ct 参数自定义 Content-Type // 文件路由 - 提供静态文件服务&#xff08;JS和CSS&#xff09;&#xff0c;支持内容类型验证 app.MapGet("/file", (string filename "", string? ct null, string?…

【仿muduo库实现并发服务器】LoopThreadPool模块

仿muduo库实现并发服务器 1.LoopThread模块1.1成员变量1.2构造函数13线程入口函数1.4获取eventloop对象GetLoop() 2.LoopThreadPool模块2.1成员变量2.2构造函数2.3配置线程数量2.4按照配置数量创建线程2.5依次分配Eventloop对象 1.LoopThread模块 这个模块是为了将EventLoop与…

华为云Flexus+DeepSeek征文|基于Dify构建文本/图像/视频生成工作流

华为云FlexusDeepSeek征文&#xff5c;基于Dify构建文本/图像/视频生成工作流 一、构建文本/图像/视频生成工作流前言二、构建文本/图像/视频生成工作流环境2.1 基于FlexusX实例的Dify平台2.2 基于MaaS的模型API商用服务 三、构建文本/图像/视频生成工作流实战3.1 配置Dify环境…

相机-IMU联合标定:IMU更新频率

文章目录 📚简介⚠️ IMU频率参数错误设置的影响❌ 相机-IMU联合标定失败:Optimization failed!🚀 确定IMU更新频率直接通过 rostopic hz 检查实际频率检查 IMU 驱动或数据手册从 bag 文件统计频率在这里插入图片描述修改 `update_rate` 的注意事项**最终建议****常见问题…

动手实践:如何提取Python代码中的字符串变量的值

要提取Python代码中所有变量类型为字符串的变量的值&#xff0c;但不执行代码&#xff08;避免安全风险&#xff09;&#xff0c;可以通过静态分析代码的抽象语法树&#xff08;AST&#xff09;来实现。以下是完整的解决方案&#xff1a; 本文由「大千AI助手」原创发布&#xf…

Python中字符串isalpha()函数详解

在 Python 中&#xff0c;isalpha() 是字符串&#xff08;string&#xff09;类型的内置方法&#xff0c;用于检查字符串中的所有字符是否都是字母字符&#xff08;alphabetic character&#xff09;。以下是详细说明&#xff1a; 一、基本功能 返回值&#xff1a;布尔值&…

Gradio全解13——MCP详解(4)——TypeScript包命令:npm与npx

Gradio全解13——MCP详解&#xff08;4&#xff09;——TypeScript包命令&#xff1a;npm与npx 第13章 MCP详解13.4 TypeScript包命令&#xff1a;npm与npx13.4.1 概念区分1. npm概念与运行逻辑2. npx概念及特点 13.4.2 操作示例1. 使用npm执行包2. 使用npx执行包3. 常用npm命令…

《推客小程序全链路开发指南:从架构设计到裂变运营》

在移动互联网流量红利逐渐消退的今天&#xff0c;如何低成本获客成为企业营销的核心痛点。推客小程序作为一种基于社交关系的裂变营销工具&#xff0c;正成为企业突破增长瓶颈的利器。本文将为您全面解析推客小程序的开发定制全流程&#xff0c;帮助您打造专属的社交裂变营销平…

中钧科技参加中亚数字经济对话会,引领新疆企业数字化新征程!

6月27 日&#xff0c;乌鲁木齐成为数字经济领域的焦点&#xff0c;中国新疆 - 中亚国家数字经济和数字贸易企业对话会在此盛大举行。 来自中亚国家及新疆数字经济领域的100 余位核心代表齐聚一堂&#xff0c;围绕数字经济时代的机遇、挑战与策略展开深度探讨。 本次对话会由新…

k8s一键部署tongweb企业版7049m6(by why+lqw)

声明 1.此贴仅供参考&#xff0c;请根据自身需求在测试环境测试和修改。 安装准备 1.获取对应的安装包和授权,并将授权和安装包放在同一个目录下 2.docekr已配置远程仓库 3.提前拉取jdk的镜像&#xff08;这里配置了使用openjdk:8&#xff09; 安装 将以下内容复制到k8s_…

Qt 与 Halcon 联合开发六:基于海康SDK设计完整的相机类【附源码】

在现代工业自动化、机器人视觉、等领域&#xff0c;相机模块的作用至关重要。通过相机模块采集到的图像数据&#xff0c;我们能够进行一系列的图像处理和分析。为了高效地控制相机和处理图像&#xff0c;本篇文章将介绍如何使用Qt和Halcon联合开发一个相机模块&#xff0c;帮助…

第7篇:Gin模板引擎——服务端页面渲染

作者:GO兔 博客:https://luckxgo.cn 分享大家都看得懂的博客 引言 在Web开发中&#xff0c;服务端页面渲染(SSR)依然是构建动态网页的重要方式。Gin框架虽然以API开发见长&#xff0c;但也内置了强大的模板引擎支持&#xff0c;基于Go标准库的html/template包实现。本文将深入…

RagFlow 源码部署启动指南

一、环境准备 1. 安装 uv 和 pre-commit 如果已安装&#xff0c;可跳过。推荐使用官方方式安装&#xff0c;避免报错&#xff1a; pipx install uv pre-commit export UV_INDEXhttps://mirrors.aliyun.com/pypi/simple安装报错 使用清华源安装&#xff1a; pipx install uv…

【Python基础】12 闲谈分享:Python用于无人驾驶的未来

引言&#xff1a;一个程序员的自动驾驶梦想 还记得2016年的那个秋天&#xff0c;我第一次坐进特斯拉Model S的驾驶座&#xff0c;体验Autopilot功能。当方向盘开始自己转动&#xff0c;车辆在高速公路上自动跟随前车时&#xff0c;我的内心涌起了一种奇妙的感觉——这不就是我…

为什么js是单线程?

js单线程&#xff0c;同一时间只能做一件事 。js的单线程 主要与它的用途有关。作为浏览器脚本语言&#xff0c;js的主要用途是与用户互动&#xff0c;以及操作DOM。这决定了它只能是单线程&#xff0c;否则会带来很复杂的同步问题。如果js同时有两个线程&#xff0c;一个线程在…

DVWA靶场通关笔记-文件包含(Medium级别 9种渗透方法)

目录 一、文件包含 1、原因 2、危害 3、防范措施 二、代码审计&#xff08;Medium级别&#xff09; 1、渗透准备 &#xff08;1&#xff09;配置php.ini &#xff08;2&#xff09;file1.php &#xff08;3&#xff09;file2.php &#xff08;4&#xff09;file3.php…

飞云翻倍布林(翻倍密码系统四线布林版)双安全系统+均价趋势指标+日线周线MACD,组合操盘技术图文分享

如上图组合操盘套装指标&#xff0c;主图指标-翻倍密码系统四线布林版-飞云翻倍布林。副图指标1-均价趋势指标&#xff0c;跟踪市场均价走势和趋势&#xff1b;副图指标2-日线周线MACD指标&#xff0c;跟踪日线和周线两个级别的MACD多空走势以及共振与否。 主图指标-飞云翻倍布…