面試官:講講redis的過期策略如何實現(xiàn)?
時隔多日,小菜雞終于接到阿里的面試通知,屁顛屁顛的從上海趕到了杭州。
經(jīng)過半個小時的廝殺:
自我介紹
hashMap和ConcurrentHashMap區(qū)別
jdk中鎖的實現(xiàn)原理
volatile的使用場景
threadLocal怎么實現(xiàn)?什么時候會用到?
面試官終于把考察點轉(zhuǎn)到了redis上面,這是小菜雞特意準備過的。
面試官:我看你簡歷提到xxx項目使用了redis
小弱雞:嗯,因為xxxx的性能問題,經(jīng)過排查之后,發(fā)現(xiàn)性能瓶頸在數(shù)據(jù)庫上面,所以引入了redis
面試官:行,那你了解redis的過期策略嗎?
小弱雞:有了解過,因為redis是基于內(nèi)存來進行高性能、高并發(fā)的讀寫操作的,既然是內(nèi)存,那肯定有空間的限制,如果只有10g內(nèi)存,一直往里面寫數(shù)據(jù),那肯定不行,所以采用一些過期策略把不需要的數(shù)據(jù)刪除、或者是淘汰掉。
面試官:那都有哪些過期策略?
小弱雞:我了解的有 定期刪除、惰性刪除兩種
面試官:你先講講定期刪除怎么實現(xiàn)?
小弱雞好像有點興奮:所謂定期刪除,指的是 redis 默認是每隔 100ms 就隨機抽取一些設(shè)置了過期時間的 key,檢查其是否過期,如果過期就刪除。
面試官:為什么是隨機抽?。?/p>
小弱雞:假如在redis 里插入10w個key,并且都設(shè)置了過期時間,如果每次都檢查所有key,那cpu基本上都消耗在過期key的檢查上了,redis對外的性能也會大大降低,簡直就是一場災(zāi)難。
面試官:隨機檢查會存在什么問題?
小弱雞:可能導致本已經(jīng)過期的key沒有被掃描到,而繼續(xù)留在內(nèi)存中,并占用空間,等待被刪除。
面試官:這種情況怎么解決?
小弱雞又興奮了:這時候就需要第二種過期策略了,惰性刪除,就是在獲取某個 key 的時候,redis 會檢查一下 ,如果這個 key 設(shè)置了過期時間,并且已經(jīng)過期了,那么就直接刪除,返回空。
面試官面帶一絲笑意:嗯,那再考慮一種情況,如果大量的key沒有被掃描到,且已過期,也沒有被再次訪問,即沒有走惰性刪除,這些大量過期 key 堆積在內(nèi)存里,導致 redis 內(nèi)存塊耗盡了,這種情況下,怎么辦?
小菜雞想了會,抓了抓腦袋:redis內(nèi)部提供了內(nèi)存淘汰機制,應(yīng)該有好幾種策略,但我只知道LRU算法。
面試官:嗯,那你手寫一個LRU算法?
小菜雞*花一緊,這不是給自己挖坑么?。?!如果從頭開始寫一個完整的LRU算法,那會要了命,幸好小菜雞還記得 LinkedHashMap,可以基于 LinkedHashMap實現(xiàn)一個簡單版本的LRU算法。
class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int CACHE_SIZE;
* @param cacheSize 緩存大小
*/
// true 表示讓 linkedHashMap 按照訪問順序來進行排序,最近訪問的放在頭部,最老訪問的放在尾部。
public LRUCache(int cacheSize) {
super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
CACHE_SIZE = cacheSize;
}
@Override
// 當 map中的數(shù)據(jù)量大于指定的緩存?zhèn)€數(shù)的時候,就自動刪除最老的數(shù)據(jù)。
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > CACHE_SIZE;
}
}
小菜雞寫完之后,輕輕舒了一口氣。
面試官看完點點頭,換了個方向繼續(xù)虐!
