
一個示例
? redis中,set一個值,有效期為 3600 秒,這個值會什么時候釋放?
常規(guī)理解為,在 3600 秒到期后,redis服務(wù)會自動清除,
也就意味著,redis需要有一個機制,每隔一個時間極短的時間段去掃描所有的key,判斷是否到期并刪除,這種開銷是巨大的
過期刪除
對于設(shè)置了過期時間的 key,刪除有兩個機制共同管理
? 惰性刪除(訪問時釋放)
觸發(fā)時機:當(dāng)客戶端訪問該 key(如執(zhí)行 GET、TTL、EXISTS 等命令)時。
行為:Redis 檢測到該 key 已過期,立即物理刪除該 key,釋放內(nèi)存,然后返回 nil。如果沒被訪問,即使過了 100 秒,key 仍可能留在內(nèi)存中。
?? 關(guān)鍵點:如果這個 key 過期后再也沒有被訪問過,它會一直占用內(nèi)存,直到被其他機制清理。
? 定期刪除(后臺定時釋放)
觸發(fā)條件:Redis 后臺每隔 100ms(默認配置)會啟動一次(后臺任務(wù))定期掃描任務(wù),隨機抽取部分帶過期時間的 key 檢查。
行為:從所有設(shè)置了過期時間的 key 中隨機抽取 n 個;刪除其中已過期的 key;
?? 關(guān)鍵點:這是一個概率性、非實時的清理過程。所以你設(shè)置了過期時間的 key 可能在這一輪掃描中,沒掃描到,會在以后的任意時間點(比如 10.2s、12s、甚至更久)被清理,取決于是否被抽中。
對于后臺任務(wù)執(zhí)行間隔配置為
# 就這一行配置,默認就是 10
hz 10
hz 10代表的含義為:Redis 每秒鐘會執(zhí)行 10 輪"后臺定時任務(wù)"循環(huán),用來完成以下工作:
- 掃描并清理已過期的 key(這就是我們用到的)。
- 關(guān)閉空閑超時的客戶端連接。
- 更新 serverCron 里的各類統(tǒng)計信息、觸發(fā) AOF 重寫/RDB 保存的閾值判斷等。
hz的值調(diào)的越高,服務(wù)負載越大
? 還有另外一種場景,當(dāng)服務(wù)器內(nèi)存使用超過健康閾值時,如何更好的調(diào)優(yōu)?
比如服務(wù)器內(nèi)存 16G,現(xiàn)在 Redis 用了 15.9G 了,在沒錢升級配置的情況下,如何保障能夠繼續(xù) set get 使用
淘汰策略
這里先說明一個參數(shù) # maxmemory <bytes> ,官方說明為
# Set a memory usage limit to the specified amount of bytes.
# When the memory limit is reached Redis will try to remove keys
# according to the eviction policy selected (see maxmemory-policy).
#
# If Redis can't remove keys according to the policy, or if the policy is
# set to 'noeviction', Redis will start to reply with errors to commands
# that would use more memory, like SET, LPUSH, and so on, and will continue
# to reply to read-only commands like GET.
意思就是,
設(shè)置內(nèi)存使用限制為指定的字節(jié)數(shù)。當(dāng)達到內(nèi)存限制時,Redis將嘗試根據(jù)選擇的清除策略,刪除鍵;
如果Redis無法根據(jù)策略移除鍵,或策略設(shè)置為 **noeviction**,則 Redis 將開始對會占用更多內(nèi)存的命令(如SET、LPUSH等)返回錯誤,但只讀命令如GET)依舊可以正常使用。
這個解釋很清晰了,該配置默認是注釋的,也就意味著 Redis 默認允許無限使用內(nèi)存?。。?/p>
在設(shè)置了該參數(shù)后,可以根據(jù)不同種規(guī)則來清除一些 redis 的 key,從而降低內(nèi)存
參數(shù)配置示例
# 限制 Redis 最大使用內(nèi)存為 4GB
maxmemory 4gb
# 或者使用字節(jié)
maxmemory 4294967296
換句話來說,當(dāng) Redis 設(shè)置了 maxmemory 參數(shù)后,可選擇下面任何一個方式,來優(yōu)化內(nèi)存,具體選擇哪種(這里面學(xué)問大的很,比方說熱點數(shù)據(jù)、不常用數(shù)據(jù)的取舍),看你自己
| 策略名稱 | 說明 | 備注 |
|---|---|---|
noeviction |
默認策略:內(nèi)存不足時,新寫入會報錯(OOM command not allowed) | 不淘汰,默認值 |
allkeys-lru |
從所有 key 中淘汰最近最少使用(Least Recently Used)的 key | LRU算法 |
volatile-lru |
從設(shè)置了過期時間的 key 中淘汰 LRU key | LRU算法 |
allkeys-lfu |
從所有 key 中淘汰最不經(jīng)常使用(Least Frequently Used)的 key | LFU算法 |
volatile-lfu |
從設(shè)置了過期時間的 key 中淘汰 LFU key | LFU算法 |
allkeys-random |
從所有 key 中隨機淘汰 | 隨機算法 |
volatile-random |
從設(shè)置了過期時間的 key 中隨機淘汰 | 隨機算法 |
volatile-ttl |
從設(shè)置了過期時間的 key 中淘汰剩余生存時間(TTL)最短的 key | TTL優(yōu)先 |
這里補充個題外話,LRU與LFU算法區(qū)別
? LRU 最近最少使用的被淘汰。
實現(xiàn)方式:內(nèi)部可能通過鏈表的特性,尾部淘汰吧,具體實現(xiàn)不清楚
? LFU 最不經(jīng)常使用的被淘汰。
實現(xiàn)方式:每個 key 有一個訪問頻率計數(shù)器(logc),具體實現(xiàn)不清楚
?? LFU示例:對于熱點數(shù)據(jù)維度的保護策略
一個 key 每天被訪問 100 次(長期熱點),LFU 會保留它。
一個 key 只被訪問一次,即使是"最近"訪問,LFU 也會優(yōu)先淘汰。
回顧
- 過期刪除方式中的
惰性刪除與定期刪除為兩種不同機制,組合起來構(gòu)成刪除機制 - 過期刪除機制 和 淘汰策略 兩種之間獨立運行,互補干涉
- 淘汰策略只有在設(shè)置
maxmemory參數(shù),且內(nèi)存達到該值時才生效 - 目前來說,不支持多種策略同時生效
- 技術(shù)不是一塵不變的,Redis 不同版本是有差異的