阿里P8架構(gòu)師談:分布式緩存的應(yīng)用場(chǎng)景、選型比較、問(wèn)題和挑戰(zhàn)
為什么要使用分布式緩存
高并發(fā)環(huán)境下,例如典型的淘寶雙11秒殺,幾分鐘內(nèi)上億的用戶涌入淘寶,這個(gè)時(shí)候如果訪問(wèn)不加攔截,讓大量的讀寫請(qǐng)求涌向數(shù)據(jù)庫(kù),由于磁盤的處理速度與內(nèi)存顯然不在一個(gè)量級(jí),服務(wù)器馬上就要宕機(jī)。從減輕數(shù)據(jù)庫(kù)的壓力和提高系統(tǒng)響應(yīng)速度兩個(gè)角度來(lái)考慮,都會(huì)在數(shù)據(jù)庫(kù)之前加一層緩存,訪問(wèn)壓力越大的,在緩存之前就開(kāi)始CDN攔截圖片等訪問(wèn)請(qǐng)求。
并且由于最早的單臺(tái)機(jī)器的內(nèi)存資源以及承載能力有限,如果大量使用本地緩存,也會(huì)使相同的數(shù)據(jù)被不同的節(jié)點(diǎn)存儲(chǔ)多份,對(duì)內(nèi)存資源造成較大的浪費(fèi),因此,才催生出了分布式緩存。
分布式緩存應(yīng)用場(chǎng)景
- 頁(yè)面緩存.用來(lái)緩存Web 頁(yè)面的內(nèi)容片段,包括HTML、CSS 和圖片等;
- 應(yīng)用對(duì)象緩存.緩存系統(tǒng)作為ORM 框架的二級(jí)緩存對(duì)外提供服務(wù),目的是減輕數(shù)據(jù)庫(kù)的負(fù)載壓力,加速應(yīng)用訪問(wèn);
- 解決分布式Web部署的session同步問(wèn)題,狀態(tài)緩存.緩存包括Session 會(huì)話狀態(tài)及應(yīng)用橫向擴(kuò)展時(shí)的狀態(tài)數(shù)據(jù)等,這類數(shù)據(jù)一般是難以恢復(fù)的,對(duì)可用性要求較高,多應(yīng)用于高可用集群。
- 并行處理.通常涉及大量中間計(jì)算結(jié)果需要共享;
- 云計(jì)算領(lǐng)域提供分布式緩存服務(wù)。
分布式緩存比較:Memcache VS Redis
1、Redis不僅僅支持簡(jiǎn)單的k/v類型的數(shù)據(jù),同時(shí)還提供list,set,zset,hash等數(shù)據(jù)結(jié)構(gòu)的存儲(chǔ)。而memcache只支持簡(jiǎn)單數(shù)據(jù)類型,需要客戶端自己處理復(fù)雜對(duì)象
2、Redis支持?jǐn)?shù)據(jù)的持久化,可以將內(nèi)存中的數(shù)據(jù)保持在磁盤中,重啟的時(shí)候可以再次加載進(jìn)行使用(PS:持久化在rdb、aof)。
3、由于Memcache沒(méi)有持久化機(jī)制,因此宕機(jī)所有緩存數(shù)據(jù)失效。Redis配置為持久化,宕機(jī)重啟后,將自動(dòng)加載宕機(jī)時(shí)刻的數(shù)據(jù)到緩存系統(tǒng)中。具有更好的災(zāi)備機(jī)制。
4、Memcache可以使用Magent在客戶端進(jìn)行一致性hash做分布式。Redis支持在服務(wù)器端做分布式(PS:Twemproxy/Codis/Redis-cluster多種分布式實(shí)現(xiàn)方式)
5、Memcached的簡(jiǎn)單限制就是鍵(key)和Value的限制。***鍵長(zhǎng)為250個(gè)字符??梢越邮艿膬?chǔ)存數(shù)據(jù)不能超過(guò)1MB(可修改配置文件變大),因?yàn)檫@是典型slab 的***值,不適合虛擬機(jī)使用。而Redis的Key長(zhǎng)度支持到512k。
6、Redis使用的是單線程模型,保證了數(shù)據(jù)按順序提交。Memcache需要使用cas保證數(shù)據(jù)一致性。CAS(Check and Set)是一個(gè)確保并發(fā)一致性的機(jī)制,屬于“樂(lè)觀鎖”范疇;原理很簡(jiǎn)單:拿版本號(hào),操作,對(duì)比版本號(hào),如果一致就操作,不一致就放棄任何操作
cpu利用。由于Redis只使用單核,而Memcached可以使用多核,所以平均每一個(gè)核上Redis在存儲(chǔ)小數(shù)據(jù)時(shí)比Memcached性能更 高。而在100k以上的數(shù)據(jù)中,Memcached性能要高于Redis 。
7、memcache內(nèi)存管理:使用Slab Allocation。原理相當(dāng)簡(jiǎn)單,預(yù)先分配一系列大小固定的組,然后根據(jù)數(shù)據(jù)大小選擇最合適的塊存儲(chǔ)。避免了內(nèi)存碎片。(缺點(diǎn):不能變長(zhǎng),浪費(fèi)了一定空間)memcached默認(rèn)情況下下一個(gè)slab的***值為前一個(gè)的1.25倍。
8、redis內(nèi)存管理: Redis通過(guò)定義一個(gè)數(shù)組來(lái)記錄所有的內(nèi)存分配情況, Redis采用的是包裝的malloc/free,相較于Memcached的內(nèi)存 管理方法來(lái)說(shuō),要簡(jiǎn)單很多。由于malloc 首先以鏈表的方式搜索已管理的內(nèi)存中可用的空間分配,導(dǎo)致內(nèi)存碎片比較多。
分布式緩存選型總結(jié)
其實(shí)對(duì)于企業(yè)選型Memcache、Redis而言,更多還是應(yīng)該看業(yè)務(wù)使用場(chǎng)景(因?yàn)镸emcache、Redis兩者都具有足夠高的性能和穩(wěn)定性)。假若業(yè)務(wù)場(chǎng)景需要用到持久化緩存功能、或者支持多種數(shù)據(jù)結(jié)構(gòu)的緩存功能,那么Redis則是***選擇。
(PS:Redis集群解決方式也優(yōu)于Memcache,Memcache在客戶端一致性hash的集群解決方案,Redis采用無(wú)中心的服務(wù)器端集群解決方案)
綜上所述:為了讓緩存系統(tǒng)能夠支持更多的業(yè)務(wù)場(chǎng)景,選擇Redis會(huì)更優(yōu)。
分布式緩存的常見(jiàn)問(wèn)題和挑戰(zhàn)
1.緩存雪崩
緩存雪崩我們可以簡(jiǎn)單的理解為:由于原有緩存失效,新緩存未到期間(例如:我們?cè)O(shè)置緩存時(shí)采用了相同的過(guò)期時(shí)間,在同一時(shí)刻出現(xiàn)大面積的緩存過(guò)期),所有原本應(yīng)該訪問(wèn)緩存的請(qǐng)求都去查詢數(shù)據(jù)庫(kù)了,而對(duì)數(shù)據(jù)庫(kù)CPU和內(nèi)存造成巨大壓力,嚴(yán)重的會(huì)造成數(shù)據(jù)庫(kù)宕機(jī)。從而形成一系列連鎖反應(yīng),造成整個(gè)系統(tǒng)崩潰。
2.緩存穿透
緩存穿透是指用戶查詢數(shù)據(jù),在數(shù)據(jù)庫(kù)沒(méi)有,自然在緩存中也不會(huì)有。這樣就導(dǎo)致用戶查詢的時(shí)候,在緩存中找不到,每次都要去數(shù)據(jù)庫(kù)再查詢一遍,然后返回空(相當(dāng)于進(jìn)行了兩次無(wú)用的查詢)。這樣請(qǐng)求就繞過(guò)緩存直接查數(shù)據(jù)庫(kù),這也是經(jīng)常提的緩存***率問(wèn)題。
3.緩存預(yù)熱
緩存預(yù)熱這個(gè)應(yīng)該是一個(gè)比較常見(jiàn)的概念,相信很多小伙伴都應(yīng)該可以很容易的理解,緩存預(yù)熱就是系統(tǒng)上線后,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)。這樣就可以避免在用戶請(qǐng)求的時(shí)候,先查詢數(shù)據(jù)庫(kù),然后再將數(shù)據(jù)緩存的問(wèn)題!用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)!
4.緩存更新
除了緩存服務(wù)器自帶的緩存失效策略之外,我們還可以根據(jù)具體的業(yè)務(wù)需求進(jìn)行自定義的緩存淘汰,常見(jiàn)的策略有兩種:
(1)定時(shí)去清理過(guò)期的緩存;
(2)當(dāng)有用戶請(qǐng)求過(guò)來(lái)時(shí),再判斷這個(gè)請(qǐng)求所用到的緩存是否過(guò)期,過(guò)期的話就去底層系統(tǒng)得到新數(shù)據(jù)并更新緩存。
兩者各有優(yōu)劣,***種的缺點(diǎn)是維護(hù)大量緩存的key是比較麻煩的,第二種的缺點(diǎn)就是每次用戶請(qǐng)求過(guò)來(lái)都要判斷緩存失效,邏輯相對(duì)比較復(fù)雜!具體用哪種方案,大家可以根據(jù)自己的應(yīng)用場(chǎng)景來(lái)權(quán)衡。
5.緩存降級(jí)
當(dāng)訪問(wèn)量劇增、服務(wù)出現(xiàn)問(wèn)題(如響應(yīng)時(shí)間慢或不響應(yīng))或非核心服務(wù)影響到核心流程的性能時(shí),仍然需要保證服務(wù)還是可用的,即使是有損服務(wù)。系統(tǒng)可以根據(jù)一些關(guān)鍵數(shù)據(jù)進(jìn)行自動(dòng)降級(jí),也可以配置開(kāi)關(guān)實(shí)現(xiàn)人工降級(jí)。
降級(jí)的最終目的是保證核心服務(wù)可用,即使是有損的。而且有些服務(wù)是無(wú)法降級(jí)的(如加入購(gòu)物車、結(jié)算)。
在進(jìn)行降級(jí)之前要對(duì)系統(tǒng)進(jìn)行梳理,看看系統(tǒng)是不是可以丟卒保帥;從而梳理出哪些必須誓死保護(hù),哪些可降級(jí);比如可以參考日志級(jí)別設(shè)置預(yù)案:
(1)一般:比如有些服務(wù)偶爾因?yàn)榫W(wǎng)絡(luò)抖動(dòng)或者服務(wù)正在上線而超時(shí),可以自動(dòng)降級(jí);
(2)警告:有些服務(wù)在一段時(shí)間內(nèi)成功率有波動(dòng)(如在95~100%之間),可以自動(dòng)降級(jí)或人工降級(jí),并發(fā)送告警;
(3)錯(cuò)誤:比如可用率低于90%,或者數(shù)據(jù)庫(kù)連接池被打爆了,或者訪問(wèn)量突然猛增到系統(tǒng)能承受的***閥值,此時(shí)可以根據(jù)情況自動(dòng)降級(jí)或者人工降級(jí);
(4)嚴(yán)重錯(cuò)誤:比如因?yàn)樘厥庠驍?shù)據(jù)錯(cuò)誤了,此時(shí)需要緊急人工降級(jí)。