微信公衆號:IT一刻鐘。 大型現實非嚴肅主義現場, 一刻鐘與你分享優質技術架構與見聞,作一個有劇情的程序員。 關注可第一時間瞭解更多精彩內容,按期有福利相送喲。mysql
因爲緩存的讀取速度比非緩存要快上不少,因此在高性能場景下,系統在讀取數據時,是首先從緩存中查找須要的數據,若是找到了則直接讀取結果,若是找不到的話,則從內存或者硬盤中查找,再將查找到的結果存入緩存,以備下次使用。程序員
實際上,對於一個系統來講,緩存的空間是有限且寶貴的,咱們不可能將全部的數據都放入緩存中進行操做,即使能夠數據安全性也得不到保證,並且,若是緩存的數據量過大大,其速度也會變得愈來愈慢。 這個時候就須要考慮緩存的淘汰機制,可是淘汰哪些數據,又保留哪些數據,這是一個問題。若是處理不得當,就會形成「緩存污染」問題。算法
而緩存污染,是指系統將不經常使用的數據從內存移到緩存,形成經常使用數據的擠出,下降了緩存效率的現象。sql
LFU,英文名Least Frequently Used,字面意思就是最不常用的淘汰掉算法,是經過數據被訪問的頻率來判斷一個數據的熱點狀況。其核心理念是「歷史上這個數據被訪問次數越多,那麼未來其被訪問的次數也多」。緩存
LFU中每一個數據塊都有一個引用計數器,全部數據塊按照引用數從大到小的排序。安全
步驟:微信
分析:因爲是根據頻數進行熱點判斷和淘汰,因此先天具有避免偶發性、週期性批量操做致使臨時非熱點數據大量涌入緩存,擠出熱點數據的問題。 雖然具有這種先天優點,但依舊存在另外一種緩存污染問題,即歷史熱點數據污染當前熱點數據,若是系統訪問模式發生了改變,新的熱點數據須要計數累加超過舊熱點數據,才能將舊熱點數據進行淘汰,形成熱點效應滯後的問題。數據結構
複雜度與代價:每次操做都須要進行計數和排序,而且須要維護每一個數據塊計數狀況,會佔用較高的內存與cpu。架構
一個小思考,根據LFU算法,如何以O(1)時間複雜度實現get和put操做緩存?性能
LFU-Aging是基於LFU的改進算法,目的是解決歷史熱點數據對當前熱點數據的污染問題。有些數據在開始時使用次數不少,但之後就再也不使用,這類數據將會長時間留在緩存中,因此「除了訪問次數外,還要考慮訪問時間」,這也是LFU-Aging的核心理念。
雖然算法將時間歸入了考量範圍,但LFU-Aging並非直接記錄數據的訪問時間,而是增長了一個最大平均引用計數的閾值,而後經過當前平均引用計數來標識時間,換句話說,就是將當前緩存中的平均引用計數值看成當前的生命年代,當這個生命年代超過了預設的閾值,就會將當前全部計數值減半,造成指數衰變的生命年代。
分析:優勢是當訪問模式發生改變的時候,生命年代的指數衰變會使LFU-Aging可以更快的適用新的數據訪問模式,淘汰舊的熱點數據。
複雜度與代價:在LFU的基礎上又增長平均引用次數判斷和統計處理,對cpu的消耗更高,而且當平均引用次數超過指定閾值(Aging)後,還須要遍歷每個數據塊的引用計數,進行指數衰變。
Window-LFU顧名思義叫作窗口期LFU,區別於原義LFU中記錄全部數據的訪問歷史,Window-LFU只記錄過去一段時間內(窗口期)的訪問歷史,至關於給緩存設置了有效期限,過時數據再也不緩存。當須要淘汰時,將這個窗口期內的數據按照LFU算法進行淘汰。
分析:因爲是維護一段窗口期的記錄,數據量會比較少,因此內存佔用和cpu消耗都比LFU要低。而且這段窗口期至關於給緩存設置了有效期,可以更快的適應新的訪問模式的變化,緩存污染問題基本不嚴重。
複雜度與代價:維護一段時期內的數據訪問記錄,並對其排序。
LRU算法,英文名Least Recently Used,意思是最近最少使用的淘汰算法,根據數據的歷史訪問記錄來進行淘汰數據,核心思想是「若是數據最近被訪問過1次,那麼未來被訪問的機率會更高」,相似於就近優先原則。
步驟:
分析:偶發性的、週期性的批量操做會使臨時數據涌入緩存,擠出熱點數據,致使LRU熱點命中率急劇降低,緩存污染狀況比較嚴重。
複雜度與代價:數據結構複雜度較低;每次須要遍歷鏈表,找到命中的數據塊,而後將數據移到頭部。
LRU-K是基於LRU算法的優化版,其中K表明最近訪問的次數,從某種意義上,LRU能夠看做是LRU-1算法,引入K的意義是爲了解決上面所提到的緩存污染問題。其核心理念是從「數據最近被訪問過1次」蛻變成「數據最近被訪問過K次,那麼未來被訪問的機率會更高」。
LRU-K與LRU區別是,LRU-K多了一個數據訪問歷史記錄隊列(須要注意的是,訪問歷史記錄隊列並非緩存隊列,因此是不保存數據自己的,只是保存對數據的訪問記錄,數據此時依舊在原始存儲中),隊列中維護着數據被訪問的次數以及時間戳,只有當這個數據被訪問的次數大於等於K值時,纔會從歷史記錄隊列中刪除,而後把數據加入到緩存隊列中去。
步驟:
分析:LRU-K下降了「緩存污染」帶來的問題,命中率比LRU要高。實際應用中LRU-2是綜合各類因素後最優的選擇,LRU-3或者更大的K值命中率會高,但適應性差,一旦訪問模式發生變化,須要大量的新數據訪問才能將歷史熱點訪問記錄清除掉。
複雜度與代價:LRU-K隊列是一個優先級隊列。因爲LRU-K須要記錄那些被訪問過,但尚未放入緩存的對象,致使內存消耗會不少。
URL-Two queues算法相似於LRU-2,不一樣點在於URL-Two queues將LRU-2算法中的訪問歷史隊列(注意這不是緩存數據的)改成一個FIFO緩存隊列,即:URL-Two queues算法有兩個緩存隊列,一個是FIFO隊列(First in First out,先進先出),一個是LRU隊列。
當數據第一次訪問時,URL-Two queues算法將數據緩存在FIFO隊列裏面,當數據第二次被訪問時,則將數據從FIFO隊列移到LRU隊列裏面,兩個隊列各自按照本身的方法淘汰數據。
步驟:
分析:URL-Two queues算法和LRU-2算法命中率相似,可是URL-Two queues會減小一次從原始存儲讀取或計算數據的操做。命中率要高於LRU。
複雜度與代價:須要維護兩個隊列,代價是FIFO和LRU代價之和。
emmmm...
這個名字實際上是我取的,大概是這種算法尚未被命名?固然,這是一個玩笑話。
我是在mysql底層實現裏發現這個算法的,mysql在處理緩存淘汰時是用的這個方法,有點像URL-Two queues的變體,只是咱們只須要維護一個隊列,而後將隊列按照5:3的比例進行分割,5的那部分叫作young區,3的那部分叫作old區。具體是怎麼樣的請先看我把圖畫出來:
步驟:
分析:五三LRU算法算做是URL-Two queues算法的變種,原理實際上是同樣的,只是把兩個隊列合二爲一個隊列進行數據的處理,因此命中率和URL-Two queues算法同樣。
複雜度與代價:維護一個隊列,代價較低,可是內存佔用率和URL-Two queues同樣。
Multi Queue算法根據訪問頻率將數據劃分爲多個隊列,不一樣的隊列具備不一樣的訪問優先級,其核心思想是「優先緩存訪問次數多的數據」。
Multi Queue算法將緩存劃分爲多個LRU隊列,每一個隊列對應不一樣的訪問優先級。訪問優先級是根據訪問次數計算出來的,例如: Q0,Q1....Qn表明不一樣的優先級隊列,Q-history表明從緩存中淘汰數據,但記錄了數據的索引和引用次數。
步驟:
分析:Multi Queue下降了「緩存污染」帶來的問題,命中率比LRU要高。
複雜度與代價:Multi Queue須要維護多個隊列,且須要維護每一個數據的訪問時間,複雜度比LRU高。Multi Queue須要記錄每一個數據的訪問時間,須要定時掃描全部隊列,代價比LRU要高。雖然Multi Queue的隊列看起來數量比較多,但因爲全部隊列之和受限於緩存容量的大小,所以這裏多個隊列長度之和和一個LRU隊列是同樣的,所以隊列掃描性能也相近。
還有哪些優秀的緩存淘汰算法,或者你有更好的想法或問題,歡迎留言給我!
喜歡就點一下「當心心」唄~