Redis的過時策略都有哪些?內存淘汰機制都有哪些?手寫一下LRU代碼實現?java
生產環境的redis怎麼常常會丟掉一些數據?寫進去了,過一下子可能就沒了。git
問這個問題就說明redis你就沒用對啊。redis是緩存,你給當存儲了是吧?github
啥叫緩存?用內存當緩存。內存是無限的嗎,內存是很寶貴並且是有限的,而磁盤是廉價且大量的!
可能一臺機器就幾十個G的內存,可是能夠有幾個T的硬盤空間。
redis主要是基於內存來進行高性能、高併發的讀寫操做的。面試
那既然內存是有限的,好比redis就只能用10個G,你要是往裏面寫了20個G的數據,會咋辦?固然會幹掉10個G的數據,而後就保留10個G的數據了。那幹掉哪些數據?保留哪些數據?固然是幹掉不經常使用的數據,保留經常使用的數據了。redis
因此說,這是緩存的一個最基本的概念,數據是會過時的,要麼是你本身設置個過時時間,要麼是redis本身給幹掉。算法
set key value 過時時間(1小時)
set進去的key,1小時以後就沒了,就失效了segmentfault
還有一種就是若是你設置好了一個過時時間,你知道redis是怎麼給你弄成過時的嗎?何時刪除掉?若是你不知道,以前有我的就問了,爲啥好多數據明明應該過時了,結果發現redis內存佔用仍是很高?那是由於你不知道redis是怎麼刪除那些過時key的。緩存
redis 內存一共是10g,你如今往裏面寫了5g的數據,結果這些數據明明你都設置了過時時間,要求這些數據1小時以後都會過時,結果1小時以後,你回來一看,redis機器,怎麼內存佔用仍是50%呢?5g數據過時了,我從redis裏查,是查不到了,結果過時的數據還佔用着redis的內存。數據結構
若是你連這個問題都不知道,上來就懵了,回答不出來,那線上你寫代碼的時候,想固然的認爲寫進redis的數據就必定會存在,後面致使系統各類漏洞和bug,誰來負責?併發
咱們set key的時候,均可以給一個expire time
,就是過時時間,指定這個key好比說只能存活1個小時?10分鐘?這個頗有用,咱們本身能夠指定緩存到期就失效。
若是假設你設置一個一批key只能存活1個小時,那麼接下來1小時後,redis是怎麼對這批key進行刪除的?
答案是:按期刪除+惰性刪除
指的是redis默認是每隔100ms就隨機抽取一些設置了過時時間的key,檢查其是否過時,若是過時就刪除。
假設redis裏放了10萬個key,都設置了過時時間,你每隔幾百毫秒,就檢查10萬個key,那redis基本上就死了,cpu負載會很高的,消耗在你的檢查過時key上了。
注意,這裏可不是每隔100ms就遍歷全部的設置過時時間的key,那樣就是一場性能上的災難。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的。
可是問題是,按期刪除可能會致使不少過時key到了時間並無被刪除掉,那咋整呢?
因此就是
這就是說,在你獲取某個key的時候,redis會檢查一下 ,這個key若是設置了過時時間那麼是否過時了?若是過時了此時就會刪除,不會給你返回任何東西。
並非key到時間就被刪除掉,而是你查詢這個key的時候,redis再懶惰的檢查一下
經過上述兩種手段結合起來,保證過時的key必定會被幹掉。
很簡單,就是說,你的過時key,靠按期刪除沒有被刪除掉,還停留在內存裏,佔用着你的內存呢,除非你的系統去查一下那個key,纔會被redis給刪除掉。
但實際上這仍是有問題的,若是按期刪除漏掉了不少過時key,而後你也沒及時去查,也就沒走惰性刪除,此時會怎麼樣?大量過時key堆積在內存裏,致使redis內存塊耗盡了,咋整?
答案是:走內存淘汰機制。
若是redis的內存佔用過多的時候,此時會進行內存淘汰,有以下一些策略:
redis 10個key,如今已經滿了,redis須要刪除掉5個key
1個key,最近1分鐘被查詢了100次
1個key,最近10分鐘被查詢了50次
1個key,最近1個小時倍查詢了1次
確實有時會問這個,由於有些候選人若是確實過五關斬六將,前面的問題都答的很好,那麼其實讓他寫一下LRU算法,能夠考察一下編碼功底
你能夠現場手寫最原始的LRU算法,那個代碼量太大了,不太現實
public class LRUCache<K, V> extends LinkedHashMap<K, V> { private final int CACHE_SIZE; // 這裏就是傳遞進來最多能緩存多少數據 public LRUCache(int cacheSize) { super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); // 這塊就是設置一個hashmap的初始大小,同時最後一個true指的是讓linkedhashmap按照訪問順序來進行排序,最近訪問的放在頭,最老訪問的就在尾 CACHE_SIZE = cacheSize; } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > CACHE_SIZE; // 這個意思就是說當map中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據 } }
最起碼你也得寫出來上面那種代碼,不求本身純手工從底層開始打造出本身的LRU,可是起碼知道如何利用已有的jdk數據結構實現一個java版的LRU
《Java工程師面試突擊第1季-中華石杉老師》
本文由博客一文多發平臺 OpenWrite 發佈!