Redis作為一個高性能的內存NoSQL資料庫,其容量受到最大內存限制的限制。
在實際生產環境中使用Redis時,偶然會覺得Redis的內存佔用要比自己預想的大。事實上,Redis佔用的內存除了保存鍵值對所需的開銷外,還有一些運行時產生的額外內存,包括:
過期Key所佔空間漸進式Rehash導致未及時刪除的空間Redis管理數據,包括底層數據結構開銷,客戶端信息,讀寫緩衝區等主從複製,bgsave時的額外開銷Redis的漸進式Rehash,在筆者介紹Concurrenthashmap擴容的時候,做了簡單的介紹,點擊查看詳解ConcurrentHashMap及JDK8的優化
Redis的主從複製,則在筆者的另一篇博客裡做了詳細的介紹,點擊查看Redis主從複製的原理
所以本文將主要圍繞過期Key的回收問題進行講解。
過期鍵的刪除策略
如果Redis的一個鍵是過期的,那它到了過期時間之後並不是馬上就從內存中被刪除,而是採用了三種不同的刪除策略:
立即刪除惰性刪除定時刪除其中第二種為被動刪除,第一種和第三種為主動刪除,而且第一種實時性更高。
1.立即刪除
立即刪除是指,在設置鍵的過期時間時,創建一個回調事件,當過期時間達到時,由時間處理器自動執行鍵的刪除操作。
立即刪除能保證內存中數據的最大新鮮度,因為它保證過期鍵值會在過期後馬上被刪除,其所佔用的內存也會隨之釋放。但是立即刪除對cpu是最不友好的。因為刪除操作會佔用cpu的時間,如果剛好碰上了cpu正在做排序等計算的時候,就會給cpu造成額外的壓力。
而且目前redis事件處理器對時間事件的處理方式--無序鍊表,查找一個key的時間複雜度為O(n),所以並不適合用來處理大量的時間事件。
2.惰性刪除
惰性刪除是指,某個鍵值過期後,此鍵值不會馬上被刪除,而是等到下次被使用的時候,才會被檢查到過期,此時才能得到刪除。所以惰性刪除的缺點很明顯:浪費內存。
舉個例子,對於一些按時間點來更新的數據,比如log日誌,過期後在很長的一段時間內可能都得不到訪問,這樣在這段時間內就要浪費這麼多內存來存log。
3.定時刪除
從上面分析來看,立即刪除會短時間內佔用大量cpu,惰性刪除會在一段時間內浪費內存,所以定時刪除是一個折中的辦法。
定時刪除是指:每隔一段時間執行一次刪除操作,並通過限制刪除操作執行的時長和頻率,來減少刪除操作對cpu的影響。另一方面定時刪除也有效的減少了因惰性刪除帶來的內存浪費。
過期Key清理算法
Redis過期Key清理的機制對清理的頻率和最大時間都有限制,在儘量不影響正常服務的情況下,進行過期Key的清理,以達到長時間服務的性能最優。
Redis會周期性的隨機測試一批設置了過期時間的key並進行處理。測試到的已過期的key將被刪除。具體的算法如下:
Redis配置項hz定義了serverCron任務的執行周期,默認為10,即CPU空閒時每秒執行10次;每次過期key清理的時間不超過CPU時間的25%,即若hz=1,則一次清理時間最大為250ms,若hz=10,則一次清理時間最大為25ms;清理時依次遍歷所有的db;從db中隨機取20個key,判斷是否過期,若過期,則逐出;若有5個以上key過期,則重複步驟4,否則遍歷下一個db;在清理過程中,若達到了25%CPU時間,退出清理過程;這是一個基於概率的簡單算法,基本的假設是抽出的樣本能夠代表整個key空間,redis持續清理過期的數據直至將要過期的key的百分比降到了25%以下。
由於算法採用的隨機取key判斷是否過期的方式,故幾乎不可能清理完所有的過期Key。
調高hz參數可以提升清理的頻率,過期key可以更及時的被刪除,但hz太高會增加CPU時間的消耗。
數據逐出策略
在redis中,允許用戶設置最大使用內存大小maxmemory(需要配合maxmemory-policy使用),設置為0表示不限制(默認配置)。
生產環境中需要設置此值,最好不超過內存60%-70%。
當redis內存數據集快到達maxmemory時,redis會實行數據淘汰策略。
Redis提供6種數據淘汰策略。在逐出算法中,根據用戶設置的逐出策略,選出待逐出的key,直到當前內存小於最大內存值為止。
可選逐出策略如下:
volatile-lru:從已設置過期時間的數據集中挑選最近最少使用的數據淘汰volatile-ttl:從已設置過期時間的數據集中挑選將要過期的數據淘汰volatile-random:從已設置過期時間的數據集中任意選擇數據 淘汰allkeys-lru:從數據集中挑選最近最少使用的數據淘汰allkeys-random:從數據集中任意選擇數據淘汰no-enviction(驅逐):禁止驅逐數據在redis2.8中默認策略是volatile-lru在redis3.2和redis4.0中默認策略是no-eviction如果使用no-eviction時,當內存不足,Redis會返回OOM的錯誤信息(error) OOM command not allowed when used memory > 'maxmemory'.
當cache中沒有符合清除條件的key時,回收策略 volatile-lru, volatile-random 和volatile-ttl 將會和策略 noeviction 一樣直接返回錯誤。
選擇正確的回收策略是很重要的,取決於你的應用程式的訪問模式。使用INFO命令輸出來監控緩存命中和錯過的次數,以調優Redis的配置。
通用規則如下:
如果期望用戶請求呈現冪律分布(power-law distribution),也就是,期望一部分子集元素被訪問得遠比其他元素多時,可以使用allkeys-lru策略。如果期望是循環周期的訪問,所有的鍵被連續掃描,或者期望請求符合平均分布(每個元素以相同的概率被訪問),可以使用allkeys-random策略。如果期望是讓redis使用緩存對象設置的TTL值,確定哪些對象應該是較好的清除候選項,可以使用volatile-ttl策略。