后端緩存的23個(gè)關(guān)鍵關(guān)注點(diǎn)
▌1:極簡(jiǎn)緩存架構(gòu)
通過(guò)JSR107規(guī)范,我們將框架定義為客戶(hù)端層、緩存提供層、緩存管理層、緩存存儲(chǔ)層。其中緩存存儲(chǔ)層又分為基本存儲(chǔ)層、LRU存儲(chǔ)層和Weak存儲(chǔ)層,如下圖所示。
緩存分層圖
其中:
客戶(hù)端層:使用者直接通過(guò)該層與數(shù)據(jù)進(jìn)行交互。
緩存提供層:主要對(duì)緩存管理層的生命周期進(jìn)行維護(hù),負(fù)責(zé)緩存管理層的創(chuàng)建,保存、獲取以及銷(xiāo)毀。
緩存管理層:主要對(duì)緩存客戶(hù)端的生命周期進(jìn)行維護(hù),負(fù)責(zé)緩存客戶(hù)端的創(chuàng)建,保存、獲取以及銷(xiāo)毀
緩存存儲(chǔ)層:負(fù)責(zé)數(shù)據(jù)以什么樣的形式進(jìn)行存儲(chǔ)。
基本存儲(chǔ)層:是以普通的ConcurrentHashMap為存儲(chǔ)核心,數(shù)據(jù)不淘汰。
LRU存儲(chǔ)層:是以最近最少用為原則進(jìn)行的數(shù)據(jù)存儲(chǔ)和緩存淘汰機(jī)制。
Weak存儲(chǔ)層:是以弱引用為原則的數(shù)據(jù)存儲(chǔ)和緩存淘汰機(jī)制。
▌2:容量評(píng)估
緩存系統(tǒng)主要消耗的是服務(wù)器的內(nèi)存,因此,在使用緩存時(shí)必須先對(duì)應(yīng)用需要緩存的數(shù)據(jù)大小進(jìn)行評(píng)估,包括緩存的數(shù)據(jù)結(jié)構(gòu)、緩存大小、緩存數(shù)量、緩存的失效時(shí)間,然后根據(jù)業(yè)務(wù)情況自行推算在未來(lái)一定時(shí)間內(nèi)的容量的使用情況,根據(jù)容量評(píng)估的結(jié)果來(lái)申請(qǐng)和分配緩存資源,否則會(huì)造成資源浪費(fèi)或者緩存空間不夠。
▌3:業(yè)務(wù)分離
建議將使用緩存的業(yè)務(wù)進(jìn)行分離,核心業(yè)務(wù)和非核心業(yè)務(wù)使用不同的緩存實(shí)例,從物理上進(jìn)行隔離,如果有條件,則請(qǐng)對(duì)每個(gè)業(yè)務(wù)使用單獨(dú)的實(shí)例或者集群,以減小應(yīng)用之間互相影響的可能性。筆者就經(jīng)常聽(tīng)說(shuō)有的公司應(yīng)用了共享緩存,造成緩存數(shù)據(jù)被覆蓋以及緩存數(shù)據(jù)錯(cuò)亂的線(xiàn)上事故。
▌4:監(jiān)控為王
所有的緩存實(shí)例都需要添加監(jiān)控,這是非常重要的,我們需要對(duì)慢查詢(xún)、大對(duì)象、內(nèi)存使用情況做可靠的監(jiān)控。
▌5:失效時(shí)間
任何緩存的key都必須設(shè)定緩存失效時(shí)間,且失效時(shí)間不能集中在某一點(diǎn),否則會(huì)導(dǎo)致緩存占滿(mǎn)內(nèi)存或者緩存雪崩。
▌6:大量key同時(shí)失效時(shí)間的危害
在使用緩存時(shí)需要進(jìn)行緩存設(shè)計(jì),要充分考慮如何避免常見(jiàn)的緩存穿透、緩存雪崩、緩存并發(fā)等問(wèn)題,尤其是對(duì)于高并發(fā)的緩存使用,需要對(duì)key的過(guò)期時(shí)間進(jìn)行隨機(jī)設(shè)置,例如,將過(guò)期時(shí)間設(shè)置為10秒+random(2),也就是將過(guò)期時(shí)間隨機(jī)設(shè)置成10~12秒。
筆者曾經(jīng)見(jiàn)過(guò)一個(gè)case:在應(yīng)用程序中對(duì)使用的大量緩存key設(shè)置了同一個(gè)固定的失效時(shí)間,當(dāng)緩存失效時(shí),會(huì)造成在一段時(shí)間內(nèi)同時(shí)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),造成數(shù)據(jù)庫(kù)的壓力較大。
▌7:先更新數(shù)據(jù)庫(kù)后更新緩存有啥問(wèn)題?
想象一下,如果兩個(gè)線(xiàn)程同時(shí)執(zhí)行更新操作,線(xiàn)程1更新數(shù)據(jù)庫(kù)后,線(xiàn)程2也更新了數(shù)據(jù)庫(kù),然后開(kāi)始寫(xiě)緩存,但線(xiàn)程2先執(zhí)行了更新緩存的操作,而線(xiàn)程1在執(zhí)行更新緩存的時(shí)候就把線(xiàn)程2更新的數(shù)據(jù)給覆蓋掉了,這樣就會(huì)出現(xiàn)數(shù)據(jù)不一致。
▌8:先刪緩存, 行不行?
“先刪緩存,然后執(zhí)行數(shù)據(jù)庫(kù)事務(wù)”也有人討論這種方案,不過(guò)這種操作對(duì)于如商品這種查詢(xún)非常頻繁的業(yè)務(wù)不適用,因?yàn)樵谀銊h緩存的同時(shí),已經(jīng)有另一個(gè)系統(tǒng)來(lái)讀緩存了,此時(shí)事務(wù)還沒(méi)有提交。當(dāng)然對(duì)于如用戶(hù)維度的業(yè)務(wù)是可以考慮的。
▌9:數(shù)據(jù)庫(kù)和緩存數(shù)據(jù)一致性
京東采用了通過(guò)canal更新緩存原子性的方法,如下圖所示。
最終一致性方案
幾個(gè)關(guān)注點(diǎn):
更新數(shù)據(jù)時(shí)使用更新時(shí)間戳或者版本對(duì)比。
使用如canal訂閱數(shù)據(jù)庫(kù)binlog;此處把mysql看成發(fā)布者,binlog是發(fā)布的內(nèi)容,canal(canal 是阿里巴巴mysql數(shù)據(jù)庫(kù)binlog的增量訂閱&消費(fèi)組件)看成消費(fèi)者,canal訂閱binlog然后更新到Redis。
將更新請(qǐng)求按照相應(yīng)的規(guī)則分散到多個(gè)隊(duì)列,然后每個(gè)隊(duì)列的進(jìn)行單線(xiàn)程更新,更新時(shí)拉取***的數(shù)據(jù)保存;更新之前獲取相關(guān)的鎖再進(jìn)行更新。
▌10.先更新數(shù)據(jù)庫(kù),再刪除緩存的一種實(shí)踐
流程如下圖所示:
過(guò)程不贅述,只強(qiáng)調(diào)一個(gè),數(shù)據(jù)庫(kù)update變更會(huì)同步發(fā)到消息,通過(guò)消息去刪除緩存。如果刪除失敗,消息有重試機(jī)制保障。另外除了極端情況,緩存更新是比較及時(shí)的。
▌11:本地緩存的挑戰(zhàn)
如果對(duì)性能的要求不是非常高,則盡量使用分布式緩存,而不要使用本地緩存,因?yàn)楸镜鼐彺嬖诜?wù)的各個(gè)節(jié)點(diǎn)之間復(fù)制,在某一時(shí)刻副本之間是不一致的,如果這個(gè)緩存代表的是開(kāi)關(guān),而且分布式系統(tǒng)中的請(qǐng)求有可能會(huì)重復(fù),就會(huì)導(dǎo)致重復(fù)的請(qǐng)求走到兩個(gè)節(jié)點(diǎn),一個(gè)節(jié)點(diǎn)的開(kāi)關(guān)是開(kāi),一個(gè)節(jié)點(diǎn)的開(kāi)關(guān)是關(guān),如果請(qǐng)求處理沒(méi)有做到冪等,就會(huì)造成處理重復(fù),在嚴(yán)重情況下會(huì)造成資金損失。
▌12:緩存熱點(diǎn)與多級(jí)緩存
對(duì)于分布式緩存,我們需要在Nginx+Lua應(yīng)用中進(jìn)行應(yīng)用緩存來(lái)減少Redis集群的訪(fǎng)問(wèn)沖擊;即首先查詢(xún)應(yīng)用本地緩存,如果***則直接緩存,如果沒(méi)有***則接著查詢(xún)Redis集群、回源到Tomcat;然后將數(shù)據(jù)緩存到應(yīng)用本地。如同14-8所示。
此處到應(yīng)用Nginx的負(fù)載機(jī)制采用:正常情況采用一致性哈希,如果某個(gè)請(qǐng)求類(lèi)型訪(fǎng)問(wèn)量突破了一定的閥值,則自動(dòng)降級(jí)為輪詢(xún)機(jī)制。另外對(duì)于一些秒殺活動(dòng)之類(lèi)的熱點(diǎn)我們是可以提前知道的,可以把相關(guān)數(shù)據(jù)預(yù)先推送到應(yīng)用Nginx并將負(fù)載均衡機(jī)制降級(jí)為輪詢(xún)。
分布式緩存方案
另外可以考慮建立實(shí)時(shí)熱點(diǎn)發(fā)現(xiàn)系統(tǒng)來(lái)發(fā)現(xiàn)熱點(diǎn),如下圖所示:
實(shí)時(shí)熱點(diǎn)發(fā)現(xiàn)方案
1)接入Nginx將請(qǐng)求轉(zhuǎn)發(fā)給應(yīng)用Nginx;
2)應(yīng)用Nginx首先讀取本地緩存;如果***直接返回,不***會(huì)讀取分布式緩存、回源到Tomcat進(jìn)行處理;
3)應(yīng)用Nginx會(huì)將請(qǐng)求上報(bào)給實(shí)時(shí)熱點(diǎn)發(fā)現(xiàn)系統(tǒng),如使用UDP直接上報(bào)請(qǐng)求、或者將請(qǐng)求寫(xiě)到本地kafka、或者使用flume訂閱本地nginx日志;上報(bào)給實(shí)時(shí)熱點(diǎn)發(fā)現(xiàn)系統(tǒng)后,它將進(jìn)行統(tǒng)計(jì)熱點(diǎn)(可以考慮storm實(shí)時(shí)計(jì)算);
4)根據(jù)設(shè)置的閥值將熱點(diǎn)數(shù)據(jù)推送到應(yīng)用Nginx本地緩存。
因?yàn)樽隽吮镜鼐彺?,因此?duì)于數(shù)據(jù)一致性需要我們?nèi)タ紤],即何時(shí)失效或更新緩存:
1)如果可以訂閱數(shù)據(jù)變更消息,那么可以訂閱變更消息進(jìn)行緩存更新;
2)如果無(wú)法訂閱消息或者訂閱消息成本比較高,并且對(duì)短暫的數(shù)據(jù)一致性要求不嚴(yán)格(比如在商品詳情頁(yè)看到的庫(kù)存,可以短暫的不一致,只要保證下單時(shí)一致即可),那么可以設(shè)置合理的過(guò)期時(shí)間,過(guò)期后再查詢(xún)新的數(shù)據(jù);
3)如果是秒殺之類(lèi)的,可以訂閱活動(dòng)開(kāi)啟消息,將相關(guān)數(shù)據(jù)提前推送到前端應(yīng)用,并將負(fù)載均衡機(jī)制降級(jí)為輪詢(xún);
4)建立實(shí)時(shí)熱點(diǎn)發(fā)現(xiàn)系統(tǒng)來(lái)對(duì)熱點(diǎn)進(jìn)行統(tǒng)一推送和更新。
應(yīng)對(duì)緩存大熱點(diǎn):數(shù)據(jù)復(fù)制模式
在Facebook有一招,就是通過(guò)多個(gè)key_index(key:xxx#N) 來(lái)解決數(shù)據(jù)的熱點(diǎn)讀問(wèn)題。解決方案是所有熱點(diǎn)key發(fā)布到所有web服務(wù)器;每個(gè)服務(wù)器的key有對(duì)應(yīng)別名,可以通過(guò)client端的算法路由到某臺(tái)服務(wù)器;做刪除動(dòng)作時(shí),刪除所有的別名key??珊?jiǎn)單總結(jié)為一個(gè)通用的group內(nèi)一致模型。把緩存集群劃分為若干分組(group),在同組內(nèi),所有的緩存服務(wù)器,都發(fā)布熱點(diǎn)key的數(shù)據(jù)。
對(duì)于大量讀操作而言,通過(guò)client端路由策略,隨意返回一臺(tái)機(jī)器即可;而寫(xiě)操作,有一種解法是通過(guò)定時(shí)任務(wù)來(lái)寫(xiě)入;Facebook采取的是刪除所有別名key的策略。如何保障這一個(gè)批量操作都成功?
(1)容忍部分失敗導(dǎo)致的數(shù)據(jù)版本問(wèn)題
(2)只要有寫(xiě)操作,則通過(guò)定時(shí)任務(wù)刷新緩存;如果涉及3臺(tái)服務(wù)器,則都操作成功代表該任務(wù)表的這條記錄成功完成使命,否則會(huì)重試。
▌13:緩存失效的連接風(fēng)暴
引起這個(gè)問(wèn)題的主要原因還是高并發(fā)的時(shí)候,平時(shí)我們?cè)O(shè)定一個(gè)緩存的過(guò)期時(shí)間時(shí),可能有一些會(huì)設(shè)置1分鐘,5分鐘,并發(fā)很高可能會(huì)出在某一個(gè)時(shí)間同時(shí)生成了很多的緩存,并且過(guò)期時(shí)間都一樣,這個(gè)時(shí)候就可能引發(fā)過(guò)期時(shí)間到后,這些緩存同時(shí)失效,請(qǐng)求全部轉(zhuǎn)發(fā)到DB,DB可能會(huì)壓力過(guò)重。那如何解決這些問(wèn)題呢?
其中的一個(gè)簡(jiǎn)單方案就是將緩存失效時(shí)間分散開(kāi),比如我們可以在原有的失效時(shí)間基礎(chǔ)上增加一個(gè)隨機(jī)值,比如1-5分鐘隨機(jī),這樣每一個(gè)緩存的過(guò)期時(shí)間的重復(fù)率就會(huì)降低,就很難引發(fā)集體失效的事件。
如果緩存集中在一段時(shí)間內(nèi)失效,DB的壓力凸顯。這個(gè)沒(méi)有***解決辦法,但可以分析用戶(hù)行為,盡量讓失效時(shí)間點(diǎn)均勻分布。
上述是緩存使用過(guò)程中經(jīng)常遇到的并發(fā)穿透、并發(fā)失效問(wèn)題。一般情況下,我們解決這些問(wèn)題的方法是,引入空值、鎖和隨機(jī)緩存過(guò)期時(shí)間的機(jī)制。
▌14:緩存預(yù)熱
提前把數(shù)據(jù)讀入到緩存的做法就是數(shù)據(jù)預(yù)熱處理。數(shù)據(jù)預(yù)熱處理要注意一些細(xì)節(jié)問(wèn)題:
(1)是否有監(jiān)控機(jī)制確保預(yù)熱數(shù)據(jù)都寫(xiě)成功了!筆者曾經(jīng)遇到部分?jǐn)?shù)據(jù)成功而影響高峰期業(yè)務(wù)的案例;
(2)數(shù)據(jù)預(yù)熱配備回滾方案,遇到緊急回滾時(shí)便于操作。對(duì)于新建cache server集群,也可以通過(guò)數(shù)據(jù)預(yù)熱模式來(lái)做一番手腳。如下圖所示,先從冷集群中獲取key,如果獲取不到,則從熱集群中獲取。同時(shí)把獲取到的key put到冷集群。如下圖
數(shù)據(jù)預(yù)熱
(3)預(yù)熱數(shù)據(jù)量的考量,要做好容量評(píng)估。在容量允許的范圍內(nèi)預(yù)熱全量,否則預(yù)熱訪(fǎng)問(wèn)量高的。
(4)預(yù)熱過(guò)程中需要注意是否會(huì)因?yàn)榕繑?shù)據(jù)庫(kù)操作或慢sql等引發(fā)數(shù)據(jù)庫(kù)性能問(wèn)題。
▌15:超時(shí)時(shí)間設(shè)計(jì)
在使用遠(yuǎn)程緩存(如Redis、Memcached)時(shí),一定要對(duì)操作超時(shí)時(shí)間進(jìn)行設(shè)置,這是非常關(guān)鍵的,一般我們?cè)O(shè)計(jì)緩存作為加速數(shù)據(jù)庫(kù)讀取的手段,也會(huì)對(duì)緩存操作做降級(jí)處理,因此推薦使用更短的緩存超時(shí)時(shí)間,如果一定要給出一個(gè)數(shù)字,則希望是100毫秒以?xún)?nèi)。
筆者曾經(jīng)遇到過(guò)一個(gè)案例:某個(gè)正常運(yùn)行的應(yīng)用突然報(bào)警線(xiàn)程數(shù)過(guò)高,之后很快就出現(xiàn)了內(nèi)存溢出。
分析原因?yàn)椋河捎诰彺孢B接數(shù)達(dá)到***限制,應(yīng)用無(wú)法連接緩存,并且超時(shí)時(shí)間設(shè)置得較大,導(dǎo)致訪(fǎng)問(wèn)緩存的服務(wù)都在等待緩存操作返回,由于緩存負(fù)載較高,處理不完所有的請(qǐng)求,但是這些服務(wù)都在等待緩存操作返回,服務(wù)這時(shí)在等待,并沒(méi)有超時(shí),就不能降級(jí)并繼續(xù)訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)。這在BIO模式下線(xiàn)程池就會(huì)撐滿(mǎn),使用方的線(xiàn)程池也都撐滿(mǎn);在NIO模式下一樣會(huì)使服務(wù)的負(fù)載增加,服務(wù)響應(yīng)變慢,甚至使服務(wù)被壓垮。
▌16:不要把緩存到存儲(chǔ)
大家都知道一個(gè)顛撲不破的真理:在分布式架構(gòu)下,一切系統(tǒng)都可能fail,無(wú)論是緩存、存儲(chǔ)包括數(shù)據(jù)庫(kù)還是應(yīng)用服務(wù)器,而且部分緩存本身就未提供持久化機(jī)制比如memcached。即使使用持久化機(jī)制的cache,也要慎用,如果作為唯一存儲(chǔ)的話(huà)。
▌17:緩存崩潰解決之道
當(dāng)我們使用分布式緩存時(shí),應(yīng)該考慮如何應(yīng)對(duì)其中一部分緩存實(shí)例宕機(jī)的情況。接下來(lái)部分將介紹分布式緩存時(shí)的常用算法。而當(dāng)緩存數(shù)據(jù)是可丟失的情況時(shí),我們可以選擇一致性哈希算法。
取模
對(duì)于取模機(jī)制如果其中一個(gè)實(shí)例壞了,如果摘除此實(shí)例將導(dǎo)致大量緩存不***,瞬間大流量可能導(dǎo)致后端DB/服務(wù)出現(xiàn)問(wèn)題。對(duì)于這種情況可以采用主從機(jī)制來(lái)避免實(shí)例壞了的問(wèn)題,即其中一個(gè)實(shí)例壞了可以那從/主頂上來(lái)。但是取模機(jī)制下如果增加一個(gè)節(jié)點(diǎn)將導(dǎo)致大量緩存不***,一般是建立另一個(gè)集群,然后把數(shù)據(jù)遷移到新集群,然后把流量遷移過(guò)去。
一致性哈希
對(duì)于一致性哈希機(jī)制如果其中一個(gè)實(shí)例壞了,如果摘除此實(shí)例將只影響一致性哈希環(huán)上的部分緩存不***,不會(huì)導(dǎo)致瞬間大量回源到后端DB/服務(wù),但是也會(huì)產(chǎn)生一些影響。
▌18. 緩存崩潰后的快速恢復(fù)
如果出現(xiàn)之前說(shuō)到的一些問(wèn)題,可以考慮如下方案:
1)主從機(jī)制,做好冗余,即其中一部分不可用,將對(duì)等的部分補(bǔ)上去;
2)如果因?yàn)榫彺鎸?dǎo)致應(yīng)用可用性已經(jīng)下降可以考慮:
部分用戶(hù)降級(jí),然后慢慢減少降級(jí)量;
后臺(tái)通過(guò)Worker預(yù)熱緩存數(shù)據(jù)。
也就是如果整個(gè)緩存集群壞了,而且沒(méi)有備份,那么只能去慢慢將緩存重建;為了讓部分用戶(hù)還是可用的,可以根據(jù)系統(tǒng)承受能力,通過(guò)降級(jí)方案讓一部分用戶(hù)先用起來(lái),將這些用戶(hù)相關(guān)的緩存重建;另外通過(guò)后臺(tái)Worker進(jìn)行緩存數(shù)據(jù)的預(yù)熱。
▌19. 開(kāi)啟Nginx Proxy Cache性能不升反降
開(kāi)啟Nginx Proxy Cache后,性能下降,而且過(guò)一段內(nèi)存使用率到達(dá)98%;解決方案:
1)對(duì)于內(nèi)存占用率高的問(wèn)題是內(nèi)核問(wèn)題,內(nèi)核使用LRU機(jī)制,本身不是問(wèn)題,不過(guò)可以通過(guò)修改內(nèi)核參數(shù):
sysctl -wvm.extra_free_kbytes=6436787
sysctl -wvm.vfs_cache_pressure=10000
2)使用Proxy Cache在機(jī)械盤(pán)上性能差可以通過(guò)tmpfs緩存或nginx共享字典緩存元數(shù)據(jù),或者使用SSD,我們目前使用內(nèi)存文件系統(tǒng)。
▌20:“網(wǎng)絡(luò)抖動(dòng)時(shí),返回502錯(cuò)誤”緣于timeout
Twemproxy配置的timeout時(shí)間太長(zhǎng),之前設(shè)置為5s,而且沒(méi)有分別針對(duì)連接、讀、寫(xiě)設(shè)置超時(shí)。后來(lái)我們減少超時(shí)時(shí)間,內(nèi)網(wǎng)設(shè)置在150ms以?xún)?nèi),當(dāng)超時(shí)時(shí)訪(fǎng)問(wèn)動(dòng)態(tài)服務(wù)。
▌21:應(yīng)對(duì)惡意刷的經(jīng)驗(yàn)
商品詳情頁(yè)庫(kù)存接口2014年被惡意刷,每分鐘超過(guò)600w訪(fǎng)問(wèn)量,tomcat機(jī)器只能定時(shí)重啟;因?yàn)槭窃斍轫?yè)展示的數(shù)據(jù),緩存幾秒鐘是可以接受的,因此開(kāi)啟nginxproxy cache來(lái)解決該問(wèn)題,開(kāi)啟后降到正常水平;后來(lái)我們使用Nginx+Lua架構(gòu)改造服務(wù),數(shù)據(jù)過(guò)濾、URL重寫(xiě)等在Nginx層完成,通過(guò)URL重寫(xiě)+一致性哈希負(fù)載均衡,不怕隨機(jī)URL,一些服務(wù)提升了10%+的緩存***率。
▌22:網(wǎng)卡打滿(mǎn)了咋辦?
用Redis都有個(gè)很頭疼的問(wèn)題,就是Redis的網(wǎng)卡打滿(mǎn)問(wèn)題,由于Redis的性能很高,在大并發(fā)請(qǐng)求下,很容易將網(wǎng)卡打滿(mǎn).通常情況下,1臺(tái)服務(wù)器上都會(huì)跑幾十個(gè)Redis實(shí)例 ,一旦網(wǎng)卡打滿(mǎn),很容易干擾到應(yīng)用層可用性.所以我們基于開(kāi)源的Contiv netplugin項(xiàng)目,限制了網(wǎng)卡的使用, 主要功能是提供基于Policy的網(wǎng)絡(luò)和存儲(chǔ)管理。Contiv比較“誘人”的一點(diǎn)就是,它的網(wǎng)絡(luò)管理能力,既有L2(VLAN)、L3(BGP),又有 Overlay(VxLAN),有了它就可以無(wú)視底層的網(wǎng)絡(luò)基礎(chǔ)架構(gòu),向上層容器提供一致的虛擬網(wǎng)絡(luò)了。最主要的一點(diǎn)是,既滿(mǎn)足了業(yè)務(wù)場(chǎng)景,又兼容了以往的網(wǎng)絡(luò)架構(gòu)。在轉(zhuǎn)發(fā)性能上,它能接近物理網(wǎng)卡的性能,特別在沒(méi)有萬(wàn)兆網(wǎng)絡(luò)的老機(jī)房也能很好的使用。在網(wǎng)絡(luò)流量監(jiān)控方面,我們通過(guò)使用ovs的sflow來(lái)抓取宿主機(jī)上所有的網(wǎng)絡(luò)流量,然后自開(kāi)發(fā)了一個(gè)簡(jiǎn)單的sflow Collecter, 服務(wù)器收到sflow的數(shù)據(jù)包進(jìn)行解析,篩選出關(guān)鍵數(shù)據(jù),然后進(jìn)行匯總分析,得到所需要的監(jiān)控?cái)?shù)據(jù)。通過(guò)這個(gè)定制的網(wǎng)絡(luò)插件,我們可以隨意的控制某個(gè)Redis的流量,流量過(guò)大,也不會(huì)影響其他的項(xiàng)目,而如果某個(gè)服務(wù)器上的Redis流量很低,我們也可以縮小它的配額,提供給本機(jī)其他需要大流量的程序使用,這些,通過(guò)后臺(tái)的監(jiān)控程序,可以實(shí)現(xiàn)完全自動(dòng)化。
▌23:緩存組件的選擇
緩存的種類(lèi)很多,我們實(shí)際使用時(shí),需要根據(jù)緩存位置(系統(tǒng)前后端)、待存數(shù)據(jù)類(lèi)型、訪(fǎng)問(wèn)方式、內(nèi)存效率等情況來(lái)選擇最適合的緩存組件。本小節(jié)接下來(lái)將主要探討在應(yīng)用層后端如何選擇分布式緩存組件。
一般業(yè)務(wù)系統(tǒng)中,大部分?jǐn)?shù)據(jù)都是簡(jiǎn)單KV數(shù)據(jù)類(lèi)型,如前述微博Feed系統(tǒng)中的feed content、feed列表、用戶(hù)信息等。這些簡(jiǎn)單類(lèi)型數(shù)據(jù)只需要進(jìn)行set、get、delete操作,不需要在緩存端做計(jì)算操作,最適合以memcached作為緩存組件。
其次對(duì)于需要部分獲取、事物型變更、緩存端計(jì)算的集合類(lèi)數(shù)據(jù),擁有豐富數(shù)據(jù)結(jié)構(gòu)和訪(fǎng)問(wèn)接口的Redis 也許會(huì)更適合。Redis還支持以主從(master-slave)方式進(jìn)行數(shù)據(jù)備份,支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤(pán),重啟時(shí)再次加載使用。因磁盤(pán)緩存(diskstore)方式的性能問(wèn)題,Redis數(shù)據(jù)基本只適合保存在內(nèi)存中,由此帶來(lái)的問(wèn)題是:在某些業(yè)務(wù)場(chǎng)景,如果待緩存的數(shù)據(jù)量特別大,而數(shù)據(jù)的訪(fǎng)問(wèn)量不太大或者有冷熱區(qū)分,也必須將所有數(shù)據(jù)全部放在內(nèi)存中,緩存成本(特別是機(jī)器成本)會(huì)特別高。如果業(yè)務(wù)遇到這種場(chǎng)景,可以考慮用pika、ssdb等其他緩存組件。pika、ssdb都兼容Redis協(xié)議,同時(shí)采用多線(xiàn)程方案,支持持久化和復(fù)制,單個(gè)緩存實(shí)例可以緩存數(shù)百G的數(shù)據(jù),其中少部分的熱數(shù)據(jù)存放內(nèi)存,大部分溫?zé)釘?shù)據(jù)或冷數(shù)據(jù)都可以放在磁盤(pán),從而很好的降低緩存成本。
對(duì)前面講到的這些后端常用的緩存組件,可以參考下表進(jìn)行選擇。
***,對(duì)于對(duì)存儲(chǔ)效率、訪(fǎng)問(wèn)性能等有更高要求的業(yè)務(wù)場(chǎng)景,結(jié)合業(yè)務(wù)特性進(jìn)行緩存組件的定制化設(shè)計(jì)與開(kāi)發(fā),也是一個(gè)很好的選擇。
總之,緩存組件的選型要考慮數(shù)據(jù)模型、訪(fǎng)問(wèn)方式、緩存成本甚至開(kāi)發(fā)人員的知識(shí)結(jié)構(gòu),從而進(jìn)行因地制宜的取舍,不要盲目引入不熟悉、不活躍、不成熟的緩存組件,否則中途頻繁調(diào)整緩存方案,會(huì)給開(kāi)發(fā)進(jìn)度、運(yùn)維成本帶來(lái)較大的挑戰(zhàn)。