LRU緩存設計與實現詳解

LRU緩存設計與實現詳解

    • 一、LRU緩存核心概念
      • 1.1 LRU策略定義
      • 1.2 應用場景
      • 1.3 核心操作要求
    • 二、數據結構設計:雙向鏈表+哈希表
      • 2.1 為什么選擇雙向鏈表?
      • 2.2 為什么結合哈希表?
      • 2.3 節點結構設計(雙向鏈表)
      • 2.4 LRU緩存的邏輯結構
    • 三、核心算法實現
      • 3.1 初始化參數
      • 3.2 get方法實現(獲取并更新順序)
      • 3.3 put方法實現(插入/更新+淘汰策略)
      • 3.4 復雜度分析
    • 四、邊界處理與優化技巧
      • 4.1 虛擬頭尾節點設計
      • 4.2 容量為1的極端情況
      • 4.3 線程安全優化
    • 五、Java內置實現:LinkedHashMap
      • 5.1 利用LinkedHashMap實現LRU
      • 5.2 LinkedHashMap原理
    • 六、分布式LRU緩存設計(進階)
      • 6.1 一致性哈希算法
      • 6.2 緩存穿透與雪崩
      • 6.3 典型架構
    • LRU總結
      • LRU的優缺點
      • 適用場景
      • 優化建議

LRU(Least Recently Used)作為最經典的緩存淘汰策略之一,廣泛應用于操作系統、數據庫、Web框架等場景。本文我將深入解析LRU緩存的核心原理、數據結構設計、算法實現及優化策略,結合Java代碼實現,幫你掌握高性能緩存系統的設計方法。

一、LRU緩存核心概念

1.1 LRU策略定義

LRU緩存通過維護一個「最近使用」的順序列表,當緩存容量已滿時,主動淘汰最近最少使用的元素,為新元素騰出空間。其核心思想是:優先保留近期使用過的數據,淘汰長時間未訪問的數據

1.2 應用場景

  • 瀏覽器緩存:存儲最近訪問的網頁資源,加速頁面加載
  • 數據庫查詢緩存:緩存高頻訪問的SQL結果,減少數據庫壓力
  • Java內存管理:JVM堆內存中的LRU機制優化對象回收
  • 分布式系統:Redis的allkeys-lru策略提升熱點數據訪問效率

1.3 核心操作要求

  • get(key):獲取指定鍵的值,若存在則返回并標記為最近使用,時間復雜度O(1)
  • put(key, value):插入或更新鍵值對,若容量不足則淘汰最近最少使用的鍵,時間復雜度O(1)

二、數據結構設計:雙向鏈表+哈希表

2.1 為什么選擇雙向鏈表?

  • 有序性維護:鏈表天然支持順序訪問,方便將最近使用的節點移動到頭部
  • 快速刪除:雙向鏈表可在O(1)時間內刪除任意節點(需前驅節點指針)
  • 高效插入:新節點始終插入頭部,舊節點淘汰從尾部移除

2.2 為什么結合哈希表?

  • 快速定位:哈希表存儲鍵到節點的映射,實現O(1)時間的存在性檢查
  • 解耦數據:鏈表維護順序,哈希表維護鍵的索引,分工明確

2.3 節點結構設計(雙向鏈表)

class Node {int key;        // 鍵(用于哈希表定位)int value;      // 值Node prev;      // 前驅節點Node next;      // 后繼節點public Node(int key, int value) {this.key = key;this.value = value;}
}

2.4 LRU緩存的邏輯結構

LRU緩存結構設計

哈希表 (key -> Node)↓
雙向鏈表(頭節點=最近使用,尾節點=最久未使用)
head <-> node1 <-> node2 <-> ... <-> tail

三、核心算法實現

3.1 初始化參數

import java.util.HashMap;public class LRUCache {private HashMap<Integer, Node> cache;  // 鍵到節點的映射private int capacity;                 // 緩存容量private Node head;                    // 最近使用節點(頭節點)private Node tail;                    // 最久未使用節點(尾節點)public LRUCache(int capacity) {this.capacity = capacity;cache = new HashMap<>(capacity);// 初始化虛擬頭尾節點,簡化邊界處理head = new Node(-1, -1);tail = new Node(-1, -1);head.next = tail;tail.prev = head;}
}

3.2 get方法實現(獲取并更新順序)

public int get(int key) {if (!cache.containsKey(key)) {return -1;}Node node = cache.get(key);// 將節點移動到頭部removeNode(node);addToHead(node);return node.value;
}// 私有方法:刪除任意節點
private void removeNode(Node node) {Node prevNode = node.prev;Node nextNode = node.next;prevNode.next = nextNode;nextNode.prev = prevNode;
}// 私有方法:將節點插入頭部
private void addToHead(Node node) {node.next = head.next;node.prev = head;head.next.prev = node;head.next = node;
}

3.3 put方法實現(插入/更新+淘汰策略)

public void put(int key, int value) {if (cache.containsKey(key)) {// 更新值并移動到頭部Node node = cache.get(key);node.value = value;removeNode(node);addToHead(node);return;}if (cache.size() == capacity) {// 淘汰尾節點(最久未使用)Node lastNode = tail.prev;cache.remove(lastNode.key);removeNode(lastNode);}// 插入新節點到頭部Node newNode = new Node(key, value);cache.put(key, newNode);addToHead(newNode);
}

3.4 復雜度分析

  • 時間復雜度
    get和put操作均通過哈希表定位節點(O(1)),雙向鏈表的刪除和插入操作均為O(1),總體時間復雜度O(1)
  • 空間復雜度
    存儲所有節點的空間為O(capacity),哈希表和鏈表的額外空間為O(capacity),總體空間復雜度O(capacity)

四、邊界處理與優化技巧

4.1 虛擬頭尾節點設計

  • 作用:避免處理頭節點和尾節點的null指針邊界問題
  • 實現:初始化兩個虛擬節點,頭節點的next指向最近使用節點,尾節點的prev指向最久未使用節點

4.2 容量為1的極端情況

// 測試用例:容量為1時,每次put都會淘汰舊節點
LRUCache cache = new LRUCache(1);
cache.put(1, 10);  // 緩存:{1:10}
cache.put(2, 20);  // 淘汰1,緩存:{2:20}
System.out.println(cache.get(1));  // 輸出-1

4.3 線程安全優化

  • 問題:上述實現非線程安全,多線程環境下需加鎖
  • 解決方案
    使用synchronized修飾get/put方法,或基于ReentrantLock實現:
private final ReentrantLock lock = new ReentrantLock();public int get(int key) {lock.lock();try {// 原有邏輯} finally {lock.unlock();}
}

五、Java內置實現:LinkedHashMap

5.1 利用LinkedHashMap實現LRU

import java.util.LinkedHashMap;public class LRUCacheJava {private LinkedHashMap<Integer, Integer> cache;private int capacity;public LRUCacheJava(int capacity) {this.capacity = capacity;// accessOrder=true表示按訪問順序排序(最近訪問的在尾部)cache = new LinkedHashMap<Integer, Integer>(capacity, 0.75f, true) {@Overrideprotected boolean removeEldestEntry(java.util.Map.Entry<Integer, Integer> eldest) {return size() > capacity;}};}public int get(int key) {return cache.getOrDefault(key, -1);}public void put(int key, int value) {cache.put(key, value);}
}

5.2 LinkedHashMap原理

  • 數據結構:哈希表+雙向鏈表(維護插入順序或訪問順序)
  • 淘汰策略:重寫removeEldestEntry方法,容量超標時淘汰最舊節點
  • 性能對比:比手動實現稍慢(封裝帶來的開銷),但代碼更簡潔

六、分布式LRU緩存設計(進階)

6.1 一致性哈希算法

  • 問題:分布式環境中緩存節點動態變化時的鍵分布問題
  • 解決方案
    通過一致性哈希將鍵映射到節點,節點增減時僅影響相鄰節點,減少緩存失效

6.2 緩存穿透與雪崩

  • 緩存穿透:查詢不存在的鍵導致大量數據庫訪問
    解決方案:緩存空值或布隆過濾器
  • 緩存雪崩:大量緩存同時失效導致請求積壓
    解決方案:設置隨機過期時間+多級緩存

6.3 典型架構

客戶端 <-> 負載均衡 <-> 緩存集群(每個節點實現LRU)↓數據源(數據庫/文件系統)

LRU總結

LRU的優缺點

優點缺點
實現相對簡單無法處理突發訪問模式
適合時間局部性數據對空間局部性支持差
平均性能穩定可能淘汰高頻低最近使用數據

適用場景

  • 優先使用:數據訪問符合時間局部性(如歷史記錄、用戶行為數據)
  • 謹慎使用:數據訪問模式頻繁變化(如實時統計類數據)
  • 結合使用:與LFU(最少頻率使用)策略結合,提升復雜場景性能

優化建議

  1. 預加載熱點數據:初始化時加載高頻訪問數據
  2. 限制最大容量:避免內存溢出,結合監控動態調整容量
  3. 異步淘汰:將淘汰操作放到后臺線程執行,減少主線程阻塞

That’s all, thanks for reading!
覺得有用就點個贊、收進收藏夾吧!關注我,獲取更多干貨~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/912336.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/912336.shtml
英文地址,請注明出處:http://en.pswp.cn/news/912336.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

RabbitMQ中,basicAck、basicNack和basicReject是三種核心的消息確認機制

channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); 在RabbitMQ中&#xff0…

UNIAPP入門基礎

一、開發環境準備 1. 安裝 HBuilderX(官方推薦IDE) 下載地址:HBuilderX 官網 版本選擇: App開發版:開箱即用,內置 UniApp 插件 標準版:需手動安裝 UniApp 插件(運行時會提示) 安裝步驟: Windows:雙擊安裝包,勾選“創建桌面快捷方式” macOS:拖拽到 Applications…

前端單點登錄

“前端單點登錄&#xff08;SSO, Single Sign-On&#xff09;”是指在多個系統之間共享用戶登錄狀態&#xff0c;使用戶只需登錄一次&#xff0c;就可以在多個子系統中使用同一身份訪問資源&#xff0c;無需重復登錄。 以下是一個典型的前端單點登錄方案的介紹和實現思路&…

DiNA:擴張鄰域注意力 Transformer

摘要 Transformer 正迅速成為跨模態、跨領域和跨任務中應用最廣泛的深度學習架構之一。在計算機視覺領域&#xff0c;除了持續發展的純 transformer 架構&#xff0c;分層 transformer 也因其優越的性能和在現有框架中易于集成而受到廣泛關注。這類模型通常采用局部化的注意力…

對于“隨機種子”的作用的理解

深度學習系統的兩大組成部分 確定性部分&#xff08;無法通過種子改變&#xff09;&#xff1a; ? 網絡結構&#xff1a;層數、神經元數量、連接方式 ? 學習率&#xff1a;如您所說&#xff0c;這是開發者明確設置的固定值或調度策略 ? 損失函數&#xff1a;MSE、CrossEnt…

C# 委托(調用帶引用參數的委托)

調用帶引用參數的委托 如果委托有引用參數&#xff0c;參數值會根據調用列表中的一個或多個方法的返回值而改變。 在調用委托列表中的下一個方法時&#xff0c;參數的新值&#xff08;不是初始值&#xff09;會傳給下一個方法。例如&#xff0c; 如下代碼調用了具有引用參數的…

Cisco FMC events無法加載并且cpu high故障- Cisco bug

FMC故障 日志無法加載&#xff0c;并且CPU high 95% 經確認是bug問題&#xff0c;需要重置1個monetdb的進程 https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwe47671 https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwi64429 2.1 備份FMC配置 2.2 重置進程 大約為2…

HarmonyOS 公共事件機制介紹以及多進程之間的通信實現(9000字詳解)

HarmonyOS 公共事件機制介紹以及多進程之間的通信 CES(Common Event Service,公共事件服務)為應用程序提供訂閱、發布、退訂公共事件的能力 1. 公共事件的介紹 1.1.1公共事件的分類&#xff1a;公共事件從系統的角度可以分為系統公共事件和自定義公共事件 系統公共事件&#x…

華為云Flexus+DeepSeek征文|快速搭建Dify LLM應用開發平臺教程

【摘要】本文介紹基于華為云Flexus X實例快速部署Dify-LLM應用開發平臺的解決方案。通過創建云服務器&#xff08;2核4G配置&#xff09;、彈性公網IP&#xff08;300Mbps帶寬&#xff09;及安全組&#xff0c;實現平臺私有化部署。方案提供兩種計費模式&#xff08;按需197元/…

【blender】使用bpy對一個obj的不同mesh進行不同的材質貼圖(涉及對bmesh的操作)

BMesh 簡介 BMesh 是 Blender 中用于表示和操作網格數據的底層數據結構系統&#xff0c;它是傳統網格數據結構的高級替代品。 主要特點 靈活拓撲支持&#xff1a; 支持 n-gons&#xff08;任意邊數的多邊形&#xff09;&#xff0c;而不僅僅是三角形和四邊形允許邊和頂點不屬…

如何通過nvm切換本地node環境詳情教程(已裝過node.js更改成nvm)

針對系統已裝過node環境或者第一次安裝nvm環境如何切換nvm 文章目錄 系列文章目錄前言一、刪除原有node環境二、使用步驟 1.下載nvm軟件2.安裝node不同版本3.使用node版本4.配置包文件、安裝包、配置包環境 總結 一、刪除原有node環境 1、刪除之前安裝的node包&#xff0c;以及…

概率論符號和公式整理

本文是由AI生成后&#xff0c;經作者優化整理的文章。個人總結&#xff0c;僅限參考&#xff01; 以下整理了概率論中的常用符號和公式表格&#xff0c;覆蓋基礎知識、關鍵定理和常用分布&#xff1a; 一、基礎集合與事件符號 符號名稱含義/公式說明 S S S樣本空間所有可能結…

SpringSecurity是什么?

Spring Security是Spring生態中的安全框架&#xff0c;用于管理Web應用的認證與權限控制&#xff0c;支持多種登錄方式并集成防護機制&#xff0c;可防范CSRF/XSS等攻擊&#xff0c;保障企業級系統的安全性。 一、核心功能與定位 身份認證&#xff08;Authentication&#xff…

nt!IoSynchronousPageWrite函數分析之atapi!IdeReadWrite----非常重要

第一部分&#xff1a;預分析 1: kd> g Breakpoint 7 hit atapi!IdeReadWrite: f729cb2a 55 push ebp 1: kd> kc # 00 atapi!IdeReadWrite 01 atapi!IdeSendCommand 02 atapi!AtapiStartIo 03 atapi!IdeStartIoSynchronized 04 nt!KeSynchronizeExecuti…

軟考系統架構設計師經驗總結

本文目的 對參加的2025年上半年系統架構設計師考試進行總結提供一些備考思路給未來參加系統架構設計師的同學 個人背景 工作背景 本科計算機與技術&#xff08;學過一些計算機基礎課程&#xff09;&#xff0c;15年畢業后從事過b端&#xff08;人群畫像、營銷、用戶增長、硬…

Tailwind CSS工作原理

文章目錄 前言1. 指令解析與 AST 操作&#x1f6a9; **核心處理流程**&#x1f9e9; **具體流程說明** 2. **配置驅動的樣式生成**3. **JIT 模式&#xff08;Just-In-Time&#xff09;的核心邏輯**4. **插件與自定義擴展**5. **與 PostCSS 管道的協同**6. **優化與 Tree Shakin…

web網頁開發,在線%旅游景點管理%系統demo,基于Idea,vscode,html,css,vue,java,maven,springboot,mysql

經驗心得 兩業務單&#xff0c;都是業務邏輯開發&#xff0c;基本crud&#xff0c;什么是前后端&#xff0c;怎么分離前后端&#xff0c;前后端怎么通訊的&#xff0c;是以什么格式進行通訊這些咱們都需要掌握&#xff0c;后面剩下就是前后端不同層如何優化。管理系統很常見了其…

面試150 長度最小的子數組

思路 聯想到滑動窗口法。左窗口的值為0&#xff0c;遍歷數組對數組求和&#xff0c;當數組的和大于等于target的時候&#xff0c;窗口要收縮&#xff0c;計算子數組的長度&#xff0c;并及時更新最小的長度&#xff0c;左窗口右移。 class Solution:def minSubArrayLen(self,…

Python字典的查詢操作

一、前言 在 Python 中&#xff0c;字典&#xff08;dict&#xff09; 是一種非常常用的數據結構&#xff0c;以鍵值對&#xff08;Key-Value Pair&#xff09;形式存儲數據&#xff0c;支持快速查找、插入和刪除操作。 本文將系統性地介紹 Python 字典中常見的查詢操作方法&…

pyhton基礎【18】面向對象基礎一

目錄 一.面向對象 二.面向對象概述 三.類與對象 一.面向對象 Python中的面向對象編程OOP是一種編程范式&#xff0c;它使用對象來設計軟件。對象是具有屬性(稱為屬性)和可以執行的操作(稱為方法)的數據結構。 基礎概念 類&#xff1a;class 類是創建對象的藍圖或模板。它…