不用緩存服務(wù),還能怎么緩存數(shù)據(jù)?
除了常見的redis/memcache等進程外緩存服務(wù),還能怎么緩存數(shù)據(jù)?
緩存還有一種常見的玩法,進程內(nèi)緩存。
什么是進程內(nèi)緩存?
將一些數(shù)據(jù)緩存在站點,或者服務(wù)的進程內(nèi),這就是進程內(nèi)緩存。
進程內(nèi)緩存的實現(xiàn)載體,最簡單的,可以是一個帶鎖的Map。又或者,可以使用第三方庫,例如leveldb。
進程內(nèi)緩存能存儲啥?
redis/memcache等進程外緩存服務(wù)能存什么,進程內(nèi)緩存就能存什么。
如上圖,可以存儲json數(shù)據(jù),可以存儲html頁面,可以存儲對象。
進程內(nèi)緩存有什么好處?
與沒有緩存相比,進程內(nèi)緩存的好處是,數(shù)據(jù)讀取不再需要訪問后端,例如數(shù)據(jù)庫。
如上圖,整個訪問流程要經(jīng)過1,2,3,4四個步驟。
如果引入進程內(nèi)緩存,
如上圖,整個訪問流程只要經(jīng)過1,2兩個步驟。
與進程外緩存相比(例如redis/memcache),進程內(nèi)緩存省去了網(wǎng)絡(luò)開銷,所以一來節(jié)省了內(nèi)網(wǎng)帶寬,二來響應(yīng)時延會更低。
進程內(nèi)緩存有什么缺點?
統(tǒng)一緩存服務(wù)雖然多一次網(wǎng)絡(luò)交互,但仍是統(tǒng)一存儲。
如上圖,站點和服務(wù)中的多個節(jié)點訪問統(tǒng)一的緩存服務(wù),數(shù)據(jù)統(tǒng)一存儲,容易保證數(shù)據(jù)的一致性。
而進程內(nèi)緩存,如上圖,如果數(shù)據(jù)緩存在站點和服務(wù)的多個節(jié)點內(nèi),數(shù)據(jù)存了多份,一致性比較難保障。
如何保證進程內(nèi)緩存的數(shù)據(jù)一致性?
保障進程內(nèi)緩存一致性,有幾種方案。
第一種方案,可以通過單節(jié)點通知其他節(jié)點。如上圖:寫請求發(fā)生在server1,在修改完自己內(nèi)存數(shù)據(jù)與數(shù)據(jù)庫中的數(shù)據(jù)之后,可以主動通知其他server節(jié)點,也修改內(nèi)存的數(shù)據(jù)。
這種方案的缺點是:同一功能的一個集群的多個節(jié)點,相互耦合在一起,特別是節(jié)點較多時,網(wǎng)狀連接關(guān)系極其復雜。
第二種方案,可以通過MQ通知其他節(jié)點。如上圖,寫請求發(fā)生在server1,在修改完自己內(nèi)存數(shù)據(jù)與數(shù)據(jù)庫中的數(shù)據(jù)之后,給MQ發(fā)布數(shù)據(jù)變化通知,其他server節(jié)點訂閱MQ消息,也修改內(nèi)存數(shù)據(jù)。
這種方案雖然解除了節(jié)點之間的耦合,但引入了MQ,使得系統(tǒng)更加復雜。
前兩種方案,節(jié)點數(shù)量越多,數(shù)據(jù)冗余份數(shù)越多,數(shù)據(jù)同時更新的原子性越難保證,一致性也就越難保證。
第三種方案,為了避免耦合,降低復雜性,干脆放棄了“實時一致性”,每個節(jié)點啟動一個timer,定時從后端拉取最新的數(shù)據(jù),更新內(nèi)存緩存。在有節(jié)點更新后端數(shù)據(jù),而其他節(jié)點通過timer更新數(shù)據(jù)之間,會讀到臟數(shù)據(jù)。
為什么不能頻繁使用進程內(nèi)緩存?
分層架構(gòu)設(shè)計,有一條準則:站點層、服務(wù)層要做到無數(shù)據(jù)無狀態(tài),這樣才能任意的加節(jié)點水平擴展,數(shù)據(jù)和狀態(tài)盡量存儲到后端的數(shù)據(jù)存儲服務(wù),例如數(shù)據(jù)庫服務(wù)或者緩存服務(wù)。
可以看到,站點與服務(wù)的進程內(nèi)緩存,實際上違背了分層架構(gòu)設(shè)計的無狀態(tài)準則,故一般不推薦使用。
什么時候可以使用進程內(nèi)緩存?
以下情況,可以考慮使用進程內(nèi)緩存。
情況一,只讀數(shù)據(jù),可以考慮在進程啟動時加載到內(nèi)存。
畫外音:此時也可以把數(shù)據(jù)加載到redis / memcache,進程外緩存服務(wù)也能解決這類問題。
情況二,極其高并發(fā)的,如果透傳后端壓力極大的場景,可以考慮使用進程內(nèi)緩存。例如,秒殺業(yè)務(wù),并發(fā)量極高,需要站點層擋住流量,可以使用內(nèi)存緩存。
情況三,一定程度上允許數(shù)據(jù)不一致業(yè)務(wù)。例如,有一些計數(shù)場景,運營場景,頁面對數(shù)據(jù)一致性要求較低,可以考慮使用進程內(nèi)頁面緩存。
末了,再次強調(diào),進程內(nèi)緩存的適用場景并不如redis/memcache廣泛,不要為了炫技而使用。
更多的時候,還是老老實實使用redis/mc吧。
畫外音:額,介紹技術(shù),不希望把大家?guī)恕?/p>
【本文為51CTO專欄作者“58沈劍”原創(chuàng)稿件,轉(zhuǎn)載請聯(lián)系原作者】