緩存系統-淘汰策略

目錄

一、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多空走勢以及共振與否。 主圖指標-飛云翻倍布…