caffeine緩存核心原理介紹

1 前言

緩存可以說(shuō)是高性能系統(tǒng)的奇兵,在很多系統(tǒng)中都能看到緩存的身影。當(dāng)緩存資源緊張時(shí),我們總是期望未來(lái)還會(huì)用到的緩存項(xiàng)(cache entry)繼續(xù)保留在緩存中,而淘汰掉未來(lái)不會(huì)再使用的緩存項(xiàng)。根據(jù)緩存的局部性原理,越是最近訪問(wèn)過(guò)的緩存項(xiàng),未來(lái)更有可能再次被使用到;最近訪問(wèn)次數(shù)最多的緩存項(xiàng),未來(lái)更有可能再次被使用到。也就是說(shuō),理想的緩存項(xiàng)目淘汰機(jī)制主要需要考慮兩個(gè)約束:最近訪問(wèn)的時(shí)間和緩存項(xiàng)的訪問(wèn)頻率。當(dāng)前大部分使用的堆內(nèi)本地緩存,淘汰機(jī)制主要側(cè)重于其中一個(gè)約束,要么側(cè)重于最近訪問(wèn)時(shí)間(LRU),要么側(cè)重于最近訪問(wèn)頻率(LFU),而caffeine則綜合考慮了這兩個(gè)約束,是一個(gè)接近最優(yōu)(near optimal)的緩存。當(dāng)前來(lái)看,caffeine緩存可以說(shuō)是java堆內(nèi)本地緩存的王者,是本人了解到的比較理想的本地緩存方案,其設(shè)計(jì)思想有很多可以學(xué)習(xí)、借鑒的地方。

當(dāng)前主流緩存性能比較

2 緩存的主流淘汰機(jī)制

緩存的淘汰機(jī)制主要回答在什么時(shí)候淘汰哪些緩存項(xiàng)目的問(wèn)題,主流的淘汰機(jī)制有:

2.1 訪問(wèn)時(shí)間

只要緩存項(xiàng)最后訪問(wèn)時(shí)間超過(guò)一定時(shí)間值,就把緩存項(xiàng)從緩存中淘汰驅(qū)逐掉(evict),而不關(guān)心當(dāng)前緩存實(shí)際使用了多少空間,也不考慮被淘汰掉的緩存項(xiàng)未來(lái)被使用的可能性高低。訪問(wèn)可以是寫(xiě)(expireAfterWrite),也可以是讀寫(xiě)(expireAfterAccess)。對(duì)于expireAfterWrite,只要緩存項(xiàng)寫(xiě)入緩存后超過(guò)一定時(shí)間就淘汰掉;expireAfterAccess則是緩存項(xiàng)既沒(méi)有讀也沒(méi)有寫(xiě)(更新)一定時(shí)間后才淘汰掉。觸發(fā)淘汰判斷的時(shí)機(jī)可以是緩存項(xiàng)訪問(wèn)時(shí)(讀緩存項(xiàng)時(shí)判斷緩存項(xiàng)是否超時(shí)),也可以是定時(shí)任務(wù)周期掃描緩存以判斷是否有緩存項(xiàng)超時(shí)了,還可以是在緩存空間不夠時(shí),掃描緩存是否有緩存項(xiàng)超時(shí)了。
redis、caffeine等很多緩存都支持這種淘汰機(jī)制。

2.2 弱引用(weak reference)和軟引用(soft reference)

緩存中緩存項(xiàng)的key和value采用弱引用或軟引用,這樣如果緩存項(xiàng)在業(yè)務(wù)中沒(méi)有被使用了時(shí)(強(qiáng)引用),JVM在垃圾收集(GC)時(shí)自動(dòng)會(huì)回收緩存項(xiàng)對(duì)象,實(shí)現(xiàn)對(duì)緩存項(xiàng)的淘汰。guava cache和caffeine都支持這種淘汰機(jī)制。

2.3 緩存空間

系統(tǒng)一般會(huì)限制緩存的最大空間,當(dāng)緩存滿了,又有新的緩存項(xiàng)需要寫(xiě)入時(shí),需要有一個(gè)機(jī)制確定淘汰掉緩存中哪些緩存項(xiàng)

2.3.1 FIFO

設(shè)定FIFO的最大空間,先進(jìn)入FIFO(寫(xiě)入緩存)的緩存項(xiàng)最先被淘汰,而不考慮被淘汰的緩存項(xiàng)后來(lái)有沒(méi)有被訪問(wèn)過(guò)(讀或?qū)懀?。FIFO方式的實(shí)現(xiàn)非常簡(jiǎn)單,但是緩存命中率(hit rate)并不理想,通用緩存基本上不考慮。

2.3.2 LRU

LRU(Least Recent Use)其實(shí)是基于緩存項(xiàng)的最后訪問(wèn)時(shí)間(讀或?qū)懀?duì)緩存項(xiàng)進(jìn)行排序,訪問(wèn)時(shí)間最早(也就是離當(dāng)前時(shí)間最遠(yuǎn),緩存項(xiàng)最舊)的緩存項(xiàng)最先被淘汰,最后訪問(wèn)時(shí)間離當(dāng)前時(shí)間最近(least recent的含義)的緩存項(xiàng)被保留下來(lái)。LRU的優(yōu)點(diǎn)是,因?yàn)榘炎罱L問(wèn)過(guò)的緩存項(xiàng)保留了下來(lái),根據(jù)局部性原理,這些緩存項(xiàng)再次被使用的概率是比較高的,所以LRU的命中率還是不錯(cuò)的,同時(shí),LRU的實(shí)現(xiàn)也比較簡(jiǎn)單(雖然比FIFO方式復(fù)雜點(diǎn)),一個(gè)中高級(jí)的java開(kāi)發(fā)人員不用太大的代價(jià)就能實(shí)現(xiàn)一個(gè)基本功能的LRU緩存,所以LRU的使用頻率非常高。我們?cè)诖蟛糠窒到y(tǒng)中見(jiàn)到的緩存基本上都是LRU緩存。LRU可以非常好的處理少量稀疏流量(sparse burst,即短時(shí)間內(nèi)使用幾次后面就不被使用了),但是無(wú)法處理大量的稀疏流量,因?yàn)檫@時(shí)會(huì)淘汰掉使用頻率很高的緩存項(xiàng),而這些緩存后面還會(huì)經(jīng)常訪問(wèn)到。比如系統(tǒng)中的一些巡檢項(xiàng)目,將大量只使用一次的緩存項(xiàng)寫(xiě)入了緩存中,從而排擠掉了哪些真正被經(jīng)常使用的緩存項(xiàng)。

2.3.3 LFU

LFU(Least Frequecy Use)把訪問(wèn)頻率高的緩存項(xiàng)保留下來(lái),同時(shí)考慮時(shí)間因素,比如一個(gè)緩存項(xiàng)最近100ms內(nèi)使用了50次,另一個(gè)緩存項(xiàng)最近200ms內(nèi)也使用了50次,毫無(wú)疑問(wèn)應(yīng)該優(yōu)先保留最近100ms內(nèi)使用了50次的緩存項(xiàng)。LFU的優(yōu)點(diǎn)是把最近最經(jīng)常使用的緩存項(xiàng)保留了下來(lái),而這些緩存項(xiàng)在未來(lái)再次被使用的概率也非常高,非常符合系統(tǒng)的需要和目標(biāo)。LFU的缺點(diǎn)是:

  1. 需要為每一個(gè)緩存項(xiàng)維護(hù)相應(yīng)的頻率統(tǒng)計(jì)信息,每一次訪問(wèn)都需要更新相應(yīng)的統(tǒng)計(jì)信息,這需要一定的存儲(chǔ)和性能開(kāi)銷(xiāo)。同時(shí),維護(hù)的頻率信息如何體現(xiàn)最近(least)也是一個(gè)不那么容易處理的問(wèn)題。如果頻率計(jì)數(shù)器只維護(hù)最近指定一段時(shí)間內(nèi)的訪問(wèn)次數(shù),那么需要有相應(yīng)的邏輯清除掉這段時(shí)間之前的統(tǒng)計(jì)頻率;
  2. 無(wú)法處理稀疏流量(sparse burst)的場(chǎng)景。因?yàn)橄∈枇髁恐挥猩倭康脑L問(wèn)次數(shù),在比較訪問(wèn)頻率決定去留時(shí)處于劣勢(shì),可能導(dǎo)致稀疏流量緩存項(xiàng)頻繁被淘汰,訪問(wèn)稀疏流量經(jīng)常無(wú)法命中。

3 caffeine緩存核心原理

caffeine緩存采用了W-TinyLFU算法,該算法綜合了LRU和LFU的優(yōu)勢(shì),妥善改善了LRU和LFU的劣勢(shì)

3.1 Count-Min Sketch頻率統(tǒng)計(jì)算法

為了解決上面介紹的LFU的第一個(gè)缺點(diǎn),caffeine緩存的LFU頻率統(tǒng)計(jì)方法采用的是Count-Min Sketch算法。該算法借鑒了boomfilter的思想,只不過(guò)hash key對(duì)應(yīng)的value不是表示存在的true或false標(biāo)志,而是一個(gè)計(jì)數(shù)器。它采用了四個(gè)hash函數(shù),四個(gè)hash函數(shù)同時(shí)對(duì)緩存項(xiàng)key計(jì)算hash值,將hash值對(duì)應(yīng)的計(jì)數(shù)器加一。計(jì)數(shù)器只有4bit,所以計(jì)數(shù)器最大只能計(jì)數(shù)到15,超過(guò)15時(shí)則保持15不變,不再往上增加計(jì)數(shù)了。計(jì)數(shù)器最大只能計(jì)數(shù)到15,容量超乎預(yù)調(diào)的小,但是從實(shí)際測(cè)試來(lái)看,效果卻超乎預(yù)調(diào)的好。因?yàn)閎loomfilter存在positive false的問(wèn)題(其實(shí)就是存在hash沖突),緩存項(xiàng)的頻率值取四個(gè)計(jì)數(shù)器的最小值(這就是Count-Min的含義)。當(dāng)所有計(jì)數(shù)器值的和超過(guò)設(shè)定的閾值(默認(rèn)是緩存項(xiàng)最大數(shù)量的10倍)時(shí),所有的計(jì)數(shù)器值都減到原來(lái)的一半。

Count-Min Sketch算法詳細(xì)實(shí)現(xiàn)方案如下:


Count-Min Sketch原理

Count-Min Sketch維護(hù)了一個(gè)long[] table數(shù)組,數(shù)組的大小為緩存空間容量(緩存項(xiàng)最大數(shù)量)向上取整為2的n次方。Count-Min Sketch的計(jì)數(shù)器是4bit,table數(shù)組的每個(gè)元素大小是64bit,相當(dāng)于table元素包含了16個(gè)計(jì)數(shù)器,這16個(gè)計(jì)數(shù)器進(jìn)一步分為4個(gè)group,那么每個(gè)group包含4個(gè)計(jì)數(shù)器,正好等于bloom hash函數(shù)的個(gè)數(shù),同一個(gè)key的四個(gè)計(jì)數(shù)器分別使用group內(nèi)相應(yīng)位置的計(jì)數(shù)器。每個(gè)table元素包含16個(gè)計(jì)數(shù)器,4個(gè)hash計(jì)數(shù)器在相應(yīng)table元素內(nèi)計(jì)數(shù)器的偏移不一樣,可以有效降低hash沖突。

緩存項(xiàng)計(jì)數(shù)統(tǒng)計(jì)過(guò)程為:先計(jì)算緩存項(xiàng)key的hash值,然后使用4個(gè)不同的種子值分別計(jì)算得到table數(shù)組四個(gè)元素的下標(biāo)。然后根據(jù)hash值的低2bit確定table數(shù)組元素中的group,那么第一個(gè)計(jì)數(shù)器位置為第一個(gè)table數(shù)組元素相應(yīng)group中的第一個(gè)計(jì)數(shù)器,第二個(gè)計(jì)數(shù)器位置為第二個(gè)table數(shù)組元素相應(yīng)group中的第二個(gè)計(jì)數(shù)器,第三個(gè)計(jì)數(shù)器位置為第三個(gè)table數(shù)組元素相應(yīng)group中的第三個(gè)計(jì)數(shù)器,第四個(gè)計(jì)數(shù)器位置為第四個(gè)table數(shù)組元素相應(yīng)group中的第四個(gè)計(jì)數(shù)器。

從Count-Min Sketch頻率統(tǒng)計(jì)算法描述可知,由于計(jì)數(shù)器大小只有4bit,極大地降低了LFU頻率統(tǒng)計(jì)對(duì)存儲(chǔ)空間的要求。同時(shí),計(jì)數(shù)器統(tǒng)計(jì)上限是15,并在計(jì)數(shù)總和達(dá)到閾值時(shí)所有計(jì)數(shù)器值減半,相當(dāng)于引入了計(jì)數(shù)飽和和衰減機(jī)制,可以有效解決短時(shí)間內(nèi)突發(fā)大流量不能有效淘汰的問(wèn)題。比如出現(xiàn)了一個(gè)突發(fā)熱點(diǎn)事件,它的訪問(wèn)量是其他事件的成百上千倍,但是該熱點(diǎn)事件很快冷卻下去,傳統(tǒng)的LFU淘汰機(jī)制會(huì)讓該事件的緩存長(zhǎng)時(shí)間地保留在緩存中而無(wú)法淘汰掉,雖然該類(lèi)型事件已經(jīng)訪問(wèn)量非常小或無(wú)人問(wèn)津了。

3.2 W-TinyLFU算法

如上所述,caffeine緩存的LFU采用了Count-Min Sketch頻率統(tǒng)計(jì)算法,該LFU的計(jì)數(shù)器只有4bit大小,所以稱為T(mén)inyLFU。TinyLFU解決了上述LFU列出的第一個(gè)問(wèn)題,但是并沒(méi)有解決第二個(gè)問(wèn)題。在TinyLFU算法基礎(chǔ)上引入一個(gè)基于LRU的Window Cache,從而解決了上述LFU列出的第二個(gè)問(wèn)題,這個(gè)新的算法叫就叫做W-TinyLFU(Window-TinyLFU)。

W-TinyLFU算法的框架如下所示:

W-TinyLFU算法原理

W-TinyLFU將緩存存儲(chǔ)空間分為兩個(gè)大的區(qū)域:Window Cache和Main Cache,Window Cache是一個(gè)標(biāo)準(zhǔn)的LRU Cache,Main Cache則是一個(gè)SLRU(Segmemted LRU)cache,因?yàn)镸ain Cache進(jìn)一步劃分為Protected Cache(保護(hù)區(qū))和Probation Cache(觀察區(qū))兩個(gè)區(qū)域,這兩個(gè)區(qū)域都是基于LRU的Cache。protected是一個(gè)受保護(hù)的區(qū)域,該區(qū)域中的緩存項(xiàng)不會(huì)被淘汰。Probation區(qū)域則是一個(gè)觀察區(qū),當(dāng)有新的緩存項(xiàng)需要進(jìn)入Probation區(qū)時(shí),如果Probation區(qū)空間已滿,則會(huì)將新進(jìn)入的緩存項(xiàng)與Probation區(qū)中根據(jù)LRU規(guī)則需要被淘汰(evict)的緩存項(xiàng)進(jìn)行比較,比較失敗的緩存項(xiàng)會(huì)被淘汰,獲勝的緩存項(xiàng)會(huì)進(jìn)入或保留在Probation區(qū)。Window Cache默認(rèn)為cache總大小的1%,Main Cache默認(rèn)為cache總大小的99%。Protected Cache默認(rèn)為Main Cache大小的80%,Probation Cache默認(rèn)為Main Cache大小的20%。當(dāng)然這些cache區(qū)域的大小會(huì)動(dòng)態(tài)調(diào)整。

當(dāng)有新的緩存項(xiàng)寫(xiě)入緩存時(shí),會(huì)先寫(xiě)入Window Cache區(qū)域,當(dāng)Window Cache空間滿時(shí),最舊的緩存項(xiàng)會(huì)被移出Window Cache。如果Probation Cache未滿,從Window Cache移出的緩存項(xiàng)會(huì)直接寫(xiě)入Probation Cache;如果Probation Cache已滿,則會(huì)根據(jù)TinyLFU算法確定從Window Cache移出的緩存項(xiàng)是丟棄(淘汰)還是寫(xiě)入Probation Cache。Probation Cache中的緩存項(xiàng)如果訪問(wèn)頻率達(dá)到一定次數(shù),會(huì)提升到Protected Cache;如果Protected Cache也滿了,最舊的緩存項(xiàng)也會(huì)移出Protected Cache,然后根據(jù)TinyLFU算法確定是丟棄(淘汰)還是寫(xiě)入Probation Cache。

TinyLFU淘汰機(jī)制為:

從Window Cache或Protected Cache移出的緩存項(xiàng)稱為Candidate,Probation Cache中最舊的緩存項(xiàng)稱為Victim。如果Candidate緩存項(xiàng)的訪問(wèn)頻率大于Victim緩存項(xiàng)的訪問(wèn)頻率,則淘汰掉Victim。如果Candidate小于或等于Victim的頻率,那么如果Candidate的頻率小于5,則淘汰掉Candidate;否則,則在Candidate和Victim兩者之中隨機(jī)地淘汰一個(gè)。

從上面對(duì)W-TinyLFU的原理描述可知,caffeine綜合了LFU和LRU的優(yōu)勢(shì),將不同特性的緩存項(xiàng)存入不同的緩存區(qū)域,最近剛產(chǎn)生的緩存項(xiàng)進(jìn)入Window區(qū),不會(huì)被淘汰;訪問(wèn)頻率高的緩存項(xiàng)進(jìn)入Protected區(qū),也不會(huì)淘汰;介于這兩者之間的緩存項(xiàng)存在Probation區(qū),當(dāng)緩存空間滿了時(shí),Probation區(qū)的緩存項(xiàng)會(huì)根據(jù)訪問(wèn)頻率判斷是保留還是淘汰;通過(guò)這種機(jī)制,很好的平衡了訪問(wèn)頻率和訪問(wèn)時(shí)間新鮮程度兩個(gè)維度因素,盡量將新鮮的訪問(wèn)頻率高的緩存項(xiàng)保留在緩存中。同時(shí)在維護(hù)緩存項(xiàng)訪問(wèn)頻率時(shí),引入計(jì)數(shù)器飽和和衰減機(jī)制,即節(jié)省了存儲(chǔ)資源,也能較好的處理稀疏流量、短時(shí)超熱點(diǎn)流量等傳統(tǒng)LRU和LFU無(wú)法很好處理的場(chǎng)景。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容