Memcached創(chuàng)建者Dormando很早就寫過兩篇文章,告誡開發(fā)人員不要用memcached存儲(chǔ)Session。他在第一篇文章中給出的理由大致是說,如果用memcached存儲(chǔ)Session,那么當(dāng)memcached集群發(fā)生故障(比如內(nèi)存溢出)或者維護(hù)(比如升級(jí)、增加或減少服務(wù)器)時(shí),用戶會(huì)無法登錄,或者被踢掉線。而在第二篇文章中,他則指出,memcached的回收機(jī)制可能會(huì)導(dǎo)致用戶無緣無故地掉線。
Titas Norkūnas是DevOps咨詢服務(wù)提供商Bear Mountain的聯(lián)合創(chuàng)始人。由于看到Ruby/Rails社區(qū)忽略了Dormando那兩篇文章所指出的問題,所以他近日撰文對(duì)此進(jìn)行了進(jìn)一步的闡述。他認(rèn)為問題的根本在于,memcached是一個(gè)設(shè)計(jì)用于緩存數(shù)據(jù)而不是存儲(chǔ)數(shù)據(jù)的系統(tǒng),因此不應(yīng)該用于存儲(chǔ)Session。
對(duì)于Dormando的那兩篇文章,他認(rèn)為第一篇文章給出的原因很容易理解,而人們經(jīng)常會(huì)對(duì)第二篇文章給出的原因認(rèn)識(shí)不足。因此他對(duì)這個(gè)原因進(jìn)行了詳細(xì)地闡述:
Memcached使用“最近最少使用(LRU)”算法回收緩存。但memcached的LRU算法針對(duì)每個(gè)slab類執(zhí)行,而不是針對(duì)整體。
這意味著,如果所有Session的大小大致相同,那么它們會(huì)分成兩三個(gè)slab類。所有其它大小大致相同的數(shù)據(jù)也會(huì)放入同一些slab,與Session爭用存儲(chǔ)空間。一旦slab滿了,即使更大的slab中還有空間,數(shù)據(jù)也會(huì)被回收,而不是放入更大的slab中……在特定的slab中,Session最老的用戶將會(huì)掉線。用戶將會(huì)開始隨機(jī)掉線,而最糟糕的是,你很可能甚至都不會(huì)注意到它,直至用戶開始抱怨……
另外,Norkūnas提到,如果Session中增加了新數(shù)據(jù),那么Session變大也可能會(huì)導(dǎo)致掉線問題出現(xiàn)。
有人提出將Session和其它數(shù)據(jù)分別使用單獨(dú)的memcached緩存。不過,由于memcached的LRU算法是局部的,那種方式不僅導(dǎo)致內(nèi)存使用率不高,而且也無法消除用戶因?yàn)镾ession回收而出現(xiàn)隨機(jī)掉線的風(fēng)險(xiǎn)。
如果讀者非常希望借助memcached提高Session讀取速度,那么可以借鑒Norkūnas提出的memcached+RDBMS(在有些情況下,NoSQL也可以)的模式:
當(dāng)用戶登錄時(shí),將Session“set”到memcached,并寫入數(shù)據(jù)庫;
在Session中增加一個(gè)字段,標(biāo)識(shí)Session最后寫入數(shù)據(jù)庫的時(shí)間;
每個(gè)頁面加載的時(shí)候,優(yōu)先從memcached讀取Session,其次從數(shù)據(jù)庫讀??;
每加載N頁或者Y分鐘后,再次將Session寫入數(shù)據(jù)庫;
從數(shù)據(jù)庫中獲取過期Session,優(yōu)先從memcached中獲取最新數(shù)據(jù)。