Redis記憶體兜底策略——記憶體淘汰及回收機制

来源:https://www.cnblogs.com/reim/archive/2023/05/23/17422410.html
-Advertisement-
Play Games

## 一、模式動機 >觀察者模式用於描述對象之間的依賴關係,它引入了觀察者和觀察目標兩類不同的角色,由於提供了抽象層,它使得增加新的觀察者和觀察目標都很方便。觀察者模式廣泛應用於各種編程語言的事件處理模型中,Java語言也提供了對觀察者模式的全面支持。 - 一個對象的狀態或行為的變化將導致其他對象的 ...


Redis記憶體兜底策略——記憶體淘汰及回收機制

Redis記憶體淘汰及回收策略都是Redis記憶體優化兜底的策略,那它們是如何進行兜底的呢?先來說明一下什麼是記憶體淘汰和記憶體回收策略:

  • Redis記憶體淘汰:當Redis的記憶體使用超過配置的限制時,根據一定的策略刪除一些鍵,以釋放記憶體空間
  • Redis記憶體回收:Redis通過定期刪除惰性刪除兩種方式來清除過期的鍵,以保證數據的時效性和減少記憶體占用

記憶體淘汰策略

Redis記憶體淘汰策略是指當Redis的記憶體使用超過配置的最大值時,如何選擇一些鍵進行刪除,以釋放空間給新的數據。Redis提供了八種記憶體淘汰策略,分別是:

  • noeviction:不會淘汰任何鍵,達到記憶體限制後返回錯誤
  • allkeys-random:在所有鍵中,隨機刪除鍵
  • volatile-random:在設置了過期時間的鍵中,隨機刪除鍵
  • allkeys-lru:通過LRU演算法淘汰最近最少使用的鍵,保留最近使用的鍵
  • volatile-lru:從設置了過期時間的鍵中,通過LRU演算法淘汰最近最少使用的鍵
  • allkeys-lfu:從所有鍵中淘汰使用頻率最少的鍵。從所有鍵中驅逐使用頻率最少的鍵
  • volatile-lfu:從設置了過期時間的鍵中,通過LFU演算法淘汰使用頻率最少的鍵
  • volatile-ttl:從設置了過期時間的鍵中,淘汰馬上就要過期的鍵

LRU和LFU

LRU(least frequently used)演算法為最近最少使用演算法,根據數據的歷史訪問記錄來進行淘汰數據,優先移除最近最少使用的數據,這種演算法認為最近使用的數據很大概率將會再次被使用

LFU(least frequently used)演算法為最少頻率使用演算法,優先移除使用頻率最少的數據,這種演算法認為使用頻率高的數據很大概率將會再次被使用

LRU和Redis的近似LRU

LRU(least frequently used)演算法為最近最少使用演算法,根據數據的歷史訪問記錄來進行淘汰數據,優先移除最近最少使用的數據,這種演算法認為最近使用的數據很大概率將會再次被使用

什麼是LRU

在演算法的選擇上,Redis需要能夠快速地查詢、添加、刪除數據,也就是說查詢、添加、刪除的時間複雜讀需為O(1)。哈希表能保證查詢數據的時間複雜度為O(1)。而雙向鏈表能保證添加、刪除數據的時間複雜度為O(1),如下:

image

Redis的近似LRU

如前文所述,真實的 LRU 演算法需要用鏈表管理所有的數據,每次訪問一個數據就要移動鏈表節點,這樣會占用額外的空間和時間。而Redis通過近似 LRU 演算法,隨機抽樣一些鍵,然後比較它們的訪問時間戳,這樣可以節省記憶體和提高性能。而Redis 的近似 LRU 演算法的具體實現如下:

  • 每個鍵值對對象(redisObject)中有一個 24 位的 lru 欄位,用於記錄每個數據最近一次被訪問的時間戳
  • 每次按鍵獲取一個值的時候,都會調用 lookupKey 函數,如果配置使用了 LRU 模式,該函數會更新 value 中的 lru 欄位為當前秒級別的時間戳
  • 當記憶體達到限制時,Redis 會維護一個候選集合(pool),大小為 16
  • Redis 會隨機從字典中取出 N 個鍵(N 可以通過 maxmemory-samples 參數設置,預設為 5),將 lru 欄位值最小的鍵放入候選集合中,並按照 lru 大小排序
  • 如果候選集合已滿,那麼新加入的鍵必須有比集合中最大的 lru 值更小的 lru 值,才能替換掉原來的鍵
  • 當需要淘汰數據時,直接從候選集合中選擇一個 lru 最小的鍵進行淘汰

舉個例子,假設我們按照下麵的順序訪問緩存中的數據:h,e,l,l,o,w,o,r,l,d且記憶體中只能存儲3個字元,下麵是每次訪問或插入後緩存的狀態,其中括弧內是lru欄位的值,假設初始時間戳為0

緩存 狀態
訪問h h(0)
訪問e e(1),h(0)
訪問l l(2),e(1),h(0)
訪問l l(3),e(1),h(0)
插入o o(4),l(3),e(1)
插入w w(5),o(4),l(3)
訪問o o(6),w(5),l(3)
插入r r(7),o(6),w(5)
插入l l(8),r(7),o(6)
插入d d(9),l(8),r(7)

LFU

LFU(Least Frequently Used)是最不經常使用演算法,它的思想是淘汰訪問頻率最低的數據。Redis在3.0版本之後引入了LFU演算法,並對lru欄位進行了拆分。

typedef struct redisObject {
    unsigned type:4;
    unsigned encoding:4;
    unsigned lru:LRU_BITS;
    int refcount;
    void *ptr;
} robj;

我們看lru:LRU_BITS這個欄位,這個欄位在LRU演算法中的意義是時間戳,精確到秒。而在LFU 演算法中,將它拆為兩部分前16bits為時間戳,精確到分;後8為則表示該對象在一定時間段內被訪問的次數。如下:
image

當Redis需要淘汰數據時,它會從記憶體中隨機抽取一定數量(預設為5個,可以通過 maxmemory-samples 參數設置)的鍵值對對象,然後比較它們的訪問次數和訪問時間戳,找出其中最小的那個,也就是最不經常使用且最早被訪問的那個,將其從記憶體中刪除。

例如,假設我們有以下鍵值對和頻率計數器:

頻率
A 1 3
B 2 2
C 3 1
D 4 4

如果我們要添加一個新的鍵值對(E,5),並且緩存已經滿了,那麼我們就需要淘汰一個舊的鍵值對。我們可以隨機選擇A,B,C中的一個,並且發現C的頻率最低,為1,所以我們就淘汰C,並且添加E到緩存中,並且將E的頻率設為1。這樣,緩存中的數據就變成了:

頻率
A 1 3
B 2 2
D 4 4
E 5 1

如何選擇

在選擇上,需要根據不同的適用場景選擇不同策略,如下:

策略 特點 適用場景
noeviction 不刪除任何數據,當記憶體不足時返回錯誤 數據都是永久有效的,且記憶體足夠大
allkeys-lru 根據所有數據的訪問時間來淘汰最久未訪問的數據 數據都是永久有效的,且訪問時間具有明顯規律
volatile-lru 根據設置了過期時間的數據的訪問時間來淘汰最久未訪問的數據 數據都有過期時間,且訪問時間具有明顯規律
allkeys-random 隨機淘汰所有類型的數據 數據都是永久有效的,且訪問時間沒有明顯規律
volatile-random 隨機淘汰設置了過期時間的數據 數據都有過期時間,且訪問時間沒有明顯規律
volatile-ttl 根據設置了過期時間的數據的剩餘生命周期來淘汰即將過期的數據 數據都有過期時間,且剩餘生命周期具有明顯規律
allkeys-lfu 根據所有數據的訪問頻率來淘汰最少訪問的數據 數據都是永久有效的,且訪問頻率具有明顯規律
volatile-lfu 根據設置了過期時間的數據的訪問頻率來淘汰最少訪問的數據 數據都有過期時間,且訪問頻率具有明顯規律

根據8種策略的特性,也從數據完整性緩存命中率淘汰效率這三個方面詳細對比了,如下:

  • 數據完整性(是否會刪除永久有效的數據)
    • noevictionvolatile-lruvolatile-lfuvolatile-random 都可以保證數據完整性,因為它們不會刪除永久有效的數據
    • allkeys-lruallkeys-lfuallkeys-random系列的策略則會影響數據完整性,因為它們會無差別地刪除所有類型的數據
  • 緩存命中率(是否能夠儘可能保留最有價值的數據)
    • allkeys-lruvolatile-lru 策略可以提高緩存命中率,因為它們會根據數據的訪問時間來淘汰數據
    • allkeys-randomvolatile-random 策略則會降低緩存命中率,因為它們會隨機淘汰數據
    • allkeys-lfuvolatile-lfu 策略也可以提高緩存命中率,因為它們會根據數據的訪問頻率來淘汰數據
    • volatile-ttl 策略則會降低緩存命中率,因為它會根據數據的剩餘生命周期來淘汰數據
  • 淘汰效率(是否能夠快速地找到並刪除目標數據)
    • allkeys-randomvolatile-random 策略可以提高執行效率,因為它們只需要隨機選擇一些數據進行刪除
    • allkeys-lruvolatile-lru 策略則會降低執行效率,因為它們需要對所有或部分數據進行排序
    • allkeys-lfuvolatile-lfu 策略也會降低執行效率,因為它們需要對所有或部分數據進行計數和排序
    • volatile-ttl 策略則會提高執行效率,因為它只需要對設置了過期時間的數據進行排序

記憶體回收策略

Redis的過期鍵刪除有兩種方式,一種是定期刪除,一種是惰性刪除

惰性刪除

Redis惰性刪除是指當一個鍵過期後,它並不會立即被刪除,而是在客戶端嘗試訪問這個鍵時,Redis會檢查這個鍵是否過期,如果過期了,就會刪除這個鍵。惰性刪除由db.c/expireIfNeeded函數實現。

惰性刪除的優點是節約CPU性能,發現必須刪除的時候才刪除。缺點是記憶體壓力很大,出現長期占用記憶體的數據。惰性刪除是Redis的預設策略,它不需要額外的配置。

惰性刪除的缺點是可能會導致過期鍵長時間占用記憶體,如果訪問頻率較低的鍵過期了,但沒有被訪問到,那麼它們就不會被惰性刪除,從而浪費記憶體空間。

為瞭解決這個問題,Redis還採用了定期刪除和記憶體淘汰機制來配合惰性刪除,以達到更好的清理效果

定期刪除

Redis會將設置了過期時間的鍵放到一個獨立的字典中,稱為過期字典。Redis會對這個字典進行每秒10次(由配置文件中的hz參數控制)的過期掃描,過期掃描不會遍歷字典中所有的鍵,而是採用了一種簡單的貪心策略。該策略的刪除邏輯如下:

  • 從過期字典中隨機選擇20個鍵
  • 刪除其中已經過期的鍵
  • 如果超過25%的鍵被刪除,則重覆步驟1
  • 如果本次掃描耗時超過1毫秒,則停止掃描

這種策略可以在一定程度上保證過期鍵能夠及時被刪除,同時也避免了對CPU時間的過度占用。但是它也有一些缺點,比如可能會誤刪一些有效的鍵(因為隨機性),或者漏刪一些無效的鍵(因為限制了掃描時間)

因此,Redis還結合了惰性刪除策略,即在每次訪問一個鍵之前,都會檢查這個鍵是否過期,如果過期就刪除,然後返回空值。這樣可以保證不返回過期的數據,也可以節省CPU時間,但是它可能會導致一些過期的鍵長期占用記憶體,如果這些鍵很少被訪問或者一直不被訪問,那麼它們就永遠不會被刪除

配置文件說明

Redis記憶體淘汰、記憶體回收策略相關的配置文件如下:

# 記憶體淘汰策略
maxmemory-policy noeviction
# 抽取數量
maxmemory-samples 5
# 最大記憶體
maxmemory 100mb
# 記憶體淘汰韌性
maxmemory-eviction-tenacity 10
# 後臺任務執行間隔
hz 10
# 是否開啟動態間隔
dynamic-hz yes

配置文件說明:

  • maxmemory-policy:記憶體淘汰策略,可選值為noeviction、allkeys-random、volatile-random、allkeys-lru、volatile-lru、allkeys-lfu、volatile-lfu、volatile-ttl其中的一個
  • maxmemory:預設值為0,也就是不限制記憶體的使用。
  • maxmemory-samples:抽取數量,預設為5,如果設為10將非常接近真實的LRU,但需要更多CPU資源,如果設為3將非常快,但是非常不准確。
  • maxmemory-eviction-tenacity:記憶體淘汰韌性,預設為10
    • maxmemory-eviction-tenacity為0時,表示不進行任何淘汰,相當於noeviction策略
    • maxmemory-eviction-tenacity為10時,表示每次淘汰鍵的數量為記憶體使用量的0.1%,每秒最多淘汰10次
  • hz:Redis後臺任務執行間隔,如超時關閉客戶端連接、定期刪除等。預設值為10。範圍在 1 到 500 之間,官方建議不要超過100,大多數應使用預設值,並且只有在極低延遲的環境中才能設為 100
  • dynamic-hz:此配置用於動態調整hz的值,預設開啟。如果有大量客戶端連接進來時,會以hz的配置值將作為基線,將hz的實際值設置為hz的配置值的整數倍,用來節省CPU資源。

總結

總結如下:

  • Redis記憶體淘汰機制是指在Redis的用於緩存的記憶體不足時,怎麼處理需要新寫入且需要申請額外空間的數據
  • Redis提供了八種記憶體淘汰策略,分別是:
    • noeviction:不會淘汰任何鍵,達到記憶體限制後返回錯誤
    • allkeys-random:在所有鍵中,隨機刪除鍵
    • volatile-random:在設置了過期時間的鍵中,隨機刪除鍵
    • allkeys-lru:通過LRU演算法淘汰最近最少使用的鍵,保留最近使用的鍵
    • volatile-lru:從設置了過期時間的鍵中,通過LRU演算法淘汰最近最少使用的鍵
    • allkeys-lfu:從所有鍵中淘汰使用頻率最少的鍵。從所有鍵中驅逐使用頻率最少的鍵
    • volatile-lfu:從設置了過期時間的鍵中,通過LFU演算法淘汰使用頻率最少的鍵
    • volatile-ttl:從設置了過期時間的鍵中,淘汰馬上就要過期的鍵
  • Redis記憶體回收機制是指在Redis中如何刪除已經過期或者被淘汰的數據,釋放記憶體空間
  • Redis提供了兩種記憶體回收策略,分別是:
    • 定期刪除:Redis會每隔一定時間(預設100ms)隨機抽取一些設置了過期時間的鍵,檢查它們是否過期,如果過期就刪除。這種策略可以減少CPU開銷,但可能會導致一些過期鍵占用記憶體
    • 惰性刪除:Redis在客戶端訪問一個鍵時,會檢查這個鍵是否過期,如果過期就刪除。這種策略可以及時釋放記憶體空間,但可能會增加CPU開銷和延遲

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 這是從一個中藥大全查詢系統中破解提取出來的中藥驗方大全,整理出了數以萬計的各家經方、時方、驗方以及秘方的用藥方劑組成,用法用量以及每組方劑的功效性能、主治應用等。有了這樣一套完善的醫學資料你可以節省很多查閱資料的時間可以更方便快捷的查閱你需要的信息。 加味涼血退斑湯 組成:鮮生地30克,鮮蘆根30克 ...
  • 中國出海中東和北非地區的策略類手游《蘇丹的復仇》(Revenge of Sultans,ROS)和華為HMS生態深度合作,為本地用戶帶來創新游戲體驗,成為當地廣受歡迎的游戲之一,下載量居應用市場前列。2023年5月10日,在阿聯酋迪拜舉辦的HUAWEI P60系列及旗艦產品發佈會中,ONEMT中東G ...
  • 本文主要介紹在業務複雜化背景下,京東零售購物車團隊努力踐行工匠精神,通過全非同步化改造提升系統性能、提升用戶體驗。通過本文,讀者可以瞭解購物車中台進行全非同步化改造的總體方案,以及方案落地過程中遇到的問題及解決方法,讀者可重點關註文中提到的多分頁並行後,分頁精細控制及底層RPC異常信息問題。 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 avaScript中有些API可能使用率比較低,下麵我們逐一介紹它們的用法和使用場景。 至於標題,主要是想讓你進來看看,兄弟們別打我! Blob API Blob API 用於處理二進位數據,可以方便地將數據轉換為Blob對象或從Blob ...
  • ## n 如果在我的電腦上已經安裝了nodejs,但是覺得這個版本不好用,或者是不相容公司的項目,那麼可以使用n進行node的版本管理。n相對於nvm來說,安裝起來還是非常方便的。 ## 安裝 #### 1. 首先確定nodejs版本,確定已安裝nodejs ``` node -v ``` #### ...
  • Deferred Components,官方實現的Flutter代碼動態下發的方案。本文主要介紹官方方案的實現細節,探索在國內環境下使用Deferred Components,並且實現了最小驗證demo。讀罷本文,你就可以實現Dart文件級別代碼的動態下發。 ...
  • 在 JavaScript 中, undefined 和 null 是兩個特殊的值,用於表示缺失或空值。 undefined 是一個表示未定義或未賦值的原始值。它在以下情況下使用: 1. 變數聲明瞭但未初始化時,預設為 undefined 。 let x; console.log(x); // und ...
  • # React筆記-Hooks(九) ## Hooks ### 概念 >React Hooks 的意思是 組件儘量寫成純函數 如果需要外部功能和副作用 就用鉤子把外部代碼"鉤"進來 ### 函數組件和類組件區別 >- 函數組件沒有狀態(state) 類組件有 >- 函數組件沒有生命周期 類組件有(掛 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...