引子
web頁面在服務器端進行渲染,需要根據模板和數(shù)據庫數(shù)據經過運算生成和渲染出最終的頁面,這些運算和渲染過程會耗費大量時間和資源,如果將這些運算結果緩存起來放在內存中,下次再請求頁面就會直接從緩存中直接讀取數(shù)據,節(jié)省了頁面渲染計算時間,從而大大提高了頁面的相應速度。目前內存價格相對便宜,通過緩存帶來性能方面的收益往往比單純從代碼層面優(yōu)化性能,高效的和經濟實惠的多。
什么是緩存
什么是緩存,緩存其實也是一種代理,作為網站內容展示的代理,用戶直接訪問緩存內容,而緩存內容來源于后端程序計算, 緩存實際為一個中間代理層。
緩存存在基礎和場景
緩存的存在基礎是內容的相對不變性,在相對的時間、相對的條件內容不發(fā)生變化,這樣不用每次都動態(tài)計算生成內容。內容變化的頻率相對不高,應用對內容實時性要求相對不高,是引入緩存的場景基礎。讀的操作遠大于寫是實際的表現(xiàn)。緩存不適合高交互性,對內容實時性要求高的場景。
緩存主要是解決變與不變的問題,變什么時候變的問題,也就是緩存的同步問題。
緩存策略
緩存策略就是緩存如何生成和如何過期的規(guī)則定義。
緩存策略定義和業(yè)務場景有很大關系如內容展示類網站(新聞類)就會更適合緩存的使用場景。緩存也是種藝術,不同的場景會適合不同的緩存策略,有時候需要拿將幾種策略在實際應用中進行對比,通過實際觀測和相關數(shù)據統(tǒng)計(緩存命中率 緩存停留時間等)來決定哪種策略更適合。
下邊就具體介紹下集中常用的緩存策略
基于時間的緩存策略
通過設置cache過期時間來同步緩存內容,預定的時間到了緩存就會自動過期,優(yōu)點是簡單,缺點是會有一定的時間數(shù)據不同步。
基于cache key的緩存策略
對于動態(tài)網站,網站頁面是基于后端數(shù)據源結合預定義模板動態(tài)渲染生成的,后端數(shù)據源一般為數(shù)據庫(關系型數(shù)據庫 非關系型數(shù)據庫),后端的數(shù)據變化帶來頁面內容的的變化,當后端數(shù)據發(fā)生變化時就會生成新的cache_key從而生成新的cache內容,老的cahe_key和對應的內容會駐留在緩存服務中,只有當緩存服務的空間滿了才會觸發(fā)緩存服務的驅逐操作,緩存才會被清除。
基于cache key的緩存策略的關鍵點就是如何檢測到數(shù)據的變化,如何構建cache key。頁面的數(shù)據源是基于數(shù)據庫的,可以利用數(shù)據庫的id和update time時間戳(id為數(shù)據唯一標識 update time時間戳為變化標識)組成cache key,數(shù)據的任何變化都會改變update time這個時間戳字段,從而生成新的key值,并根據key值重新緩存內容。像memcache和redis等緩存服務會有自己的驅逐策略,會按照最近最少使用的策略自動驅逐緩存,緩存驅逐并不是當緩存空間滿了以后才執(zhí)行的,以memcache為例,他會根據緩存內容的大小分成不同的slab,當前slab空間滿的時候就會觸發(fā)驅逐,這就是為什么你會發(fā)現(xiàn)緩存還有大量空間的時候就有cache被驅逐。
對于文檔行數(shù)據庫如elasticsearch等,提供了一個版本字段,當文檔數(shù)據有任何修改,版本字段都會進行改變(版本號遞增),可以利用文檔的id和版本字段來組成cache key
基于cache key的緩存策略是每次對象變化就重新寫入緩存,舊的緩存對象時在slab滿的情況下由緩存服務根據最近最少訪問策略來驅逐舊的緩存,這種策略很簡單將緩存的清理工作交給了緩存服務,缺點就是有場景會將一些正在使用的cache給驅逐了,反而當前緩存的舊版本的對象會長期滯留在緩存服務中從而引發(fā)緩存震蕩(同一cache頻繁換入換出)。
網頁模板會隨著開發(fā)工作的進行而變動,當模板變化時需要重新生成cache。cache key的值的一部分是基于模板內容的degist md5值來計算的,只有當模板內容改變了,才會生成新的key和緩存內容。計算模板內容degist值也是需要耗費時間的,也可以將其緩存起來,將模板的名稱和路徑當做key來把degist值緩存起。在實際操作過程中,程序重新啟動時(新的relase發(fā)生)會檢測一次模板內容是否改變來決定是否生成新的模板內容degist。
實際應用中會結合數(shù)據庫數(shù)據值和模板digest值來生成cache key和cache content。
可回收的cache key緩存策略
簡單理解就是根據cache key在緩存服務中存儲對象,如果對象被修改就用新版本的緩存對象替換舊版本的對象,從而做到基于當前key的緩存對象空間循環(huán)利用。
他的核心就是基于被緩存對象定義的cache_key和cache_version,
名詞解釋:
cache_key: 為被緩存對象的唯一標識,如數(shù)據庫對象的id
cache_vertion:保存對象的版本信息,追蹤對象的變化,如關系數(shù)據庫對象的update_at 時間戳,文檔型數(shù)據庫的vertion字段。
被緩存的類:需要被緩存的對象。
緩存類:對于緩存內容封裝
被緩存的類中需要同時定義cache_key方法和cache_vertion方法,構建一個緩存類來包裹被緩存的對象,設置一個字段為vertion用來存被緩存對象的vertion,一個字段為value存儲實際的對象,將這個緩存對象按照cache_key存入緩存服務中(memcahe redis),在讀取緩存對象時,先將緩存對象按照cache_key從緩存服務中取出,然后對比當前對象的vertion和緩存對象的vertion是否一致,如果一致就會直接讀取緩存對象中的value值,如果不一致就是對象的內容發(fā)生了變化從而緩存服務中的緩存對象就是過期緩存,需要根據新的vertion和內容重新構建緩存對象,然后根據cache_key存入緩存服務中。因為是同一個cache_key所以就等于根據cache_key把上一個老版本的緩存對象替換為新版本的緩存對象供讀取使用,這樣做的優(yōu)點就是是節(jié)省緩存空間,提高緩存駐留時間,預防緩存震蕩
動態(tài)更新cache內容
通過程序來手動控制緩存的更新,如通過數(shù)據庫變化調用回調(after save)來重新生成緩存。這樣做好處是可以實時改變緩存,讓緩存變得實時可控,缺點是大大增加了程序的復雜性。
總結
這些策略的實際使用。往往不是單一的,要根據具體情況,如業(yè)務場景、應用場景、程序邏輯、技術框架等結合使用,通過實際的工程試驗來實現(xiàn)最適合自己的緩存方案。