引子
Memcached采用LRU(Least Recent Used)淘汰算法,在內(nèi)存容量滿(mǎn)時(shí)踢出過(guò)期失效和LRU數(shù)據(jù),為新數(shù)據(jù)騰出內(nèi)存空間。不過(guò)該淘汰算法在內(nèi)存空間不足以分配新的Slab情況下,這時(shí)只會(huì)在同一類(lèi)Slab內(nèi)部踢出數(shù)據(jù)。即當(dāng)某個(gè)Slab容量滿(mǎn),且不能在內(nèi)存足夠分配新的Slab,只會(huì)在相同Slab內(nèi)部踢出數(shù)據(jù),而不會(huì)挪用或者踢出其他Slab的數(shù)據(jù)。這種局部剔除數(shù)據(jù)的淘汰算法帶來(lái)一個(gè)問(wèn)題:Slab鈣化。
實(shí)踐
1 搭建一個(gè)64M、growth factor=1.25的MC節(jié)點(diǎn)。
2 用item數(shù)據(jù)寫(xiě)滿(mǎn)MC 192B的chunk,因?yàn)橐呀?jīng)有evicted數(shù)量,所以192B chunk肯定已經(jīng)寫(xiě)滿(mǎn),如圖1所示。

3 計(jì)算內(nèi)存利用率:bytes(59066176)/limit_maxbytes(67108864)=88%,已經(jīng)達(dá)到growth factor=1.25的期望內(nèi)存利用率,MC期望內(nèi)存利用率計(jì)算方法請(qǐng)參考拙作《期望內(nèi)存利用率計(jì)算方法》,所以?xún)?nèi)存已滿(mǎn),如圖2所示。

4 flush_all刪除所有數(shù)據(jù),從圖3看item仍占用192B的chunk size,MC刪除機(jī)制是數(shù)據(jù)不會(huì)真正從內(nèi)存中消失,只要被其他數(shù)據(jù)覆蓋,MC不會(huì)主動(dòng)刪除Slab chunk已存在的數(shù)據(jù)。

5 再用5萬(wàn)個(gè)96B的item寫(xiě)入MC,一共寫(xiě)50000*96B/1024/1024=4M數(shù)據(jù),遠(yuǎn)遠(yuǎn)小于64M,但是只能寫(xiě)入96B*10922=1MB,即只能寫(xiě)一個(gè)Page,還是有很多96B的item被Evicted,如圖4所示。
即使192B的chunk數(shù)據(jù)已經(jīng)被清除,MC淘汰策略是淘汰相同的Slab class數(shù)據(jù),96B的item也不會(huì)重新使用192B的chunk size,只會(huì)使用原有啟動(dòng)Memcached時(shí)分配的1MB Slab class(Chunk size 96B),這就是所謂的Slab鈣化問(wèn)題。

Slab鈣化可以解釋這個(gè)問(wèn)題:為什么我寫(xiě)入比較新的數(shù)據(jù),但被淘汰了?
假設(shè)Slab有各種規(guī)格(64~ 1M字節(jié)),比如應(yīng)用存入的大部分?jǐn)?shù)據(jù)大小在 64 ~ 128 字節(jié)范圍內(nèi),那么這些數(shù)據(jù)會(huì)存儲(chǔ)在128個(gè)字節(jié)大小的Slab chunk中,這些Slab chunk以鏈表的方式連接在一起。當(dāng)已經(jīng)沒(méi)有空余的內(nèi)存分配新的Slab,如果這時(shí)候?qū)懭?0K新數(shù)據(jù),且之前并沒(méi)有這么大的數(shù)據(jù)寫(xiě)入時(shí),那么這條新數(shù)據(jù)可以寫(xiě)入成功。但是當(dāng)下次再寫(xiě)入10K數(shù)據(jù)時(shí),第一次寫(xiě)入的10K數(shù)據(jù)就會(huì)被逐出。當(dāng)下一次寫(xiě)入的新數(shù)據(jù)在64 ~ 128字節(jié)時(shí),128字節(jié)大小的Slab鏈表上的數(shù)據(jù)會(huì)以L(fǎng)RU方式淘汰,所以L(fǎng)RU只會(huì)淘汰同一級(jí)別的Slab數(shù)據(jù)。
Slab鈣化降低內(nèi)存使用率,如果發(fā)生Slab鈣化,有三種解決方案:
1) 重啟Memcached實(shí)例,簡(jiǎn)單粗暴,啟動(dòng)后重新分配Slab class,但是如果是單點(diǎn)可能造成大量請(qǐng)求訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),出現(xiàn)雪崩現(xiàn)象,沖跨數(shù)據(jù)庫(kù)。
2) 隨機(jī)過(guò)期:過(guò)期淘汰策略也支持淘汰其他slab class的數(shù)據(jù),twitter工程師采用隨機(jī)選擇一個(gè)Slab,釋放該Slab的所有緩存數(shù)據(jù),然后重新建立一個(gè)合適的Slab。
3) 通過(guò)slab_reassign、slab_authmove參數(shù)控制。