面試官心理分析
如果你連這個問題都不知道,上來就懵了,回答不出來,那線上你寫代碼的時候,想當然的認為寫進redis的數據就一定會存在,后面導致系統各種 bug,誰來負責?
常見的問題:
往redis寫入的數據怎么沒了?
可能有同學會遇到,在生產環境的redis經常會丟掉一些數據,寫進去了,過一會兒可能就沒了。我的天,同學,你問這個問題就說明 redis 你就沒用對啊。redis 是緩存,你給當存儲了是吧?
啥叫緩存?用內存當緩存。內存是無限的嗎,內存是很寶貴而且是有限的,磁盤是廉價而且是大量的。可能一臺機器就幾十個G的內存,但是可以有幾個 T 的硬盤空間。redis 主要是基于內存來進行高性能、高并發的讀寫操作的。
那既然內存是有限的,比如redis就只能用 10G,你要是往里面寫了 20G 的數據,會咋辦?當然會干掉10G 的數據,然后就保留 10G 的數據了。那干掉哪些數據?保留哪些數據?當然是干掉不常用的數據,保留常用的數據了。數據明明過期了,怎么還占用著內存?這是由redis的過期策略來決定。
面試題剖析
redis 過期策略?
redis過期策略是:定期刪除+惰性刪除。
所謂定期刪除,指的是redis默認是每隔 100ms 就隨機抽取一些設置了過期時間的 key,檢查其是否過期,如果過期就刪除。
假設redis里放了 10w 個 key,都設置了過期時間,你每隔幾百毫秒,就檢查 10w 個 key,那 redis 基本上就死了,cpu 負載會很高的,消耗在你的檢查過期 key 上了。注意,這里可不是每隔 100ms 就遍歷所有的設置過期時間的 key,那樣就是一場性能上的災難。實際上 redis 是每隔 100ms 隨機抽取一些key 來檢查和刪除的。
但是問題是,定期刪除可能會導致很多過期key到了時間并沒有被刪除掉,那咋整呢?所以就是惰性刪除了。這就是說,在你獲取某個 key 的時候,redis 會檢查一下 ,這個 key 如果設置了過期時間那么是否過期了?如果過期了此時就會刪除,不會給你返回任何東西。
獲取key的時候,如果此時 key 已經過期,就刪除,不會返回任何東西。
但是實際上這還是有問題的,如果定期刪除漏掉了很多過期key,然后你也沒及時去查,也就沒走惰性刪除,此時會怎么樣?如果大量過期 key 堆積在內存里,導致 redis 內存快耗盡了,咋整?
答案是:走內存淘汰機制。
內存淘汰機制
redis內存淘汰機制有以下幾個:
· noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用吧,實在是太惡心了。
· allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的 key(這個是最常用的)。
· allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個 key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的key給干掉啊。
· volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key(這個一般不太合適)。
· volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key。
· volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除。
?手寫一個LRU 算法
你可以現場手寫最原始的LRU算法,那個代碼量太大了,似乎不太現實。
不求自己純手工從底層開始打造出自己的LRU,但是起碼要知道如何利用已有的 JDK 數據結構實現一個Java版的 LRU。
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int CACHE_SIZE;
/** * 傳遞進來最多能緩存多少數據 * * @param cacheSize 緩存大小 */
public LRUCache(int cacheSize) {
// true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放
在尾部。
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
// 當 map 中的數據量大于指定的緩存個數的時候,就自動刪除最老的數據。
return size() > CACHE_SIZE;
}
}