詳解緩存淘汰策略:LRU

文章目錄

  • 緩存淘汰策略
  • LRU
    • 核心結構
    • 核心操作流程
    • 局限性
  • 源碼走讀
    • Add
    • Get

緩存淘汰策略

緩存淘汰策略的存在是為了解決 緩存容量有限性高緩存命中率 之間的矛盾。其核心目標是在有限的緩存空間內,盡可能提高緩存命中率


  • 緩存容量有限性:緩存(例如進程的內存緩存)的空間是有限的。當緩存空間被填滿,又來了新數據時,需要淘汰一些老數據,給新數據騰出空間
  • 高緩存命中率:決定要淘汰哪些數據,對于提高緩存命中率至關重要。如果選擇淘汰熱數據,那么緩存命中率就低。反之如果淘汰冷數據,緩存命中率就高

常見的緩存淘汰策略有LRU,2q,LFU,tinyLFU等。本文介紹第一種:LRU

LRU

其核心思想是:當緩存空間不足時,優先淘汰最久未被訪問的數據。它基于“時間局部性”原理,即假設最近被訪問的數據更有可能在未來被再次訪問

核心結構

LRU的核心結構為 一個哈希表 + 一個雙向鏈表

  • 雙向鏈表:按訪問時間順序維護緩存項,鏈表頭部是最近使用的項,尾部是最久未使用的項(淘汰候選)

    • 鏈表的每個節點entry包含以下字段:key,value,prev(鏈表上一個節點),next(鏈表下一個節點)
  • 哈希表:存儲鍵(Key)到鏈表節點(Node)的映射

在這里插入圖片描述

核心操作流程

  1. 訪問數據(Get):
    1. 通過哈希表在 O(1) 時間內找到對應鏈表節點。
    2. 將該節點從鏈表中刪除(O(1)),并重新插入到鏈表頭部(O(1))。
    3. 返回節點值。
  2. 插入數據(Put):
    1. 如果鍵已存在:更新值,并像 Get 一樣將節點移動到頭部。
    2. 如果鍵不存在:
      1. 創建新節點,插入鏈表頭部(O(1))。
      2. 將鍵和節點存入哈希表(O(1))。
      3. 如果緩存已滿,刪除鏈表尾部節點(O(1)),并同步刪除哈希表中對應的鍵。

可以看出通過哈希表和雙向鏈表的配合,Get和Put的時間復雜度都是O(1)非常高效


一些設計上的關鍵問題:

  • 為啥不用單鏈表,要用雙向鏈表?
    • 拿到要刪除的節點后,單鏈表無法在 O(1) 時間內刪除一個節點
  • 為啥節點需要存儲key?
    • 當某個節點被淘汰時,可以O(1)時間根據key去哈希表中進行刪除操作

局限性

上面介紹的LRU有下面的局限性:

  • 突發流量污染:如果某個很少訪問的項在短時間內被突然大量訪問(即使之后不再訪問),它會長時間占據緩存頭部,擠掉可能更熱(訪問頻率更高但近期未被訪問)的項。
  • 沒有考慮緩存項的頻率:例如一個只訪問一次但剛好是最近訪問的項,會排在訪問了十次但稍早訪問的項前面。但這在大多數場景下是不符合預期的

這兩個問題在2q,LFU,tinyLFU會得到解決

源碼走讀

下面將針對一個經典的開源庫https://github.com/hashicorp/golang-lru的LRU實現進行源碼走讀,版本:v2.0.7

數據結構如下:

type LRU[K comparable, V any] struct {// 表示緩存的最大容量size int// 雙向鏈表,用于維護緩存中條目的訪問順序evictList *internal.LruList[K, V]// 是一個哈希表(字典),將鍵(K)映射到對應的緩存條目(*internal.Entry) items   map[K]*internal.Entry[K, V]onEvict EvictCallback[K, V]
}

每個entry的核心字段如下:
type Entry[K comparable, V any] struct {next, prev *Entry[K, V]// 屬于哪個鏈表list *LruList[K, V]Key KValue V
}

Add

func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {// 檢查是否已存在相同鍵的條目if ent, ok := c.items[key]; ok {// 如果存在,則將該條目移動到雙向鏈表的最前面(表示最近使用),并更新其值c.evictList.MoveToFront(ent)ent.Value = valuereturn false}// 將新的鍵值對插入到雙向鏈表的頭部,表示這是最新的訪問項ent := c.evictList.PushFront(key, value)// 同時將該條目加入到哈希表 items 中,以便后續快速查找c.items[key] = ent// 判斷是否超出容量限制evict := c.evictList.Length() > c.sizeif evict {// 移除最老的元素c.removeOldest()}return evict
}

移除最老元素流程如下:

func (c *LRU[K, V]) removeOldest() {// 找到雙向鏈表中最老的元素entif ent := c.evictList.Back(); ent != nil {c.removeElement(ent)}
}func (c *LRU[K, V]) removeElement(e *internal.Entry[K, V]) {// 從雙向鏈表中移除c.evictList.Remove(e)// 從哈希表中刪除delete(c.items, e.Key)if c.onEvict != nil {c.onEvict(e.Key, e.Value)}
}

Get

func (c *LRU[K, V]) Get(key K) (value V, ok bool) {// 如果存在,將其移動到鏈表頭部,標識最近訪問if ent, ok := c.items[key]; ok {c.evictList.MoveToFront(ent)return ent.Value, true}return
}

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

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

相關文章

什么是 Bootloader?怎么把它移植到 STM32 上?

一、Bootloader 是啥?它都干了些啥?想象一下你的 MCU(比如 STM32)是一個小機器人,上電之后第一件事,它不會立馬開始“干正事”(運行你的主程序),而是先去運行一個“開場引…

無人機避障——感知篇(Ego_Planner_v2中的滾動窗口實現動態實時感知建圖grid_map ROS節點理解與參數調整影響)

處理器:Orin nx 雙目視覺傳感器:ZED2 實時感知建圖方法:Vins Fusion Raycast (VIO與射線投影法感知定位加建圖方法) 項目地址:https://github.com/ZJU-FAST-Lab/EGO-Planner-v2 【注意】:建…

26-計組-尋址方式

指令尋址與PC自增一、指令尋址方式定義:尋找下一條將要執行的指令地址的過程。 核心部件:程序計數器(PC),用于指示待執行指令的地址。 執行流程:CPU根據PC值從主存取指令。取指后,PC自動自增&am…

生成式對抗網絡(GAN)模型原理概述

生成對抗網絡(Generative Adversarial Network, GAN)是一種通過對抗訓練生成數據的深度學習模型,由生成器(Generator)和判別器(Discriminator)兩部分組成,其核心思想源于博弈論中的零…

Vue和Element的使用

文章目錄1.vue 腳手架創建步驟2.vue項目開發流程3.vue路由4.Element1.vue 腳手架創建步驟 創建一個文件夾 vue雙擊進入文件夾,在路徑上輸入cmd輸入vue ui, 目的:調出圖形化用戶界面點擊創建 9. 10.在vscode中打開 主要目錄介紹 src目錄介紹 vue項目啟動 圖形化界面中沒有npm…

如何設置直播間的觀看門檻,讓直播間安全有效地運行?

文章目錄前言一、直播間觀看門檻有哪幾種形式?二、設置直播間的觀看門檻,對直播的好處是什么三、如何一站式實現上述功能?總結前言 打造一個安全、高效、互動良好的直播間并非易事。面對海量涌入的觀眾,如何有效識別并阻擋潛在的…

【SkyWalking】配置告警規則并通過 Webhook 推送釘釘通知

🧭 本文為 【SkyWalking 系列】第 3 篇 👉 系列導航:點擊跳轉 【SkyWalking】配置告警規則并通過 Webhook 推送釘釘通知 簡介 介紹 SkyWalking 告警機制、告警規則格式以及如何通過 webhook 方式將告警信息發送到釘釘。 引入 服務響應超時…

關于 驗證碼系統 詳解

驗證碼系統的目的是:阻止自動化腳本訪問網頁資源,驗證訪問者是否為真實人類用戶。它通過各種測試(圖像、行為、計算等)判斷請求是否來自機器人。一、驗證碼系統的整體架構驗證碼系統通常由 客戶端 服務端 風控模型 數據采集 四…

微服務集成snail-job分布式定時任務系統實踐

前言 從事開發工作的同學,應該對定時任務的概念并不陌生,就是我們的系統在運行過程中能夠自動執行的一些任務、工作流程,無需人工干預。常見的使用場景包括:數據庫的定時備份、文件系統的定時上傳云端服務、每天早上的業務報表數…

依賴注入的邏輯基于Java語言

對于一個廚師,要做一道菜。傳統的做法是:你需要什么食材,就自己去菜市場買什么。這意味著你必須知道去哪個菜市場、怎么挑選食材、怎么討價還價等等。你不僅要會做菜,還要會買菜,職責變得復雜了。 而依賴注入就像是有一…

skywalking鏡像應用springboot的例子

目錄 1、skywalking-ui連接skywalking-oap服務失敗問題 2、k8s環境 檢查skywalking-oap服務狀態 3、本地iidea啟動服務連接skywalking oap服務 4、基于apache-skywalking-java-agent-9.4.0.tgz構建skywalking-agent鏡像 4.1、Dockerfile內容如下 4.2、AbstractBuilder.M…

3. java 堆和 JVM 內存結構

1. JVM介紹和運行流程-CSDN博客 2. 什么是程序計數器-CSDN博客 3. java 堆和 JVM 內存結構-CSDN博客 4. 虛擬機棧-CSDN博客 5. JVM 的方法區-CSDN博客 6. JVM直接內存-CSDN博客 7. JVM類加載器與雙親委派模型-CSDN博客 8. JVM類裝載的執行過程-CSDN博客 9. JVM垃圾回收…

UnityShader——SSAO

目錄 1.是什么 2.原理 3.各部分解釋 2.1.從屏幕空間到視圖空間 2.2.以法線半球為基,獲取隨機向量 2.3.應用偏移,并將其轉換為uv坐標 2.4.獲取深度 2.5.比較并計算貢獻 2.6.最后計算 4.改進 4.1.平滑過渡 4.2.模糊 5.變量和語句解釋 5.1._D…

【設計模式】外觀模式(門面模式)

外觀模式(Facade Pattern)詳解一、外觀模式簡介 外觀模式(Facade Pattern) 是一種 結構型設計模式,它為一個復雜的子系統提供一個統一的高層接口,使得子系統更容易使用。 外觀模式又稱為門面模式&#xff0…

【6.1.1 漫畫分庫分表】

漫畫分庫分表 “數據量大了不可怕,可怕的是不知道如何優雅地拆分。” 🎭 人物介紹 架構師老王:資深數據庫架構專家,精通各種分庫分表方案Java小明:對分庫分表充滿疑問的開發者ShardingSphere師傅:Apache S…

Tomcat問題:啟動腳本startup.bat中文亂碼問題解決

一、問題描述 我們第一次下載或者打開Tomcat時可能在控制臺會出現中文亂碼問題二、解決辦法 我的是8.x版本的tomcat用notepad打開:logging.properties 找到:java.util.logging.ConsoleHandler.encoding設置成GBK,重啟tomcat即可

Linux中Gitee的使用

一、Gitee簡介:Gitee(碼云)是中國的一個代碼托管和協作開發平臺,類似于GitHub或GitLab,主要面向開發者提供代碼管理、項目協作及開源生態服務。適用場景個人開發者:托管私有代碼或參與開源項目。中小企業&a…

Oracle大表數據清理優化與注意事項詳解

一、性能優化策略 1. 批量處理優化批量大小選擇: 小批量(1,000-10,000行):減少UNDO生成,但需要更多提交次數中批量(10,000-100,000行):平衡性能與資源消耗大批量(100,000行):適合高配置環境,但需監控資源使…

Anaconda及Conda介紹及使用

文章目錄Anaconda簡介為什么選擇 Anaconda?Anaconda 安裝Win 平臺macOS 平臺Linux 平臺Anaconda 界面使用Conda簡介Conda下載安裝conda 命令環境管理包管理其他常用命令Jupyter Notebook(可選)Anaconda簡介 Anaconda 是一個數據科學和機器學…

外包干了一周,技術明顯退步

我是一名本科生,自2019年起,我便在南京某軟件公司擔任功能測試的工作。這份工作雖然穩定,但日復一日的重復性工作讓我逐漸陷入了舒適區,失去了前進的動力。兩年的時光匆匆流逝,我卻在原地踏步,技術沒有絲毫…