Redis中刪除策略的幾種實現(xiàn)方式
前言
要全面、深入理解 Redis 的刪除策略,需從設(shè)計背景、核心分類(過期鍵刪除策略 + 內(nèi)存淘汰策略)、內(nèi)部實現(xiàn)細節(jié)、與持久化的關(guān)聯(lián)及實踐選擇五個維度展開。Redis 的刪除策略本質(zhì)是為了解決 “內(nèi)存有限” 與 “性能優(yōu)先” 的核心矛盾 —— 既要避免內(nèi)存溢出,又要減少刪除操作對單線程主線程的阻塞,同時保證數(shù)據(jù)一致性。
一、設(shè)計背景:為什么需要刪除策略?
Redis 是內(nèi)存數(shù)據(jù)庫,內(nèi)存資源昂貴且有限,若不主動管理內(nèi)存,會導(dǎo)致內(nèi)存溢出(OOM);同時,Redis 支持為鍵設(shè)置過期時間(TTL),需保證過期鍵能被合理清理;此外,Redis 是單線程模型,刪除操作若過于耗時,會阻塞主線程,導(dǎo)致響應(yīng)延遲。因此,Redis 設(shè)計了兩類互補的刪除策略:
- 過期鍵刪除策略:主動處理 “已過期但未刪除” 的鍵,減少無效內(nèi)存占用;
- 內(nèi)存淘汰策略:當內(nèi)存達到上限時,被動刪除部分鍵以釋放內(nèi)存,保證服務(wù)可用性。
二、第一類:過期鍵的 3 種核心刪除策略
Redis 通過過期字典(expires dict) 單獨存儲所有鍵的過期時間(key 為目標鍵,value 為過期時間戳),所有過期鍵刪除策略均基于此字典實現(xiàn)。三種策略各有優(yōu)劣,Redis 最終采用 “惰性刪除 + 定期刪除” 的組合方案。

1. 定時刪除(Timed Delete):“實時清理,CPU 殺手”
定義:為每個設(shè)置了過期時間的鍵,創(chuàng)建一個定時器(Timer),當鍵的過期時間到達時,定時器立即觸發(fā),執(zhí)行DEL命令刪除該鍵。
實現(xiàn)邏輯:
- 當調(diào)用
EXPIRE key ttl時,Redis 不僅在過期字典中記錄過期時間,還會注冊一個定時器; - 定時器到期后,直接在主線程中執(zhí)行刪除操作。
優(yōu)缺點:
| 優(yōu)點 | 缺點 |
|---|---|
| 內(nèi)存最 “干凈”:過期鍵立即刪除,無無效內(nèi)存占用; | CPU 壓力大:若存在大量過期鍵,定時器密集觸發(fā),會占用大量 CPU 資源,導(dǎo)致主線程阻塞,影響 Redis 響應(yīng)速度; |
| 數(shù)據(jù)一致性高:不會出現(xiàn) “讀取到過期鍵” 的情況; | 定時器管理成本高:每個過期鍵對應(yīng)一個定時器,內(nèi)存開銷也會增加。 |
適用場景:
幾乎不適用。Redis 是單線程模型,定時刪除的 CPU 開銷會嚴重影響服務(wù)性能,因此 Redis未采用此策略。
2. 惰性刪除(Lazy Delete):“用的時候再刪,內(nèi)存隱患”
定義:Redis 不主動刪除過期鍵,而是在訪問鍵的瞬間(如GET、HGET、SETNX等操作),才檢查該鍵是否過期:
- 若未過期:正常返回鍵值;
- 若已過期:執(zhí)行
DEL命令刪除該鍵,返回nil(空)。
實現(xiàn)邏輯:
以GET key為例:
- 先檢查
key是否存在于過期字典中; - 若不存在:直接返回鍵值;
- 若存在:對比當前時間與過期時間戳,判斷是否過期;
- 若已過期:刪除
key(從數(shù)據(jù)庫字典和過期字典中同時移除),返回nil; - 若未過期:返回鍵值。
優(yōu)缺點:
| 優(yōu)點 | 缺點 |
|---|---|
| CPU 友好:僅在 “訪問過期鍵” 時才執(zhí)行刪除,無額外 CPU 開銷,不影響主線程正常請求; | 內(nèi)存泄漏風(fēng)險:若大量過期鍵長期不被訪問,會一直占用內(nèi)存(“僵尸鍵”),導(dǎo)致內(nèi)存利用率低,甚至觸發(fā)內(nèi)存上限; |
| 實現(xiàn)簡單:無需管理定時器,邏輯輕量; | 可能出現(xiàn) “瞬時過期鍵”:若過期鍵未被訪問,其他操作(如KEYS、DBSIZE)會統(tǒng)計到這些過期鍵,導(dǎo)致數(shù)據(jù)統(tǒng)計不準確。 |
3. 定期刪除(Periodic Delete):“折中方案,平衡 CPU 與內(nèi)存”
定義: Redis 每隔一段時間(默認 100ms),主動掃描部分過期鍵并刪除,掃描頻率和范圍由配置控制,避免一次性掃描所有過期鍵導(dǎo)致阻塞。
核心實現(xiàn)邏輯(Redis 6.0+)
- 掃描頻率:由
hz配置控制(默認hz 10,即每秒執(zhí)行 10 次定期掃描,每次間隔約 100ms); - 掃描范圍:每次掃描不遍歷所有過期鍵,而是采用 “采樣 + 限制時間” 的方式:
- 從過期字典中隨機抽取
N個鍵(默認N=20); - 檢查這些鍵是否過期,刪除已過期的鍵;
- 若刪除的鍵占比超過
25%(即刪除數(shù)≥5),則繼續(xù)抽取N個鍵重復(fù)掃描; - 若占比≤25%,或掃描時間超過
2ms(避免阻塞主線程),則停止本次掃描。
- 從過期字典中隨機抽取
優(yōu)缺點:
| 優(yōu)點 | 缺點 |
|---|---|
| 平衡 CPU 與內(nèi)存:既避免了定時刪除的 CPU 壓力,也緩解了惰性刪除的內(nèi)存泄漏問題; | 掃描參數(shù)難調(diào)優(yōu):hz、N、25%閾值需根據(jù)業(yè)務(wù)調(diào)整,若hz過高或N過大,會增加 CPU 負擔;若過低,則內(nèi)存清理不及時; |
非阻塞設(shè)計:單次掃描時間限制在2ms內(nèi),不影響主線程處理請求; | 仍有 “漏刪” 可能:部分過期鍵可能在兩次掃描間隔內(nèi)未被訪問,也未被掃描到,暫時占用內(nèi)存。 |
4. Redis 的最終選擇:惰性刪除 + 定期刪除
兩種策略互補,覆蓋大部分場景:
- 定期刪除:主動 “批量清理” 過期鍵,減少 “僵尸鍵” 數(shù)量,緩解內(nèi)存壓力;
- 惰性刪除:兜底清理 “漏網(wǎng)之魚”,確保訪問時不會返回過期鍵,保證數(shù)據(jù)有效性。
例如:一個過期鍵未被定期掃描刪除,但用戶訪問時,惰性刪除會立即清理它;反之,若過期鍵長期不被訪問,定期掃描會逐步清理它。
三、第二類:內(nèi)存淘汰策略(Maxmemory Eviction)
即使有過期鍵刪除策略,Redis 仍可能因 “大量未設(shè)置過期時間的鍵” 或 “過期鍵刪除不及時” 導(dǎo)致內(nèi)存達到上限(maxmemory配置)。此時,Redis 會觸發(fā)內(nèi)存淘汰策略,刪除部分鍵以釋放內(nèi)存,避免 OOM。
1. 核心前提:maxmemory配置
- 默認值:
0(即不限制內(nèi)存,僅受系統(tǒng)內(nèi)存限制,生產(chǎn)環(huán)境必須手動設(shè)置,如maxmemory 4gb); - 觸發(fā)時機:當 Redis 使用的內(nèi)存(包括鍵值對、過期字典、緩沖區(qū)等)達到
maxmemory時,觸發(fā)淘汰策略(僅對 “寫操作” 觸發(fā),讀操作不受影響)。
2. 8 種內(nèi)存淘汰策略(Redis 5.0+)
根據(jù) “淘汰范圍” 和 “淘汰算法”,分為 4 類,核心區(qū)別是 “是否只淘汰過期鍵” 和 “用什么規(guī)則淘汰”:
| 策略常量 | 淘汰范圍 | 淘汰算法 | 適用場景 |
|---|---|---|---|
| noeviction(默認) | 無(不淘汰任何鍵) | - | 禁止淘汰,內(nèi)存滿時拒絕所有寫請求(返回OOM command not allowed),適合不允許數(shù)據(jù)丟失的場景(如核心配置存儲)。 |
| volatile-lru | 僅淘汰 “設(shè)置了過期時間” 的鍵 | LRU(最近最少使用) | 希望保留常用的過期鍵,淘汰不常用的,適合有明確過期時間且需優(yōu)先保留熱點數(shù)據(jù)的場景(如緩存用戶會話)。 |
| allkeys-lru | 所有鍵(無論是否過期) | LRU(最近最少使用) | 不清楚哪些鍵常用,希望淘汰長期未訪問的鍵,適合通用緩存場景(如商品詳情緩存)。 |
| volatile-lfu | 僅淘汰 “設(shè)置了過期時間” 的鍵 | LFU(最不經(jīng)常使用) | 比 LRU 更精準(統(tǒng)計 “訪問頻率” 而非 “最近訪問時間”),適合淘汰低頻訪問的過期鍵(如低頻訪問的活動頁面緩存)。 |
| allkeys-lfu | 所有鍵(無論是否過期) | LFU(最不經(jīng)常使用) | 適合需要精準淘汰 “低頻率訪問” 鍵的場景(如內(nèi)容推薦系統(tǒng),淘汰很少被點擊的內(nèi)容)。 |
| volatile-random | 僅淘汰 “設(shè)置了過期時間” 的鍵 | 隨機淘汰 | 適合對淘汰鍵無明確優(yōu)先級,僅需釋放內(nèi)存的場景(較少用)。 |
| allkeys-random | 所有鍵(無論是否過期) | 隨機淘汰 | 適合數(shù)據(jù)無熱點、淘汰任意鍵均可的場景(極少用)。 |
| volatile-ttl | 僅淘汰 “設(shè)置了過期時間” 的鍵 | 淘汰 “剩余 TTL 最短” 的鍵 | 希望盡快刪除快過期的鍵,釋放內(nèi)存給新鍵,適合需優(yōu)先保留 “剩余時間長” 的過期鍵的場景(如短期活動緩存,優(yōu)先保留剛創(chuàng)建的活動數(shù)據(jù))。 |
maxmemory最大可使用內(nèi)存 占用物理內(nèi)存的比例,默認值為0,表示不限制,生產(chǎn)環(huán)境中根據(jù)需求設(shè)定,通常設(shè)置在50%以上。
maxmemory-samples每次選取待刪除數(shù)據(jù)的個數(shù) 選取數(shù)據(jù)時并不會全庫掃描,導(dǎo)致嚴重的性能消耗,降低讀寫性能。因此采用隨機獲取數(shù)據(jù)的方式 作為待檢測刪除數(shù)據(jù)
maxmemory-policy刪除策略
檢測易失數(shù)據(jù)(可能會過期的數(shù)據(jù)集server.db[i].expires )
① volatile-lru:挑選最近最少使用的數(shù)據(jù)淘汰
② volatile-lfu:挑選最近使用次數(shù)最少的數(shù)據(jù)淘汰
③ volatile-ttl:挑選將要過期的數(shù)據(jù)淘汰
④ volatile-random:任意選擇數(shù)據(jù)淘汰 檢測全庫數(shù)據(jù)(所有數(shù)據(jù)集server.db[i].dict )
⑤ allkeys-lru:挑選最近最少使用的數(shù)據(jù)淘汰
⑥ allkeys-lfu:挑選最近使用次數(shù)最少的數(shù)據(jù)淘汰
⑦ allkeys-random:任意選擇數(shù)據(jù)淘汰 放棄數(shù)據(jù)驅(qū)逐
⑧ no-enviction(驅(qū)逐):禁止驅(qū)逐數(shù)據(jù)(redis4.0中默認策略),會引發(fā)錯誤OOM(Out Of Memory)達到最大內(nèi)存后的,對被挑選出來的數(shù)據(jù)進行刪除的策略,如下圖:

四、實踐建議:如何選擇刪除策略?
- 優(yōu)先關(guān)閉默認的noeviction策略:生產(chǎn)環(huán)境若用 Redis 做緩存,noeviction會導(dǎo)致內(nèi)存滿時寫請求失敗,建議替換為allkeys-lru或allkeys-lfu;
- 根據(jù)業(yè)務(wù)場景選擇淘汰算法:
- 通用緩存(如商品、頁面緩存):選allkeys-lru(簡單且有效);
- 訪問頻率波動大的場景(如內(nèi)容推薦、促銷活動):選allkeys-lfu(更精準);
- 有明確過期時間的場景(如會話、短期活動):選volatile-ttl或volatile-lfu;
- 合理配置maxmemory和hz:
- maxmemory:建議設(shè)置為物理內(nèi)存的 50%-70%(避免 Redis 占用過多內(nèi)存導(dǎo)致系統(tǒng) OOM);
- hz:默認 10 即可,若內(nèi)存壓力大,可適當提高到 20(增加定期掃描頻率),但不建議超過 100(避免 CPU 開銷過高);
- 避免大量設(shè)置短期過期鍵:若需頻繁清理短期數(shù)據(jù),優(yōu)先用volatile-ttl策略,而非依賴定時刪除。
總結(jié)
Redis 的刪除策略是 “主動清理(定期刪除)+ 被動兜底(惰性刪除)+ 內(nèi)存溢出保護(內(nèi)存淘汰)” 的三層架構(gòu),核心目標是平衡 CPU 資源、內(nèi)存資源與數(shù)據(jù)一致性。理解每種策略的適用場景,結(jié)合業(yè)務(wù)需求選擇合適的內(nèi)存淘汰策略,是保障 Redis 高性能、高可用的關(guān)鍵。
到此這篇關(guān)于Redis中刪除策略的幾種實現(xiàn)方式的文章就介紹到這了,更多相關(guān)Redis 刪除策略內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

